计算机基础学习日志(八) 链接

计算机基础学习日志(八)链接

    • gcc编译过程
    • 目标文件
    • 符号
    • 链接器处理符号的规则
    • 链接静态库
    • 链接顺序

gcc编译过程

/* main.c */
int sum(int *a, int n);

int array[2] = {1, 2};

int main() 
{
    int val = sum(array, 2);
    return val;
}
/* sum.c */
int sum(int *a, int n)
{
    int i, s = 0;
    
    for (i = 0; i < n; i++) { 
        s += a[i];
    }
    return s;
}        

示例程序由main.c和sum.c两个源文件组成,main函数初始化一个整数数组,然后调用sun函数对数组元素求和。

gcc -c  main.c sum.c
/* 编译、汇编到目标代码,生成可重定位目标文件 */
ld -o myprog main.o sum.o 
/* 链接文件,生成可执行目标文件myprog */
./myprog
/* 运行可执行目标文件myprog */

过程如图:
计算机基础学习日志(八) 链接_第1张图片

目标文件

目标文件有三种形式:

  • 可重定位目标文件
  • 可执行目标文件
  • 共享目标文件

现代x86-64 Linux和Unix系统 使用可执行可链接格式(ELF)

  • ELF头 描述了生成该文件的系统的字的大小和字节顺序。其中包括ELF头的大小、目标文件的类型(如可重定位、可执行、共享的)、机器类型(如x86-64)、节头部表的文件偏移,以及节头部表中条目的大小和数量。

  • 部分节的具体内容:
    .text:已编译程序的机器代码。
    .rodata:只读数据。
    .data:已初始化的全局和静态C变量。
    .bss:未初始化的全局和静态C变量,在目标文件中这个节不占据实际的空间,它仅仅是一个占位符。
    .symtab:符号表
    注意:局部C变量在运行时被保存在栈中,既不出现在.data节中,也不出现在.bss节中。

符号

全局符号的强、弱
强符号:函数名和已初始化的全局变量名
弱符号:未初始化的全局变量名

链接器处理符号的规则

  • 不允许有多个同名的强符号,会链接错误
  • 如果有一个强符号和多个弱符号同名时,选择强符号
  • 若有多个弱符号定义,则任选其中一个

所以编写程序时不到万不得已不要定义全局符号。

链接静态库

/* fun1.c */
int addcnt = 0;

void addvec(int *x, int *y,
	    int *z, int n) 
{
    int i;

    addcnt++;

    for (i = 0; i < n; i++)
	z[i] = x[i] + y[i];
}
/* fun2.c */
int multcnt = 0;

void multvec(int *x, int *y, 
	     int *z, int n) 
{
    int i;

    multcnt++;

    for (i = 0; i < n; i++)
	z[i] = x[i] * y[i];
}

使用AR工具创建函数的静态库

gcc -c fun1.c fun2.c
ar rcs libvector.a fun1.o fun2.o

编写应用main2.c来使用这个库。

/* main2.c */
#include 
#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;
}

接下来需要编译和链接文件main2.o和libvector.a

gcc -c main2.c
gcc -static -o myprog2 main2.o ./libvector.a
/* 静态库放在最后 */

或者等价地使用

gcc -c main2.c
gcc -static -o myprog2 main2.o -L. -lvector

计算机基础学习日志(八) 链接_第2张图片
静态库很有用,但也容易引起迷惑,因为链接时会扔掉库里没有使用的内容。

链接顺序

假设调用关系如下:
func.o → libx.a 和liby.a 中的函数
libx.a →libz.a 中的函数
libx.a 和liby.a 之间、liby.a 和libz.a 相互独立
则以下几个命令行都是可行的:

 gcc -static -o myfunc func.o libx.a liby.a libz.a 
 gcc -static -o myfunc func.o liby.a libx.a libz.a 
 gcc -static -o myfunc func.o libx.a libz.a liby.a

假设调用关系如下:
func.o → libx.a 和liby.a 中的函数
libx.a →liby.a 同时liby.a →libx.a
则以下命令行可行:

gcc -static -o myfunc func.o libx.a liby.a libx.a

你可能感兴趣的:(计算机基础学习日志)