C语言程序编译全流程,从源代码到二进制

源程序

对于一个最简单的程序:

int main(){
	int a = 1;
	int b = 2;
	int c = a + b;
	return 0;
}

预处理

处理源代码中的宏指令,例如#include等

clang -E test.c

处理结果:

# 1 "test.c"
# 1 "<built-in>" 1
# 1 "<built-in>" 3
# 343 "<built-in>" 3
# 1 "<command line>" 1
# 1 "<built-in>" 2
# 1 "test.c" 2
int main(){
    int a=1;
    int b=2;
    int c=a+b;
    return 0;
}

预处理输出中多出来的这些行,被称为linemarkers,
格式为:# linenum filename flags
具体意思可以看gnu的文档以及Cppreference的文档

RTFM:reading the f**k manual

编译过程

词法分析

clang -fsyntax-only -Xclang -dump-tokens test.c
# Clang编译器的一个选项,它告诉编译器只进行语法分析而不生成目标代码。这意味着编译器将检查源文件中的语法错误,但不会生成可执行文件或目标文件。
#-Xclang: 这是Clang编译器的一个选项,它允许传递额外的选项给Clang编译器的底层部分。
#-dump-tokens 告诉编译器在对源代码进行词法分析后输出标记(tokens)的信息。

生成结果:
在这里插入图片描述
词法分析把原文件划分为一个个的token,记录下每个token的类型,内容,位置(所在文件,行号列号)
类型有:

  • 关键字(Keywords):例如int、for、if等。
  • 标识符(Identifiers):由用户定义的名称,用于表示变量、函数等。
  • 常量(Constants):包括整数常量、浮点数常量、字符常量、字符串常量等。
  • 字符(Characters):例如单引号括起来的字符常量。
  • 字符串(Strings):例如双引号括起来的字符串常量。
  • 运算符(Operators):例如+、-、*等。
  • 分隔符(Delimiters):例如逗号,、分号;、括号(、)等。
  • 注释(Comments):包括单行注释//和多行注释/* */。
  • 空白符(Whitespace):例如空格、制表符、换行符等。
    这一步一般不会报错

语法分析

将函数

clang -fsyntax-only -Xclang -ast-dump test.c
# -ast-dump: 这个选项告诉Clang编译器在语法分析之后输出抽象语法树(Abstract Syntax Tree,AST)。抽象语法树是编译器在语法分析阶段生成的一种树形结构,它反映了源代码的语法结构,是编译器进行进一步分析和优化的基础

输出【clang的语法树输出把语义信息也输出了】
在这里插入图片描述
这一步将识别出来的结果组合成树型结构,称为语法树,包括每个节点的类型,节点间的关系等。
上面的TranslationUnitDecl就是指一个翻译单元,一般一个源文件就是一个翻译单元,上面的一些invalid sloc的是一些C语言内置的东西,不用看。
从FuncitonDecl开始,是我们写的代码的语法树
VarDecl指变量声明。
BinaryOperator是二进制操作符,也就是+号,
LValueToRValue是指C语言中的左值和右值机制,a和b是左值,先转化为右值,然后再相加把结果赋给c。
在这一步会报告一些语法错误,例如缺了分号。
关于Clang的AST的详细信息可以看官方文档

语义分析

按照C语言的语义确定AST中每个表达式的类型,相容的类型将根据C语言标准规范进行类型转换,例如两个不同类型的数相加这种,会进行隐式转换。
报告一些语义错误,例如:未定义的引用,运算符的操作数类型不匹配(如struct + int),函数调用与定义的参数数量不一致等。

静态程序分析

对代码进行静态分析,检查其中的语法错误, 代码风格和规范, 潜在的软件缺陷, 安全漏洞, 性能问题等

clang --analyze -Xanalyzer -analyzer-output=text ./test.c
#--analyze: 这是Clang编译器的一个选项,指示编译器执行静态代码分析。它告诉Clang不仅要编译代码,还要分析代码中潜在的问题。
#-Xanalyzer: 这个选项允许将后续参数传递给分析器。
#-analyzer-output=text: 这个选项告诉分析器以文本形式输出分析结果。换句话说,它指示分析器将结果以易读的文本形式打印到终端。

输出:
在这里插入图片描述

中间代码生成

生成IR中间代码。
IR:编译器定义的, 面向编译场景的指令集,与源代码编程语言和后端运行平台架构都无关得指令集。
Clang使用的是LLVM IR,gcc使用的是GIMPLE。
在这里插入图片描述

优化

在确保代码可观测行为一致的情况下,对代码进行优化,如果把程序看作状态机,那么优化就是指使用尽可能简单的状态机来代替复杂的状态机。
对volatile修饰变量的访问需要严格执行
程序结束时, 写入文件的数据需要与严格执行时一致
交互式设备的输入输出(stdio.h)需要与严格执行时一致
Clang or Gcc中的优化等级都是可控的,优化等级通过-O选项指定,其取值范围为0到3,其中0表示不进行优化,1表示基本优化,2表示更多的优化,3表示更加激进的优化。

clang -O1 ./test.c
#-O1表示基本优化
clang -S -foptimization-record-file=- a.c -O1
#-foptimization-record-file选项表示输出优化记录,-表示直接输出到终端,或者可以输入一个文件名,表示输出到文件中,格式为yaml

输出结果:

--- !Analysis
Pass:            prologepilog
Name:            StackSize
DebugLoc:        { File: test.c, Line: 1, Column: 0 }
Function:        main
Args:
  - NumStackBytes:   '0'
  - String:          ' stack bytes in function'
...
--- !Analysis
Pass:            asm-printer
Name:            InstructionMix
DebugLoc:        { File: test.c, Line: 6, Column: 1 }
Function:        main
Args:
  - String:          'BasicBlock: '
  - BasicBlock:      ''
  - String:          "\n"
  - String:          retq
  - String:          ': '
  - INST_retq:       '1'
  - String:          "\n"
  - String:          'xorl      '
  - String:          ': '
  - INST_xorl:       '1'
  - String:          "\n"
...
--- !Analysis
Pass:            asm-printer
Name:            InstructionCount
DebugLoc:        { File: test.c, Line: 1, Column: 0 }
Function:        main
Args:
  - NumInstructions: '2'
  - String:          ' instructions in function'
...

目标代码生成

clang -S test.c --target=riscv32-linux-gnu

生成的代码:

.text
        .attribute      4, 16
        .attribute      5, "rv32i2p0_m2p0_a2p0_f2p0_d2p0_c2p0"
        .file   "test.c"
        .globl  main                            # -- Begin function main
        .p2align        1
        .type   main,@function
main:                                   # @main
# %bb.0:
        addi    sp, sp, -32
        sw      ra, 28(sp)                      # 4-byte Folded Spill
        sw      s0, 24(sp)                      # 4-byte Folded Spill
        addi    s0, sp, 32
        addi    a0, zero, 1
        sw      a0, -12(s0)
        addi    a0, zero, 2
        sw      a0, -16(s0)
        lw      a0, -12(s0)
        lw      a1, -16(s0)
        mul     a0, a0, a1
        sw      a0, -20(s0)
        mv      a0, zero
        lw      s0, 24(sp)                      # 4-byte Folded Reload
        lw      ra, 28(sp)                      # 4-byte Folded Reload
        addi    sp, sp, 32
        ret
.Lfunc_end0:
        .size   main, .Lfunc_end0-main
                                        # -- End function
        .ident  "clang version 13.0.0 (https://github.com/llvm/llvm-project/ 24c8eaec9467b2aaf70b0db33a4e4dd415139a50)"
        .section        ".note.GNU-stack","",@progbits
        .addrsig

还可以使用ftime-report来查看编译中都做了什么,每个流程都花了多少时间。

clang -S test.c --target=riscv32-linux-gnu -ftime-report

汇编

这一步已经和编译原理不怎么搭边了

clang -c test.s

输出文件为一个.o文件,这个文件是二进制的,无法用cat等工具来看,cat和vim这些工具都是需要有相应的给人看的编码方式,例如UTF-8等。.o文件是为了给计算机看的,使用的编码方式是ISA规定的,具体可以看ISA的手册,每一个汇编语句对应一段二进制代码。
但是我们可以用objdump来看:

llvm-objdump -d test.o
#GNU也有objdump工具,但是需要给出二进制所对应的硬件ISA,llvm的objdump会自动识别ISA

输出结果:
在这里插入图片描述
objdump的原理跟汇编器的原理正好反过来的,objdump根据二进制指令反猜汇编代码,得到上图所示。

相关工具

clang是基于LLVM作为后端的,clang本身只是一个前端,LLVM不止是一个工具,而是多个工具集合到一起的,clang本身只负责将C或C++翻译成LLVM IR,再往后由LLVM IR到目标代码并不是由clang完成的,而是clang调用LLVM相应的工具。
clang中主要包括:
编译要用到的:

  • clang,从程序员的角度看就是C/C++编译器
  • llc:将LLVM IR的代码转化为目标代码
  • llvm-as:将LLVM IR的“汇编”转化为LLVM的“bitcode”【二者是一个东西不同形式,都是LLVM IR】
  • llvm-link:将多个LLVM bitcode文件链接为一个bitcode文件
  • lld链接器,为了替代系统链接器,输入多个.o文件和.a文件,链接成可执行文件eg:ELF。
    • 目前Linux系统自带的链接器一般都是GNU开发的ld链接器,lld文档里号称比ld要快很多。

配套的工具:

  • static analyzer:静态代码分析
  • llvm-objdump:objdump是一个用于分析目标文件(包括可执行文件、共享库、目标文件等)的工具。它可以显示目标文件的各种信息,包括可执行代码的汇编指令、符号表、段信息等。objdump通常与GNU Binutils软件包一起提供,是开发和调试工具链中的一部分。通过objdump,开发人员可以深入了解目标文件的结构和内容,有助于调试和优化程序。
  • llvm-strace:系统调用跟踪
  • llvm-size:输出二进制文件的大小信息
  • llvm-nm:列出二进制文件中的符号名

配套的工具就那么几种,GNU Binutils里面已经都实现过一遍了,只不过LLVM又实现了一遍。
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/521032.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【力扣】94. 二叉树的中序遍历、144. 二叉树的前序遍历、145. 二叉树的后序遍历

先序遍历&#xff1a;根-左-右中序遍历&#xff1a;左-根-右后序遍历&#xff1a;左-右-根 94. 二叉树的中序遍历 题目描述 给定一个二叉树的根节点 root &#xff0c;返回 它的 中序 遍历 。 示例 1&#xff1a; 输入&#xff1a;root [1,null,2,3] 输出&#xff1a;[1,3…

【ControlNet v3版本论文阅读】

网络部分最好有LDM或者Stable Diffusion的基础&#xff0c;有基础的话会看的很轻松 Abstract 1.提出了一种网络结构支持额外输入条件控制大型预训练的扩散模型。利用预训练模型学习一组不同的条件控制。 2.ControlNet对于小型&#xff08;<50k&#xff09;或大型&#xff…

vue项目使用element ui

目录 1、创建一个vue项目 2、找到element官网&#xff0c;点击指南&#xff0c;找到安装栏 3、 找到使用包管理器&#xff0c;复制命令 4、在main.js中引入element 5、使用element ui 6、找到App.vue&#xff0c;导入Button.vue文件&#xff0c;保存启动项目 1、创建一个vu…

千字深度理解:什么是电容的直流偏压特性?

原文来自微信公众号&#xff1a;工程师看海&#xff0c;与我联系&#xff1a;chunhou0820 看海原创视频教程&#xff1a;《运放秘籍》 大家好&#xff0c;我是工程师看海&#xff0c;很久没发千字长文了&#xff0c;原创文章欢迎点赞分享&#xff01; 电容是电路中最常用的被动…

GIt 删除某个特定commit

目的 多次commit&#xff0c;想删掉中间的一个/一些commit 操作方法 一句话说明&#xff1a;利用rebase命令的d表示移除commit的功能&#xff0c;来移除特定的commit # 压缩这3次commit,head~3表示从最近1次commit开始&#xff0c;前3个commit git rebase -i head~3rebase…

nest路由通配符使用

写法 路由路径 ‘ab*cd’ 将匹配 abcd 、ab_cd 、abecd 等。 src/cats/cats.controller.ts import { Controller, Get } from nestjs/common;Controller(cats) export class CatsController {Get(ab*cd)findAll1(): string {return 测试路由通配符;} }效果展示 截图1 截图2 …

蓝桥杯python组真题练习2

目录 1.裁纸刀 2.路径 3.排列字母 4.直线 5.纸张尺寸 1.裁纸刀 11.裁纸刀 - 蓝桥云课 (lanqiao.cn) import os import sys# 请在此输入您的代码sum 4 sum 19 sum 21*20 print(sum) 2.路径 12.路径 - 蓝桥云课 (lanqiao.cn) 这道题涉及到求两个数的最小公倍数。一开始…

企业如何设计和实施有效的网络安全演练?

现实世界中&#xff0c;武装部队一直利用兵棋推演进行实战化训练&#xff0c;为潜在的军事冲突做准备。随着当今的数字化转型&#xff0c;同样的概念正在以网络安全演习的形式在组织中得到应用&#xff0c;很多企业每年都会基于合理的网络攻击场景和事件响应做一些测试和模拟。…

Day:004(1) | Python爬虫:高效数据抓取的编程技术(数据解析)

数据解析-正则表达式 在前面我们已经搞定了怎样获取页面的内容&#xff0c;不过还差一步&#xff0c;这么多杂乱的代码夹杂文字我们怎样 把它提取出来整理呢&#xff1f;下面就开始介绍一个十分强大的工具&#xff0c;正则表达式&#xff01; 正则表达式是对字符串操作的一种…

【Python毕业设计】python基于CatBoost模型的混凝土强度预测研究(源码+数据集+毕业论文)【独一无二】

&#x1f449;博__主&#x1f448;&#xff1a;米码收割机 &#x1f449;技__能&#x1f448;&#xff1a;C/Python语言 &#x1f449;公众号&#x1f448;&#xff1a;测试开发自动化【获取源码商业合作】 &#x1f449;荣__誉&#x1f448;&#xff1a;阿里云博客专家博主、5…

PAC与BTI、MTE的关系

PAC与BTI、MTE的关系如何&#xff1f;标记是否有冲突&#xff1f;

manga-ocr漫画日文ocr

github 下载 解压 anaconda新建环境 conda create -n manga_ocr python3.8 激活环境 conda activate manga_ocr cd到解压目录 cd /d manga-ocr-master 安装依赖包 pip install -r requirements.txt pip3 install manga-ocr 下载离线model huggingface 123云盘 解压到一个目录…

蓝桥真题--路径之谜DFS解法

路径之谜 思路 前置知识&#xff1a;深度搜索模板搜索所有可以找的路径&#xff0c;将走过的靶子减去一走到最后一个格子的时候&#xff0c;直接去判断所有的靶子只有除最后一个位置的靶子&#xff0c;其余靶子都归零的时候&#xff0c;判断一个最后一个位置横坐标和纵坐标的靶…

Stale Diffusion、Drag Your Noise、PhysReaction、CityGaussian

本文首发于公众号&#xff1a;机器感知 Stale Diffusion、Drag Your Noise、PhysReaction、CityGaussian Drag Your Noise: Interactive Point-based Editing via Diffusion Semantic Propagation Point-based interactive editing serves as an essential tool to compleme…

爬虫 新闻网站 以湖南法治报为例(含详细注释) V1.0

目标网站&#xff1a;湖南法治报 爬取目的&#xff1a;为了获取某一地区更全面的在湖南法治报已发布的宣传新闻稿&#xff0c;同时也让自己的工作更便捷 环境&#xff1a;Pycharm2021&#xff0c;Python3.10&#xff0c; 安装的包&#xff1a;requests&#xff0c;csv&#xff…

RabbitMQ3.13.x之七_RabbitMQ消息队列模型

RabbitMQ3.13.x之七_RabbitMQ消息队列模型 文章目录 RabbitMQ3.13.x之七_RabbitMQ消息队列模型1. RabbitMQ消息队列模型1. 简单队列2. Work Queues(工作队列)3. Publish/Subscribe(发布/订阅)4. Routing(路由)5. Topics(主题)6. RPC(远程过程调用)7. Publisher Confirms(发布者…

2024-04-26 学习笔记(工业大模型,SAM优化)

坚持每周一篇&#xff0c;加油。 1.穷人的思想钢印&#xff1a;踏实做事情&#xff0c;不做马屁精 摘要&#xff1a;大模型摘要和文章意思相反。。可知这个思想钢印也传递给了AI Raiden说&#xff1a;最近一直在想一句话&#xff1a;假如你混了很久还是一事无成&#xff0c;不…

JavaWeb前端基础(HTML CSS JavaScript)

本文用于检验学习效果&#xff0c;忘记知识就去文末的链接复习 1. HTML 1.1 HTML基础 结构 头<head>身体<body> 内容 图片<img>段落<p>图标<link> 标签 单标签双标签 常用标签 div&#xff1a;分割块span&#xff1a;只占需要的大小p&…

「 典型安全漏洞系列 」12.OAuth 2.0身份验证漏洞

在浏览网页时&#xff0c;你肯定会遇到允许你使用社交媒体帐户登录的网站。此功能一般是使用流行的OAuth 2.0框架构建的。本文主要介绍如何识别和利用OAuth 2.0身份验证机制中发现的一些关键漏洞。 1. OAuth产生背景 为了更好的理解OAuth&#xff0c;我们假设有如下场景&#…

全国日照时数空间分布数据/月度降雨量分布/月均气温分布

引言 地理遥感生态网结合1971-2021年各地区地面气象监测站数据&#xff0c;应用气候数据空间插值软件Anusplin预测全国日照时数分布数据成果。得出全国各个省市自治区日照时数分布图&#xff0c;全国各省市自治区日照时数数据产品是地理遥感生态网推出的气象气候类数据产品之一…