大话程序链接过程(一)

0x00 前言

最近读了《程序员的自我修养:链接、装载与库》,对以前不甚了解的链接过程清晰了不少,所以准备和大家简单的聊一聊链接的过程。今天我们只说静态链接(因为我动态链接还没看完,咳咳)。

0x01 为什么要链接

作为初学者,大家心里可能会嘀咕,为什么我的程序在编译完之后需要链接呢。这里我们拿大家初学c语言都会写的一段代码给大家解释一下。

#include 
int main()
{
  printf("hello world!");
  return 0;
}

这段代码估计大家都可以盲打下来了,我们分析一下代码成分。第一句#include 是包含头文件的命令,这里不是我们的重点暂时掠过。然后是main函数,这是我们程序的入口,接下来到了一个printf函数。好了,大家有没有想过这个printf函数是从哪里来的呢?我们都知道函数有声明、定义、调用这几个过程。printf的定义在include进来的库函数中已经完成了。我们的程序中也有了对printf的调用。那么其定义在哪里呢?这就是我们链接的意义所在了。我们知道,其实类似printf这种API的底层实现是比较复杂的,如果每个程序员在输出“hello world”的时候都需要重写一遍API那么编程无疑是痛苦的。所以系统为我们提供了一系列的库函数,我们直接调用就可以了。
大家如果注意观察的话就能发现,我们的c程序在VC编译后会出现.obj的文件,在linux下会出现.o文件。这些文件我们称之为目标文件,拿上面的helloworld程序来说,在其目标文件状态时,虽然其内部构造和可执行文件差不多,不过他还是无法运行的,因为其缺少链接这个过程。

0x02 动态链接和静态链接的区别

这里我们简单说一下他们的根本区别,动态链接的话在可执行文件中找不到printf的实现代码,其代码会在装载的时候载入程序的虚拟地址空间(关于虚拟地址概念可以看我前面的文章)。静态链接则会把printf的代码也链入可执行文件。大家记到这里也就差不多了。

0x03 静态链接

下面我们以linux环境来说。前面说到了helloworld被编译到了.o文件这一步,熟悉linux c程序编译的同学都知道,用ld链接一下.o文件即可生成可执行文件。那么这个过程发生了什么呢?
首先我们的.o文件中的其实是包含一个符号表的,其中记录着自身的符号情况。关于符号的定义由于篇幅问题不好展开,大家可以自行百度一下。而我们的printf函数就在符号表中,而其状态是UND的,即标志我们的printf函数是被定义在其他地方的,而非当前目标文件中。
当我们在使用ld命令链接的时候,系统会将我们的helloword.o文件默认和系统的静态链接库链接,而我们的printf函数就定义在该静态链接库中。ld将helloworld.o文件和静态链接库中的printf的定义代码整合,形成一个新的文件。这个文件就是我们的可执行文件了,运行他就可以在屏幕上打印"hello world"。

0x04 后记

今天的第一篇写的比较浅显,主要和大家阐述一下链接的概念和过程。下面一篇我将会给大家分享关于静态链接的时候hellworld.o的代码和静态链接库的内容是如何整合、索引、修正的。希望大家可以多多支持!

你可能感兴趣的:(操作系统,静态链接)