关于gcc:gcc-好玩的-builtin-函数

gcc 好玩的 builtin 函数前言在本篇文章当中次要想给大家介绍一些在 gcc 编译器当中给咱们提供的一些好玩的内嵌函数 (builtin function) 。 __builtin_frame_address应用内嵌函数实现__builtin_frame_address(x) // 其中 x 一个整数这个函数次要是用于失去函数的栈帧的,更具体的来说是失去函数的 rbp (如果是 x86_64 的机器,在 32 位零碎上就是 ebp)的值,也就是栈帧的栈底的值。 咱们当初应用一个例子来验证测试一下: #include <stdio.h>void func_a(){ void* p = __builtin_frame_address(0); printf("fun_a frame address = %p\n", p);}int main(){ void* p = __builtin_frame_address(0); printf("main frame address = %p\n", p); func_a(); return 0;}下面的程序的输入后果如下所示: main frame address = 0x7ffcecdd7a00fun_a frame address = 0x7ffcecdd79d0下面输入的后果就是每个函数的栈帧中栈底 rbp/ebp 寄存器的值,可能你会有疑难,凭什么说这个值就是 rbp 的值。咱们当初来证实一下,咱们能够应用代码获取失去 rbp 的值。 应用内敛汇编实现#include <stdio.h>#include <sys/types.h>u_int64_t rbp;#define frame_address \ asm volatile( \ "movq %%rbp, %0;" \ :"=m"(rbp):: \ ); \ printf("rbp = %p from inline assembly\n", (void*) rbp);void bar(){ void* rbp = __builtin_frame_address(0); printf("rbp = %p\n", rbp); frame_address}int main(){ bar(); return 0;}在下面的程序当中,咱们应用一段宏能够失去寄存器 rbp 的值(在下面的代码当中,咱们应用内敛汇编失去 rbp 的值,并且将这个值存储到变量 rbp 当中),咱们将这个值和 builtin 函数的返回值进行比照,咱们就能够晓得返回的是不是寄存器 rbp 的值了,下面的程序执行后果如下所示: ...

November 20, 2022 · 7 min · jiezi

编译链接和加载介绍

整个过程 预处理器:将.c 文件转化成 .i文件,使用的gcc命令是:gcc –E,对应于预处理命令cpp;编译器:将.c/.h文件转换成.s文件,使用的gcc命令是:gcc –S,对应于编译命令 cc –S;汇编器:将.s 文件转化成 .o文件,使用的gcc 命令是:gcc –c,对应于汇编命令是 as;链接器:将.o文件转化成可执行程序,使用的gcc 命令是: gcc,对应于链接命令是 ld;加载器:将可执行程序加载到内存并进行执行,loader和ld-linux.so。过程详解1. 预编译在正式的编译阶段之前进行。预处理阶段将根据已放置在文件中的预处理指令来修改源文件的内容。 宏定义指令,如 #define a b 对于这种伪指令,预编译所要做的是将程序中的所有a用b替换,但作为字符串常量的 a则不被替换。还有 #undef,则将取消对某个宏的定义,使以后该串的出现不再被替换。条件编译指令,如#ifdef,#ifndef,#else,#elif,#endif等。 这些伪指令的引入使得程序员可以通过定义不同的宏来决定编译程序对哪些代码进行处理。预编译程序将根据有关的文件,将那些不必要的代码过滤掉头文件包含指令,如#include "FileName"或者#include 等。 该指令将头文件中的定义统统都加入到它所产生的输出文件中,以供编译程序对之进行处理。特殊符号,预编译程序可以识别一些特殊的符号。 例如在源程序中出现的LINE标识将被解释为当前行号(十进制数),FILE则被解释为当前被编译的C源程序的名称。预编译程序对于在源程序中出现的这些串将用合适的值进行替换。头文件的目的主要是为了使某些定义可以供多个不同的C源程序使用,这涉及到头文件的定位即搜索路径问题。头文件搜索规则如下: 所有header file的搜寻会从-I开始然后找环境变量 C_INCLUDE_PATH,CPLUS_INCLUDE_PATH,OBJC_INCLUDE_PATH指定的路径再找默认目录(/usr/include、/usr/local/include、/usr/lib/gcc-lib/i386-linux/2.95.2/include......)2. 编译通过词法分析和语法分析,在确认所有的指令都符合语法规则之后,将其翻译成等价的中间代码表示或汇编代码。 3. 汇编汇编器(as)把汇编语言代码翻译成目标机器指令(.o)。目标文件中所存放的也就是与源程序等效的目标的机器语言代码。目标文件由段组成。通常一个目标文件中至少有两个段: 代码段:该段中所包含的主要是程序的指令。该段一般是可读和可执行的,但一般却不可写。数据段:主要存放程序中要用到的各种全局变量或静态的数据。一般数据段都是可读,可写,可执行的。4. 链接将有关的目标文件彼此相连接生成可加载、可执行的目标文件。链接器的核心工作就是符号表解析和重定位。 4.1 链接的时机编译时,就是源代码被编译成机器代码时(静态链接器负责);加载时,也就是程序被加载到内存时(加载器负责);运行时,由应用程序来实施(动态链接器负责)。4.2 链接的作用使得分离编译成为可能;动态绑定(binding):使定义、实现、使用分离4.3 静态库搜索路径gcc先从-L寻找;再找环境变量LIBRARY_PATH指定的搜索路径;再找内定目录 /lib /usr/lib /usr/local/lib 这是当初compile gcc时写在程序内的。4.4 动态库搜索路径编译目标代码时指定的动态库搜索路径-L;环境变量LD_LIBRARY_PATH指定的动态库搜索路径;配置文件/etc/ld.so.conf中指定的动态库搜索路径;默认的动态库搜索路径/lib /usr/lib/ /usr/local/lib4.5 静态链接(编译时)链接器将函数的代码从其所在地(目标文件或静态链接库中)拷贝到最终的可执行程序中。这样该程序在被执行时这些代码将被装入到该进程的虚拟地址空间中。静态链接库实际上是一个目标文件的集合,其中的每个文件含有库中的一个或者一组相关函数的代码。为创建可执行文件,链接器必须要完成的主要任务: 符号解析:把目标文件中符号的定义和引用联系起来;重定位:把符号定义和内存地址对应起来,然后修改所有对符号的引用。重定位让我们结合具体的CPU指令来了解这个过程。假设我们有个全局变量叫做var,它在目标文件A中。我们在目标文件B里面要访问这个全局变量,比如我们在目标文件B里面有这么一条指令: movl $0x2a, var这条指令就是给这个var变量赋值0x2a,相当于C语言中的语句var = 42。然后我们编译目标文件B,得到这条指令机器码 由于在编译目标文件B的时候,编译器并不知道变量var的目标地址,所以编译器在没法确定地址的情况下,将这条mov指令的目标地址设为0,等待链接器在将目标文件A和B链接起来的时候再将其修正。假设A和B链接后,变量var的地址确定下来为0x1000,那么链接器将会把这个指令的目标地址部分修改成0x10000。这个地址修正的过程也叫做重定位(Relocation),每个要被修正的地方叫一个重定位入口(Relocation Entry)。每个目标文件除了拥有自己的数据和二进制代码外,还提供了三个表:未解决符号表、导出符号表、地址重定向表。 未解决符号表提供了所有在该编译单元里引用但是定义并不是在本编译单元的符号以及其出现的地址; 导出符号表提供了本编译单元具有定义,并且愿意提供给其他单元使用的符号及其地址;地址重定向表提供了本编译单元所有对自身地址的引用的记录;编译器将extern声明的变量置入未解决符号表,而不置入导出符号表;----外部链接编译器将static声明的全局变量不置入未解决符号表,也不置入导出符号表,因此其他单元无法使用;----内部链接普通变化及其函数被置入导出符号表; 4.6 动态链接(加载、运行时)在此种方式下,函数的定义在动态链接库或共享对象的目标文件中。在编译的链接阶段,动态链接库只提供符号表和其他少量信息用于保证所有符号引用都有定义,保证编译顺利通过。动态链接器(ld-linux.so)链接程序在运行过程中根据记录的共享对象的符号定义来动态加载共享库,然后完成重定位。在此可执行文件被执行时,动态链接库的全部内容将被映射到运行时相应进程的虚地址空间。动态链接程序将根据可执行程序中记录的信息找到相应的函数代码。 各种文件ELF ExecutableELF exectuable File内容文件头描述文件属性, 段表, 重定位表代码段 .code .text源代码编译后的机器指令, 程序的指令数据段.data已初始化的全局变量,局部静态变量.bss未初始化的全局变量, 局部静态变量.symtab存放在程序中定义和引用的函数和全局变量的信息符号表目标文件可重定位(Relocatable)文件:由编译器和汇编器生成,可以与其他可重定位目标文件合并创建一个可执行或共享的目标文件;共享(Shared)目标文件:一类特殊的可重定位目标文件,可以在链接(静态共享库)时加入目标文件或加载时或运行时(动态共享库)被动态的加载到内存并执行;可执行(Executable)文件:由链接器生成,可以直接通过加载器加载到内存中充当进程执行的文件。静态库(Archive FIle)多个.o文件的集合.Linux中默认后缀是.a, 静态库的的.o没有进行链接,只是.o的集合。 ...

July 12, 2019 · 1 min · jiezi

CentOS-7上升级安装gcc

转载请注明文章出处:https://tlanyan.me/install-up... CentOS 7官方源带的gcc最新版本是4.8.5,发布于2015年,年代久远且不支持c++14。要编译c++14及以上项目,必须要升级现有版本或者安装高版本的gcc。 问题的解决办法主要有两种:手动编译安装(也可以从其他机器拷贝或者网上下载),或从源安装。大多数情况下本人都不推荐从源码编译,因为过程中常会出现各种依赖问题需要人工手动解决,费时(单核编译gcc至少一小时)且耗费精力,并且软件升级还要再来一次,相当的折腾。鉴于此下文介绍从源安装的方法。 红帽其实已经编译好了高版本的gcc,但未更新到base和epel这两个常用的源中,而是放在scl中。第一步便是安装scl: yum install -y centos-release-scl如果你之前用过grouplist/install等命令,应该知道gcc包含在Development Tools这个组中。那么scl中的gcc/g++软件包的前缀都是devtoolset,也就不难理解了。安装gcc 6版本的命令是: yum install -y devtoolset-6-gcc devtoolset-6-gcc-c++devtoolset-6中的gcc版本为gcc 6,除此之外还有如下版本: devtoolset-3: gcc 4.9devtoolset-4: gcc 5devtoolset-6: gcc 6devtoolset-7: gcc 7devtoolset-8: gcc 8至于为什么没有devtoolset-5,我也不清楚,估计是包含在devtoolset-4中了吧。值得说明的是这些软件包可以同时安装,不会相互覆盖和冲突,也不会覆盖系统的版本。即可以在系统中同时安装gcc 6, gcc 7, gcc 8等多个版本。 因为不会覆盖系统默认的gcc,使用这些软件的方法有四种: 使用绝对路径;添加可执行文件路径到PATH环境变量;使用官方推荐的加载命令:scl enable devtoolset-x bash, x为要启用的版本;执行安装软件自带的脚本: source /opt/rh/devtoolset-x/enable,x为要启用的版本。推荐使用最后两种方式,例如启用gcc 6: source /opt/rh/devtoolset-6/enable,然后输入gcc -v查看版本已经变成gcc 6.3.1。使用类似的命令可以随时在多个gcc版本中切换。如果希望长期使用高版本,可将此命令写入.bashrc等配置文件。 最后说一下,scl以及scl-rh源中的软件包都安装在/opt/rh/目录下,包含可执行文件、配置等。所以启用命令的路径是/opt/rh/xxx/enable,安装的服务重启命令则可能是systemctl restart rh-xxx,需要加rh或scl前缀以区别其他源的包。如果你用过remi/gitlab等源,其行为方式也是类似的。 参考https://www.softwarecollectio...

June 23, 2019 · 1 min · jiezi

linux-源码安装gcc-g

你经历过绝望吗 就像↓ 没错,今天我在断网的centos服务上安装GMP的时候就是这样的,你要安装gcc就必须依赖gmp,但是你要安装gmp你又需要gcc来编译。。。ps 我用的centos是mini版,原本只是想在上面装个NGINX,MySQL,没想到都需要gcc环境,那好吧 需要,我就安装就好了。然后百度、Google都是下载gmp 、mpfr、mpc,然后编译,安装,然后就。。。。 折腾了一个多小时,终于找到了一种叫rpm的安装方式(原谅我是个Linux小白),然后就发现,其实只要三分钟就可以了。。。 下载源http://vault.centos.org/6.5/o... 安装rpm -ivh ppl-0.10.2-11.el6.x86_64.rpm ...全部安装 完成

May 4, 2019 · 1 min · jiezi

gcc版本升级

直接上代码了wget https://ftp.gnu.org/gnu/gcc/gcc-6.2.0/gcc-6.2.0.tar.gztar -zxvf gcc-6.2.0.tar.gzcd gcc-6.2.0./contrib/download_prerequisites ##安装gcc需要下载诸如gmp、mpfr、mpc等依赖文件,执行download_prerequisites将会自动下载这些软件并解压到当前目录(文件不大,总共只有几MB,请耐心等待下载)。mkdir buildcd build../configure -enable-checking=release -enable-languages=c,c++ -disable-multilibmake && make install遇到的坑:gcc 版本升级后 使用 编译时 还是提示版本过低,是因为系统环境变量还是链接的之前的老版本,需要删除#列出已安装的gcc包rpm -qa | grep gcc*#删除rpm -e gcc-4.4.7-23.el6.x86_64rpm -e gcc-c++-4.4.7-23.el6.x86_64

December 13, 2018 · 1 min · jiezi