CSAPP--Chap7 链接

一、hello.c 你经历了什么?

话不多说,直接上图:

1. 预处理:hello.c --> hello.i  (命令 gcc -E或cpp)

2.编译:hello.i --> hello.s    (gcc -0g -S)

3.汇编:hello.s --> hello.o     (gcc或as)

4.链接: hello.o + 所需静态库 --> hello(gcc或ld)

其实gcc 是把所有步骤(cpp、cc1、as)都做了一遍。

 

二、链接,你是什么鬼?

2.1 链接的本质

上图:

没错了,其实链接就是把相同的块(代码、数据)分别合并到一起的过程,当然这只能算是重定位中的一部分吧;所以在合并之前还得进行符号解析,不然咋知道符号引用关系勒。

1.符号解析 Symbol resolution

程序中会有定义和引用的符号(非静态全局函数/变量),局部非静态变量是保存在栈中的,它在链接器眼中就是空气,不存在的。 而编译器会把定义的符号存在一定符号表中,链接器就将符号的引用和确定的符号建立关联。(还有强符号和弱符号的问题,强符合就是函数和初始化的全局变量,弱符号就是未初始化的全局变量)

强弱符号处理规则:

  1. 不能出现多个同名的强符号,不然就会出现链接错误
  2. 如果有同名的强符号和弱符号,选择强符号,弱符号是无效的
  3. 如果有多个弱符号,随便选择一个

 

那类型不匹配结果会如何呢? 上个例子来看看:

//文件 mismatch-main.c
long int x;  /* Weak symbol */
int main(int argc,
         char *argv[]) {
    printf("%ld\n", x);
    return 0;
}
//文件 mismatch-variable.c
/* Global strong symbol */
double x = 3.14;

 gcc -Og -o  mismatch  mismatch-main.c mismatch-variable.c

运行结果:

居然不是默认0,也不是3.14?这么大的数字是从哪来的??什么鬼东西? 

这是因为强符合 x=3.14,而浮点数在计算机中的存储、运算、表示等是按IEEE754标准。

 

再看一个全局变量的例子吧

//文件global.h
extern int g;
int f();

使用extern 关键字 ,其实就是告诉说明这个全局变量,在别的地方也有,所以容易被其他地方的修改所影响到。

//文件global-c1.c
#include "global.h"

int f() {
  return g+1;
}
//文件global-c2.c
#include <stdio.h>
#include "global.h"

int g = 0;

int main(int argc, char *argv[]) {
    if (argc >= 2) {
        g = atoi(argv[1]);
    }
    printf("g = %d.  f() = %d\n", g, f());
    return 0;
}

 运行: gcc -Wall -Og -o global global-c1.c global-c2.c

结果:

 使用的是g=0 强符号,所以没问题。

 

//文件 global-1.h
#ifdef INITIALIZE
  int g = 23;
  static int init = 1;
#else
  extern int g;
  static int init = 0;
#endif

----------------------------
//文件global-c1.c
#include "global-1.h"

int f() {
    return g+1;
}
----------------------------
//文件global-c4.c
#define INITIALIZE
#include <stdio.h>
#include "global-1.h"
  // int  f();
int main(int argc, char** argv) {
  if (init){
    // do something, e.g., g=31;
    //g=31;
  int t = f();
  printf("Calling f yields %d\n", t);
  return 0;
  }
}


结果:

说明:注释//g =31时,头文件替换是这样的

而加上g=31后,g被重新赋值了,所以g使用的是重新赋值之后的。

 

2.重定位 Relocation 

重定位其实包括合并.o文件(如上图,把各块合在一起成可执行对象文件),确定每个符号的地址(这是虚拟地址)和在指令中填入新地址这三步。

//文件main.c
int array[2] = {1, 2};

int main(int argc, char** argv)
{
    int val = sum(array, 2);
    return val;
}

 可以算出 sum()函数的地址 0x614=0x60f+0x5。

下图老师的ppt中更易理解,对一些汇编代码进行注解。

 

已为你打包,随便拿去用吧

比如你想计算某些数学式或排序,或输入输出等,当然这些别人都实现好了,我们直接拿来用就行,比如c语言中的一些库中都有。所以我们用的时候将头文件一包含进来就能使用了,而数学式与输入输出是不一样的,所以它们被打到不同的包中。因为将所有的函数放到一个源文件中,但使用时候只是用到其中之一却要全部加载进来,是很不明智的。

静态库 Static Library

在编译期间由编译器与连接器将它集成至应用程序内,并制作成目标文件以及可以独立运作的可执行文件。具体过程就是把不同文件的 .o 文件通过 Archiver 打包成为一个 .a 文件。

例如:自己编写一个向量运算库 libvector.a,其中包含两个函数 addvec 和 multvec.

// 文件 main.c
#include <stdio.h>
#include "vector.h"

int x[2] = {1, 2};
int y[2] = {3, 4};
int z[2];

int main()
{
    addvec(x, y, z, 2);
    printf("z = [%d %d]\n", z[0], z[1]);
    return 0;
}

// -----------------------------------------
// 文件 addvec.c
void addvec(int *x, int *y, int *z, int n)
{
    int i;
    for (i = 0; i < n; i++)
        z[i] = x[i] + y[i];
}

// -----------------------------------------
// 文件 multvec.c
void multvec(int *x, int *y, int *z, int n)
{
    int i;
    for (i = 0; i < n; i++)
        z[i] = x[i] * y[i];
}

 具体过程是这样的:

gcc -Wall -Og -static -o prog2c main2.o -L. -lvector  
gcc -Wall -Og -static -o prog2c main2.o libvector.a

上面两条语句等效。-lvector 就是libvector.a的一种表示 。-Wall是打印出all warning, -O (-O1)是一级优化级别,-g是用于调式。

但如果使用这样顺序链接  gcc -static -o prog2c -L. -lvector main2.o

则会报错:main2.o: In function `main':

main2.c:(.text+0x19): undefined reference to `addvec' collect2: error: ld returned 1 exit status

这是因为链接器是如何解析外部引用,是按顺序查找,所以实际上是有引用依赖问题的。

上面命令中,在编译链接的时候,如果在 main2.o 中发现了外部引用,就会在 -lvector中查找,但是如果反过来,在下面语句中 main2.o 后面没有东西,就会出现找不到引用的错误。

但是我有个疑问就是我在main.c中引用了 sum.c 中的sum()函数,而我直接gcc 编译链接两个文件的时候,无论把main.c和sum.c哪个放在前面都是可以的? 而一步一步执行的时候到链接时候出现了一个不能识别.o文件的错误,查找资料没有得到解决。

 

共享库 Shared Library

     静态库很方便,但是如果我们只是想用库中的某一个函数,却仍然得把所有的内容都链接进去。为了避免了在文件中静态库的大量重复有了共享库。这样所有的程序可以共享同一个库。

1.加载时链接 load-time linking

 

2.运行时链接 run-time linking

链接过程如下图,运行结果一样。

 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值