编译、装载和库那些事-《程序员的自我修养-链接、装载和库》总结(读后感)

         如果你想了解程序如何编译、链接,动态库、静态库如何加载以及可执行文件生成过程,推荐俞甲子老师、石凡老师以及 潘爱民老师编著的《程序员的自我修养-链接、装载和库》。虽然相关知识并不能直观的提升编程技能,但是对于程序编译、运行过程中遇到的问题,能够更快、更深入的定位问题的原因。写这篇文章,一部分是为了和各位分享知识,一部分是为了网络上做备份。正文内容部分来自书籍,部分来自理解,可能有理解错误的地方,希望能够通过私信或评论的方式给我矫正和建议。

       北桥      作用:高速数据交换  涉及硬件:CPU、内存...             南桥      作用:低速设备数据交换    涉及硬件:键盘、磁盘...

       内存利用从一开始的分段=> 分页

写时复制(COW):两个任务同时自由的读取内存,其中任何一个试图修改内存数据时,就复制一份内存单独使用

        集成开发环境(以下Linux使用gcc,windows使用VS),将编译、链接称为构建,集成开发环境将具体细节隐藏,使得我们更专心程序开发。程序从源码变为可执行文件,其实分为四个步骤:

  • 预编译/预处理

       处理头文件,宏定义,条件编译等

       Linux:gcc -E hello.c -o hello.i     Windows:cl /P hello.c

  • 编译

       词法分析,语法分析,语义分析。。。

       词法分析:解析单个词汇;语法分析:生成语法树;语义分析:加入变量等类型到语法树    产生汇编文件

      Linux: gcc -S hello.i -o hello.s    Windows:cl /c hello.c 

       注:编译由c语言生成的hello.i时,用这种方式编译成功,但如果如果文件是cpp文件生成的,用gcc -S hello.i -o hello.s出错(我也不知道出错原因~。·~),可以使用gcc -S hello.cpp -o hello.s生成汇编文件

      Windows下好像无法直接生成汇编文件,再通过汇编生成机器指令文件。/c直接生成机器指令文件obj。不过可以通过cl /Fa hello.c生成汇编文件asm,也可以通过dumpbin /DISASM hello.obj查看汇编文件或导出为汇编文件。

  • 汇编

      汇编指令与机器指令的映射操作    产生机器指令文件

     Linux: gcc -c hello.s -o hello.o   Windows:cl /c hello.c 

  • 链接

     目标文件链接成可执行文件exe或out

     Linux:ld xxx.o xxxx.so;Windows:link   xxx.obj xxxx.dll

图1:COFF格式及变种

COFF格式

     我们经常遇见的.exe、.dll、.lib、obj、so等后缀文件,其实都是COFF格式的变种。Windows下在COFF格式基础上扩展为PF;Linux下在COFF格式基础上扩展为ELF。而COFF格式内容格式如上图1所示(只列出了主要的格式)。PE和ELF格式文件内容与COFF格式类似。

     如何查看以上文件格式内容呢?Windows平台提供dumpbin程序,dumpbin应用程序在VS的VC目录和cl编译器在同个目录下;Linux平台下提供objdump程序,objdump终端即可使用。dumpbin程序想要在终端直接使用,需要在path环境变量中配置。两个平台在终端直接键入dumpbin或objdump回车,即可提示选项。

图2:查看COFF格式文件

函数和变量修饰符       

       随着项目工程代码量越来越大,为防止函数重定义的冲突,各编译器生成目标文件obj对源码中函数和变量进行修饰。各编译器修饰规则不一样,导致各编译器生成的目标文件obj,没法混合链接,形成了二进制级别ABI的不兼容。Windows平台下VS编译C语言32位程序,添加修饰符下划线;编译C语言64位程序,不添加下划线。Linux平台下gcc编译C语言,不添加下划线。对于C++的修饰方式更加复杂,但修饰后的函数和变量32位程序也会有下划线,64位程序则没有。

例子一:oracle的oci库分为32位和64位,如果你在VS的32位平台,使用64位的OCI库,链接提示未定义错误。因为VS32位程序编译后有修饰符下划线,而64位的oci动态库的导入库,没有下划线,链接器不识别,所以未定义错误。这就是32位应用程序必须使用oci32位的库。查看导入库内容和目标文件内容可以使用查看COFF格式文件命令。

图3:OCI库与应用程序

例子二:在阅读源码时,经常能够看见extern "C"{}这种写法,主要是为了C++程序能够使用C库。下面从修饰符的角度描述,在cpp文件中使用到C库的A函数,cpp生成的中间文件obj将采用C++修饰符修饰,而C库的A函数导入库lib中采用C修饰符修饰,导致链接失败。将使用的函数使用extern写法包括,将使编译器知道,中间的函数使用C修饰符修饰,链接器链接可识别,链接成功。同理,也可以通过这种方式实现C++实现的库被C语言使用。

静态链接和动态链接

       静态链接库和动态链接库可以理解为中间文件obj的集合。在单独编译文件时,生成中间文件,而源文件中有些内容引用的是其余文件,这时编译器就会将外部引用的内容对应的地址做个标记,标识是外部引用的内容,地址设置位0x000000。

        对于静态链接,生成可执行文件,在链接的过程中,链接器起到重定位的作用,即将值为0x000000的地址,根据其余链接过来的文件重新设置地址,如果这个链接过程中,内容无法重定位,则链接出错,未定义。所有的中间文件,库文件等全部被写入到可执行文件中,所以静态链接生成的库较于动态链接来说大。

       对于动态链接,链接生成exe,生成可执行文件必须确定引用函数性质(猜测此时用到导入库.lib),链接器将引用函数标记为动态链接符号,但此时的可执行文件并没有包括依赖的库文件,即可执行文件地址还存在0x000000,未重定位,需要在运行的时候加载依赖的库dll,即重定位的操作放在了运行时。

      静态链接较于动态链接的缺点是1)可执行文件大 2)依赖库一旦更新,所有用到的项目工程需要重新编译

Linux共享库版本和查找过程

so 为shared object缩写;usr是Unix System Resource的缩写

图4:Linux下共享库路径

Windows下动态链接创建

     使用如下命令:cl /lDd xxx 创建带调试信息的动态库

declspec全称declaration specification 即声明规范

     __declspec(dllexport): 修饰动态库中需要被外部引用(导出)的函数

     __declspec(dllimport): 修饰从第三方动态库中导入的函数

     创建动态库dll时,会产生后缀名exp结尾的文件,该文件是链接器创建dll时的临时文件(包含导出表的信息)

函数调用惯例

图5:常用的函数调用方式

程序执行过程

1)运行库入口函数_start.XXX

2)环境初始化:堆、IO、线程、全局变量构造、环境变量填充argc,argv...

      执行这一步之后,才能在函数体中调用malloc,new等

3)main函数体执行

4)清理工作:堆、IO。。。

图6:程序调用路径

小结

gcc   GCC编译器                                             cl    msvc编译器

ld      GNU链接器                                             link msvc链接器

objdump GNU文件查看器                                dumpbin PE文件查看器

Windows System32与SysWOW64目录

System32目录存放本机操作系统位数的运行时库

SysWOW64目录只存在64位操作系统上,存放32位运行时库

x86-64/amd64:AMD 64位处理器(兼容32位应用);Intel IA-64 没很好提供32位应用支持

Wow64,全称是32bit Windows On 64bit Windows(64位Windows上的32位Windows)。

虽然整个系统是运行在64位模式,但如果一个应该程序是32位的,Windows会在64位的基础上,加载一个“32位的Windows”。这样,这个32位应用程序就以为自己是运行在32位的系统之上的。

于是,你也可以想象,这就意味着,64位的Windows,不但带有64位操作系统应有的系统文件,还带有32位系统应有的系统文件。

那让32位搬到SysWow64中去以后,那些写死在32位应用程序中的System32怎么办?答:Windows会给他们转向到SysWow64中去

在64位操作系统中,32位应用程序因为需要转向到SysWOW64原因要做一些小的牺牲。

此外,为了保证32位应用程序不与64位应用程序相冲突,除了System32文件夹外,注册表也需要为32位和64位提供两套,也需要让32位的应用程序在必要时重定向。

SysWow64文件夹,是64位Windows,用来存放32位Windows系统文件的地方。

什么是SysWow64 | Microsoft Learn

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
程序员自我修养:链接,装载》是一本由林锐、郭晓东、郑蕾等人合著的计算机技术书籍,在该书中,作者从程序员的视角出发,对链接装载等概念进行了深入的阐述和解析。 在计算机编程中,链接是指将各个源文件中的代码模块组合成一个可执行的程序的过程。链接可以分为静态链接和动态链接两种方式。静态链接是在编译时将所有代码模块合并成一个独立的可执行文件,而动态链接是在运行时根据需要加载相应的代码模块。 装载是指将一个程序从磁盘上加载到内存中准备执行的过程。在装载过程中,操作系统会为程序分配内存空间,并将程序中的各个模块加载到相应的内存地址上。装载过程中还包括解析模块之间的引用关系,以及进行地址重定位等操作。 是指一组可重用的代码模块,通过链接装载的方式被程序调用。可以分为静态动态库。静态是在编译时将的代码链接到程序中,使程序与的代码合并为一个可执行文件。动态库则是在运行时通过动态链接的方式加载并调用。 《程序员自我修养:链接,装载》对于理解链接装载的原理和机制具有极大的帮助。通过学习这些概念,程序员可以更好地优化代码结构和组织,提高程序的性能和可维护性。同时,了解链接装载的工作原理也对于进行调试和故障排除具有重要意义。 总之,链接装载是计算机编程中的重要概念,对于程序员来说掌握这些知识是非常必要的。《程序员自我修养:链接,装载》这本书提供了深入浅出的解释和实例,对于想要学习和掌握这些知识的程序员来说是一本非常有价值的参考书籍

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值