链接过程

note:这里还要补充的可能很多,包含可重定位目标文件的内容等。

符号和符号表

在ld等链接器的上下文中,有三种不同的符号。每一个可重定位目标模块m都有一个符号表,它包含m所定义的和引用的符号的信息。

由m所定义的并且能够被其他模块所引用的全局符号。就是非静态的c函数和被定义为不带c static属性的全局变量。

在其他模块定义并被模块m引用的全局符号。对应于定义在其他模块中的c函数和变量,external。

只在模块m定义和引用的本地符号。有的就是对应于带static属性的c函数和全局变量。这些符号在模块m中随处可见,但是不能被其他模块引用。目标文件中对应于模块m的节和相应的源文件中的名字都能获得本地符号。

.symtab中的符号表不包含对应于本地非静态程序变量的任何符号。这些符号在运行时在栈中被管理,链接器对此类符号不感冒。

对于static属性的本地过程变量是不在栈中管理的,相反,编译器在.data和.bss中为每个定义分配空间,并在符号表中创建一个有唯一名字的本地链接器符号。

带有static属性的全局变量和函数是模块私有的。相反,则是可以被其他模块访问的。

符号表是由汇编器构造的,使用编译器输出到汇编语言.s文件中的符号。

关于这个表的每个条目,有name和value。value是表示符号的地址,对于可重定位的模块来说,value是距定义目标的节的起始位置的偏移。还有size是目标的大小。每个符号都和目标文件的某个节相关联。.ndx为1表示.text节,为3表示.data节。

链接器解析符号引用的方法是将每个引用和它输入的可重定位的目标文件的符号表中的一个确定的符号定义联系起来。

符号解析

首先,我对符号表已经有了一定的概念,就是那张可以重定义的符号表(含有对符号的定义)。这个表格第一次接触到,还是会比较新颖的,现在就成了学习符号解析的利器了。

为什么需要解析符号引用?因为在代码的其它部分会使用到这些符号。

对于不同类型的符号,符号解析分别有什么特点?对那些引用和定义在同一个模块中的符号,符号解析是非常简单的。

当编译器遇到一个不是在本地定义的全局符号的时候呢?它当然会假设该符号是在其他模块中定义的,然后就去查找(这个过程就生成一个链接器符号表条目,并把它交给链接器处理)如果链接器在它的任何模块都找不到这个被引用的符号,它就输出一条错误信息。。

(note哈哈,下面将要给出一个例子,这个很简单,也经常遇到。但是在c.learncodethehardway里面介绍了很多静态库和动态库编译链接的东西,从这里过去便可以做进一步的理解了。)

在cpp和Java 中由于有重载的存在,所以链接器貌似要使用到一种叫做mangling的技术(可以多查找一点资料看看,应该非常有意思)

那么链接器是如何工作的呢?

强弱符号的概念?没有初始化的全局变量则是弱符号。

链接器的逻辑并不复杂。

但是在特殊的情况下,链接器是如何解析多重定义的全局符号的?

规则:

1.部允许有多个强符号。

2.如果有一个强符号和多个弱符号,那么选择强符号。(这里输出一个例子,非常有意思哦)

3.如果有多个弱符号,那么从这些弱符号中任意选择一个。(这里甚至还谈到了不同类型的重复符号,可能会产生很大的分歧)

与静态库(以前写好的模块)链接

note:这里有很多例子和技巧可言(恰好还有这方面的经验)。

除了静态库,还有哪些可以使用到的alternative?

为什么系统要支持库的概念呢?因为他定义了一组广泛的标准IO,字符串操作盒证书数学函数等。他们在libc.a中。如果不使用静态库,可以由多种方法来做这个工作(比如让编译器辨认出对标准函数的调用,并直接生成相应的代码(这个对编译器的复杂性将是一个可怕的灾难。。。。当然还有别的缺点啦。根本就是不合适的))

另外一个呢?就是把所有的标准C函数都放在一个单独的可重定位目标模块中,然后应用程序员就可以把这个模块连接到他们的可执行文件中啦。。。(和上一种方法相比,至少能够把编译器的实现和标准函数的实现分离开来)缺点呢?每个可执行文件都包含着一份标准函数集合的完全拷贝,这对磁盘空间是很大的浪费。更糟糕的是,每个运行的程序都将它自己的这些函数的拷贝放在存储器中,这又是一个极大地浪费。

当然我们也可以为每个标准函数创建一个独立的可重定位文件,把他们放在一个大家都知道的目录中来解决其中的一些问题。(这需要应用程序员现实的简介合适的目标模块到他们的可执行文件)

静态库到底是什么?有什么好处?

在unix系统中,静态库以一种称为archive的特殊文件格式存放在磁盘中。存档文件是一组连接起来的可重定位目标文件的集合(有一个头部用来描述每个成员目标文件的大小和位置)。存档文件名由后缀.a标志。

现在有一个很重要的问题,那就是链接器如何使用静态库来解析引用?

重定位

是符号解析的下一步,将合并输入模块,并为每个符号分配运行时地址。

可执行目标文件

我们的c程序,开始时是一组ascii 文本文件,已经被转化为一个二进制文件,而且这个二进制文件包含加载程序到存储器并运行它所需的所有信息。

加载可执行目标文件

一个loader的操作系统代码,来运行可执行程序。

动态链接共享库

从应用程序中加载和链接共享库

p.s.,这些我都讲的过于简略,有空再详细补充图片解析和内容

参考文献

csapp:Computer Systems A Programmer’s perspective

http://c.learncodethehardway.org/book/ex28.html

http://c.learncodethehardway.org/book/ex29.html

时间: 2024-10-14 13:04:22

链接过程的相关文章

C++编译与链接(1)-编译与链接过程

大家知道计算机使用的一系列的1和0 那个一个C++语言程序又是如何从一个个.h和.cpp文件变成包含1和0的可执行文件呢? 可以认为有以下的几个阶段 源程序->预处理->编译和优化->生成目标文件->链接->可执行文件 1.预处理 C++的预处理是指在C++程序源代码被编译之前,由预处理器对C++程序源代码进行的处理.这个过程并不对程序的源代码进行解析. 这里的预处理器(preprocessor)是指真正的编译开始之前由编译器调用的一个独立程序. 预处理器主要负责以下的几处

程序的编译链接过程

还是从HelloWorld开始说吧... #include <stdio.h> int main(int argc, char* argv[]) { printf("Hello World!\n"); return 0; } 从源文件Hello.cpp编译链接成Hello.exe,需要经历如下步骤: 可使用以下命令,直接从源文件生成可执行文件 linux: gcc -lstdc++ Hello.cpp -o Hello.out // 要带上lstdc参数,否则会报undef

第20课 链接过程简介

1. 链接器的意义 (1)每个C语言源文件被编译后生成目标文件,这些目标文件最终要被链接在一起生成可执行文件. (2)链接器的主要作用是把各个模块之间相互引用的部分处理好,使得各个模块之间能够正确的衔接. 2. 静态链接 (1)由链接器在链接时将库的内容直接加入到可执行程序中 (2)Linux下静态链的创建和使用 ①编译静态库源码:gcc –c  lib.c –o lib.o ②生成静态库文件:ar –q lib.a lib.o //将lib.o与其他文件打包到lib.a中 ③使用静态库编译:g

IAR生成文件链接过程解析

IAR生成文件设置 最近在搞contiki在IAR上的移植工作,好不容易移植完成了,但是生成的bin文件超出了MCU的Flash大小,MCUFlash为512K,生成的bin文件747K,开始一直认为是系统文件太多导致,后来觉得疑点颇多,所以就研究了一下IAR工程的编译和链接过程,具体过程如下: 1. 使用IAR compiler + assembler,对工程的每个".c/.cpp/.asm",分别生成一个.o文件,为可重定位ELF,叫做symobol.(内含的目标代码实际为机器码)

GCC编译链接过程

编译链接过程 代码 #cat main.c #include <stdio.h> int add(int x, int y); int sub(int x, int y); int mul(int x, int y); int div(int x, int y); int main(void) { printf("add:%d\n", add(1,2)); printf("sub:%d\n", sub(10,100)); printf("mul

嵌入式C语言自我修养 09:链接过程中的强符号和弱符号

9.1 属性声明:weak GNU C 通过 attribute 声明weak属性,可以将一个强符号转换为弱符号. 使用方法如下. void __attribute__((weak)) func(void); int num __attribte__((weak); 编译器在编译源程序时,无论你是变量名.函数名,在它眼里,都是一个符号而已,用来表征一个地址.编译器会将这些符号集中,存放到一个叫符号表的 section 中. 在一个软件工程项目中,可能有多个源文件,由不同工程师开发.有时候可能会遇

C++模版的编译链接过程

模版(template)设计的初衷,是设计一种自动实例化机制,不需要使用者参与,编译器可根据使用者提供的模版参数再套用类的定义来实例化.所谓实例化,除了包含对于程序变量的实例化,即开辟空间并设置某些变量的初值(构造函数)以及指针(如vptr)以及其他支持(virtual base class offset),还有对于函数的实例化,即根据函数的定义生成机器指令,并在函数调用处提供函数的入口地址.简单来看,普通类和模版类的区别,在于普通类实例化时有较为固定的空间开销(除非类似new string(n

Delphi编译/链接过程

下面展示了Delphi是怎样编译源文件,并且把它们链接起来,最终形成可执行文件. 当Delphi编译项目(Project)时,将编译项目源文件.窗体单元和其他相关单元,在这个过程中将会发生好几件事情: 首先,Object Pascal编译器把项目单元编译为二进制对象文件,然后资源编辑器将把诸如程序图标.窗体文件等资源编译成二进制资源文件,接着链接开始起作用:链接器根据编译器产生的二进制文件,依项目需要增加一些库文件,并把这些文件综合在一起产生最终的可执行文件. 编译.创建和链接 每当点击Run按

[转]keil编译链接过程以及ARMCC、ARMASM、FROMELF、ARMLINK、ARMAR的使用

1.keil5 MDK的编译工具 armar.exe armasm.exe armcc.exe armlink.exe fromelf.exe 以及动态链接库 armcompiler_libFNP.dll 2.各工具用法 >>>armar.exe 可以在windows下使用命令行切换到该程序所在文件夹(keil5\ARM\ARMCC\bin),执行armar.exe -h进行命令查看.若有gitbash的话直接在该文件夹下右键选择gitbash here,之后运行./armar.exe

c 编译和链接过程

详解link  有 些人写C/C++(以下假定为C++)程序,对unresolved external link或者duplicated external simbol的错误信息不知所措(因为这样的错误信息不能定位到某一行).或者对语言的一些部分不知道为什么要(或者不要)这样那样设计.了解本文之后, 或许会有一些答案.      首先看看我们是如何写一个程序的.如果你在使用某种IDE(Visual Studio,Elicpse,Dev C++等),你可能不会发现程序是如何组织起来的(很多人因此而