# C Book 摘要 :::success 摘选自泰晓科技整理的[C语言编程透视](https://tinylab.gitbooks.io/cbook/content/) ::: ## 疑惑 1. web服务器的请求到达时,服务器动态地加载和链接适当的函数 然后直接调用它。 ## VIM用法 indent -kr -gnu -i8 *** ## gcc编译 ### 预处理 ### 翻译 在编译之前C语言编译器会进行词法分析、语法分析,接着会把源代码翻译成中间语言(汇编语言) :::info - 可以通过gcc -S 看到这个中间结果 - 诸如 Shell 等解释语言也会经历一个词法分析和语法分析的阶段,不过之后并不会进行“翻译”,而是“解释”,边解释边执行。 ::: 语法检查也可以通过gcc来配置 不展开 编译器优化也是在这个阶段 通过 -o 来进行配置。 总体来说是一个trade-off的过程。 ### 汇编 汇编实际上还是翻译过程,只不过把作为中间结果的汇编代码翻译成了机器代码,即目标代码 生成可重定位的ELF文件,需要通过ld进行进一步链接成可执行程序和共享库。 动态链接库在链接时,库文件本身并没有添加到可执行文件中,只是在可执行文件中加入了该库的名字等信息,以便在可执行文件运行过程中引用库中的函数时由动态连接器去查找相关函数的地址,并调用他们。 ### 链接 - [x] 主要讨论静态链接 还没细看 通过修改汇编程序的 return 语句为 `_exit(0)` 和修改程序的入口为 `_start`,我们的代码不链接 gcc 默认链接的那些额外的文件同样可以工作得很好。 :::warning :+1: 这个知识还蛮有意思.. 具体可以看看这篇文章[Before main](http://linux.kutx.cn/linux/linux457.htm) ::: ## 程序执行的一刹那 主要介绍命令行接口 /bin/bash 暂时还不感兴趣.. ## 动态链接的细节 ### 基本概念 #### 1.ELF 本身包含可重定位目标文件 可执行文件 共享目标文件(.so)三种类型。但它们有统一的结构 - LF Header(ELF文件头) - Program Headers Table(程序头表,实际上叫段表好一些,用于描述可执行文件和可共享库) - Section 1 2 3 ... - Section Headers Table(节区头部表,用于链接可重定位文件成可执行文件或共享库) :::info - :cake: 通过 readelf 和 objdump 这两个辅助工具来认识下ELF文件。 - readelf -h myprintf.o Header - readelf -l myprintf.o 头部表(段表) - readelf -s myprintf.o 节区表 - readelf -x .data/.bss/.comment/.text myprintf.o - objdump -d -j .text myprintf.o 看具体段 ::: #### 2.符号 可执行文件除了编译器引入的一些符号外,主要就是用户自定义的全局变量,函数等。 可重定位文件仅仅包含用户自定义的一些符号 `nm test.o // nm是names的意思 列出ELF文件的符号表` 当检查可执行文件的时候 printf的地址还是不确定的 因为需要动态链接glibc #### 3.重定位 外部符号可能定义在动态数据库中,在程序运行时需要通过动态链接器进行重定位,即动态链接。 #### 4.动态链接 动态链接就是在程序运行时对符号进行重定位,确定符号对应的内存地址的过程。 Linux下符号的动态链接默认方式是Lazy-mode。只有在程序运行过程中用到该符号时才去解析它的位置 #### 5.动态链接器 目标文件纯粹是字节块的集合。这些块中,有些包含代码,有些包含数据,而其他的则包含指导链接器和加载器的数据机构。链接器将这些块连再一起,确定被连接块的运行时位置,并且修改代码和数据块中的各种位置。链接器对目标机器了解甚少,产生目标文件的编译器和汇编器已经完成了大部分工作。 :::info 这段话摘录自《深入理解计算机系统》 ::: ```shell= readelf -x .interp test可以得到程式的指定动态链接器 readelf -d test | grep NEEDED 程式需要的动态链接库 ldd test 通过 ldd 命令则 ``` **exec()之后和程序指令运行之前的过程是怎样的呢** >1. 将可执行文件的内存段添加到进程映像中; >2. 把共享目标内存段添加到进程映像中; >3. 为可执行文件和它的共享目标(动态链接库)执行重定位操作; >4. 关闭用来读入可执行文件的文件描述符,如果动态链接程序收到过这样的文件描述符的话; >5. 将控制转交给程序,使得程序好像从 exec() 直接得到控制 主要关注以下第三步 如何进行符号的重定位?下面来探求以下这个过程以及与其紧密相连的三个数据结构,分别是ELF文件的过程链接表,全局偏移表和重定位表,这三个表都是ELF文件的节区。 //这几个部分c-book介绍地过于实战了 先看看书 #### 6.PLT 过程链接表 #### 7.GOT 全局偏移表 #### 8.重定位表 ### 动态库的创建与调用 ### 动态链接过程 ### 加载 Unix系统中地每一个程序都运行在一个进程上下文中,有自己的虚拟地址空间。当外壳运行一个程序的时候,父外壳进程生成一个子进程。子进程通过execve()系统调用启动加载器。加载器删除子进程现有的虚拟存储段,并创建一组新的代码 数据 堆和栈。新的栈和堆被初始化为0。通过将虚拟地址空间中的页映射到可执行文件的页大小的片(chunk),新的代码和数据段被初始化为可执行文件的内容。最后 ,加载器跳转到_start地址,它最终会调用应用程序的main函数。除了一些头部信息,在加载过程中没有任何从磁盘到存储器的数据拷贝。 直到CPU引用一个被映射的虚拟页才会进行拷贝,此时,操作系统利用它的页面调度机制自动将页面从磁盘传送到存储器。 ### 动态链接 共享库是一个目标模块,在运行的时候,可以加载到任意的存储器地址。 两种方式: 1. 首先,在任何给定的文件系统中,对于一个库只有一个.so文件。所有引用该库的可执行目标文件共享这个.so文件的代码和数据,而不是像静态库的内容那样被拷贝和嵌入到引用它们的可执行文件中。 2. 其次在存储器中,一个共享库的.text节的一个副本可以被不同的正在运行的进程共享 当创建可执行文件时 静态执行一些链接。然后在程序加载时,动态完成连接过程。 此时.so的代码和数据节不会真的被拷贝到可执行文件中,反之,连接器拷贝了一些重定位和符号表信息,它们使得run time可以解析对动态库中代码和数据的引用。 :::warning 动态库的加载就是动态链接! :::
×
Sign in
Email
Password
Forgot password
or
By clicking below, you agree to our
terms of service
.
Sign in via Facebook
Sign in via Twitter
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
New to HackMD?
Sign up