11.Linux Linux系统和HelloWorld

三个问题

了解Hello World程序的执行过程有什么用?

1.对我们以后开发其他程序有很好的借鉴意义

2.对程序的各种报错有清晰的了解,从而针对性的解决问题

裸机开发中的HelloWorld程序是怎么执行的?

预编译--编译--汇编--连接 

 

Linux系统下的Hello World程序是怎么执行的?

GCC和Hello World

本章以Linux下使用GCC编译Hello World程序来讲解Linux C编程的相关流程和概念。

在Windows下开发C程序代码可以用Visual Studio,开发MCU的程序可以使用Keil、IAR等IDE集成开发环境;而在Linux下也有类似的IDE,如eclipse、Clion等。在这些环境下开发通常我们按照它们预定的步骤建立工程模板,再编写具体的代码,直接点击对应的编译、运行按钮即可完成操作。

在开发大型应用程序特别是调试的时候,使用IDE是非常好的选择, IDE的一个特点是它把各种常用操作封装成图形界面供用户使用,但如同学习Shell命令行的原因一样,在图形界面之下还潜藏着海量的功能,在Linux下的日常开发中常常直接使用命令行来操作,编译时配合其它命令行工具的时候简单快捷,而且非常直观,有利于了解编译的原理。

本章通过解构hello world程序在Linux下的编译运行过程, 掌握GCC、readelf、ldd工具的基本使用,便于理解开发流程以及后期建立编译工具链是要做什么事情。 了解各编译步骤及其生成的文件,这对后期编写Makefile及使用其它工具大有好处。 了解程序的链接过程有利于明白为什么某些程序需要 依赖特定的文件,从而方便专门定制Linux文件系统。

3.1. GCC编译工具链

GCC编译工具链(toolchain)是指以GCC编译器为核心的一整套工具,用于把源代码转化成可执行应用程序。它主要包含以下三部分内容:

  • gcc-core:即GCC编译器,用于完成预处理和编译过程,例如把C代码转换成汇编代码。

  • Binutils :除GCC编译器外的一系列小工具包括了链接器ld,汇编器as、目标文件格式查看器readelf等。

  • glibc:包含了主要的 C语言标准函数库,C语言中常常使用的打印函数printf、malloc函数就在glibc 库中。

在很多场合下会直接用GCC编译器来指代整套GCC编译工具链。

3.1.1. GCC编译器

GCC(GNU Compiler Collection)是由 GNU 开发的编程语言编译器。 GCC最初代表“GNU C Compiler”,当时只支持C语言。 后来又扩展能够支持更多编程语言,包括 C++、Fortran 和 Java 等。 因此,GCC也被重新定义为“GNU Compiler Collection”,成为历史上最优秀的编译器, 其执行效率与一般的编译器相比平均效率要高 20%~30%。

GCC的官网地址为:GCC, the GNU Compiler Collection- GNU Project,在Ubuntu系统下系统默认已经安装好GCC编译器,可以通过如下命令查看Ubuntu系统中GCC编译器的版本及安装路径:

#在主机上执行如下命令
gcc -v          #查看gcc编译器版本
which gcc       #查看gcc的安装路径

3.4. GCC编译过程

3.4.1. 基本语法

GCC使用的命令语法如下:

gcc [选项] 输入的文件名

常用选项:

  • -o:小写字母“o”,指定生成的可执行文件的名字,不指定的话生成的可执行文件名为a.out。

  • -E:只进行预处理,既不编译,也不汇编。

  • -S:只编译,不汇编。

  • -c:编译并汇编,但不进行链接。

  • -g:生成的可执行文件带调试信息,方便使用gdb进行调试。

  • -Ox:大写字母“O”加数字,设置程序的优化等级,如“-O0”“-O1” “-O2” “-O3”, 数字越大代码的优化等级越高,编译出来的程序一般会越小,但有可能会导致程序不正常运行。

3.4.1.1. 编译过程

若不了解程序的编译过程,那么GCC的编译选项会让人一头雾水。下面以X86_64平台下Ubuntu的编译过程为例进行初步讲解, ARM平台下Debian的编译过程也是类似的,不再进行分析。

GCC编译选项除了-g和-Ox选项,其它选项实际上都是编译的分步骤,即只进行某些编译过程。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
#直接编译成可执行文件
gcc hello.c -o hello

#以上命令等价于执行以下全部操作
#预处理,可理解为把头文件的代码汇总成C代码,把*.c转换得到*.i文件
gcc –E hello.c –o hello.i

#编译,可理解为把C代码转换为汇编代码,把*.i转换得到*.s文件
gcc –S hello.i –o hello.s

#汇编,可理解为把汇编代码转换为机器码,把*.s转换得到*.o,即目标文件
gcc –c hello.s –o hello.o

#链接,把不同文件之间的调用关系链接起来,把一个或多个*.o转换成最终的可执行文件
gcc hello.o –o hello

对于有MCU开发经验的读者,建议学习一下野火的《STM32库开发实战指南》、《i.MX RT库开发实战指南》中《MDK的编译过程及文件详解》章节,它从MCU的角度非常详细地讲解了上述编译过程。GCC的编译过程也是一样的,而且在Linux平台下解构这个过程更加直观,不过本章作为入门章节,仅从表面去建立编译原理的轮廓,不作深入介绍。

GCC 编译工具链在编译一个C源文件时需要经过以下 4 步:

  1. 预处理,在预处理过程中,对源代码文件中的文件包含(include)、 预编译语句(如宏定义define等)进行展开,生成.i文件。 可理解为把头文件的代码、宏之类的内容转换成更纯粹的C代码,不过生成的文件以.i为后缀。

  2. 编译,把预处理后的.i文件通过编译成为汇编语言,生成.s文件,即把代码从C语言转换成汇编语言,这是GCC编译器完成的工作。

  3. 汇编,将汇编语言文件经过汇编,生成目标文件.o文件,每一个源文件都对应一个目标文件。即把汇编语言的代码转换成机器码,这是as汇编器完成的工作。

  4. 链接,最后将每个源文件对应的.o文件链接起来,就生成一个可执行程序文件,这是链接器ld完成的工作。

四个案例

 gcc预处理c文件

 #预处理,可理解为把头文件的代码汇总成C代码,把*.c转换得到*.i文件

查看打印信息(-v)

sudo gcc -E hello.c -o hello.i -v


COLLECT_GCC_OPTIONS='-E' '-o' 'hello.i' '-v' '-mtune=generic' '-march=x86-64'
 /usr/lib/gcc/x86_64-linux-gnu/7/cc1 -E -quiet -v -imultiarch x86_64-linux-gnu hello.c -o hello.i -mtune=generic -march=x86-64 -fstack-protector-strong -Wformat -Wformat-security
 

 gcc编译c文件

sudo gcc -S hello.i -o hello.s -v

COLLECT_GCC_OPTIONS='-S' '-o' 'hello.s' '-v' '-mtune=generic' '-march=x86-64'
 /usr/lib/gcc/x86_64-linux-gnu/7/cc1 -fpreprocessed hello.i -quiet -dumpbase hello.i -mtune=generic -march=x86-64 -auxbase-strip hello.s -version -o hello.s -fstack-protector-strong -Wformat -Wformat-security
生成汇编文件

 gcc编译汇编

sudo gcc -c hello.s -o hello.o -v

 as -v --64 -o hello.o hello.s
 

 gcc链接可重定位文件

 sudo gcc hello.o -o hello -v

ld连接器

collter2封装了id连接器

/usr/lib/gcc/x86_64-linux-gnu/7/collect2 -plugin /usr/lib/gcc/x86_64-linux-gnu/7/liblto_plugin.so -plugin-opt=/usr/lib/gcc/x86_64-linux-gnu/7/lto-wrapper -plugin-opt=-fresolution=/tmp/ccJYPevg.res -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lc -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_s --build-id --eh-frame-hdr -m elf_x86_64 --hash-style=gnu --as-needed -dynamic-linker (说明编译是动态编译的)/lib64/ld-linux-x86-64.so.2 -pie -z now -z relro -o hello /usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/Scrt1.o /usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/crti.o /usr/lib/gcc/x86_64-linux-gnu/7/crtbeginS.o -L/usr/lib/gcc/x86_64-linux-gnu/7 -L/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu -L/usr/lib/gcc/x86_64-linux-gnu/7/../../../../lib -L/lib/x86_64-linux-gnu -L/lib/../lib -L/usr/lib/x86_64-linux-gnu -L/usr/lib/../lib -L/usr/lib/gcc/x86_64-linux-gnu/7/../../.. hello.o -lgcc --push-state --as-needed -lgcc_s --pop-state -lc -lgcc --push-state --as-needed -lgcc_s --pop-state /usr/lib/gcc/x86_64-linux-gnu/7/crtendS.o /usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/crtn.o
COLLECT_GCC_OPTIONS='-o' 'hello' '-v' '-mtune=generic' '-march=x86-64'
接下来给大家看一下静态编译

sudo gcc hello.o -o hello_static -v -static

/usr/lib/gcc/x86_64-linux-gnu/7/collect2 -plugin /usr/lib/gcc/x86_64-linux-gnu/7/liblto_plugin.so -plugin-opt=/usr/lib/gcc/x86_64-linux-gnu/7/lto-wrapper -plugin-opt=-fresolution=/tmp/ccvPgTQK.res -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_eh -plugin-opt=-pass-through=-lc --build-id -m elf_x86_64 --hash-style=gnu --as-needed -static -z relro -o hello_static /usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/crt1.o /usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/crti.o /usr/lib/gcc/x86_64-linux-gnu/7/crtbeginT.o -L/usr/lib/gcc/x86_64-linux-gnu/7 -L/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu -L/usr/lib/gcc/x86_64-linux-gnu/7/../../../../lib -L/lib/x86_64-linux-gnu -L/lib/../lib -L/usr/lib/x86_64-linux-gnu -L/usr/lib/../lib -L/usr/lib/gcc/x86_64-linux-gnu/7/../../.. hello.o --start-group -lgcc -lgcc_eh -lc --end-group /usr/lib/gcc/x86_64-linux-gnu/7/crtend.o /usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/crtn.o
COLLECT_GCC_OPTIONS='-o' 'hello_static' '-v' '-static' '-mtune=generic' '-march=x86-64'

动态编译出来的hello:8.3k

静态编译出来的hello_static:845k 

sudo gcc hello.c -o hello -v

/usr/lib/gcc/x86_64-linux-gnu/7/cc1 -quiet -v -imultiarch x86_64-linux-gnu hello.c -quiet -dumpbase hello.c -mtune=generic -march=x86-64 -auxbase hello -version -fstack-protector-strong -Wformat -Wformat-security -o /tmp/ccOeHSOm.s
GNU C11 (Ubuntu 7.5.0-3ubuntu1~18.04) version 7.5.0 (x86_64-linux-gnu)
    compiled by GNU C version 7.5.0, GMP version 6.1.2, MPFR version 4.0.1, MPC version 1.1.0, isl version isl-0.19-GMP
 

as汇编器

 as -v --64 -o /tmp/cc3ne0Az.o /tmp/ccOeHSOm.s
GNU assembler version 2.30 (x86_64-linux-gnu) using BFD version (GNU Binutils for Ubuntu) 2.30
COMPILER_PATH=/usr/lib/gcc/x86_64-linux-gnu/7/:/usr/lib/gcc/x86_64-linux-gnu/7/:/usr/lib/gcc/x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/7/:/usr/lib/gcc/x86_64-linux-gnu/
LIBRARY_PATH=/usr/lib/gcc/x86_64-linux-gnu/7/:/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/7/../../../../lib/:/lib/x86_64-linux-gnu/:/lib/../lib/:/usr/lib/x86_64-linux-gnu/:/usr/lib/../lib/:/usr/lib/gcc/x86_64-linux-gnu/7/../../../:/lib/:/usr/lib/
COLLECT_GCC_OPTIONS='-o' 'hello' '-v' '-mtune=generic' '-march=x86-64'
 

先了解到这里吧我睡觉了-_-

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值