程序员的自由修养(3)静态链接


前言:

链接器的工作过程可以分成两步,一、空间与地址分配;二、符号解析与重定位,下面详细介绍下这两步的工作原理。

一、静态链接

1、空间与地址分配

静态链接的输入是一组目标文件(.obj,由编译器生成)和静态库(.lib,本质是目标文件的集合)。完成空间与地址分配后就可以确定所有符号的虚拟地址,核心工作如下:

  • 扫描所有的输入目标文件,获取它们的各个段的长度、属性和位置。将多个目标文件中相同类型的段合并为一个大段,计算输出文件中各个段合并后的长度与位置,并建立映射关系。
  • 将输入文件中的符号表中的所有符号定义及引用收集起来,统一放到一个全局符号表。

示例代码:

// util.cpp
#include "util.h"

int shared = 1;
int add(int a, int b) {
    return a + b;
}

// util.h
#pragma once
int add(int a, int b);

// main.cpp
#include "util.h"
extern int shared;
int main()
{
    int a = 10, b = 20;
    shared = add(a, b);
    return 0;
}

2、符号解析与重定位

使用第一步收集到的信息,读取输入文件中的段的数据、重定位信息,并进行符号解析与重定位、调整代码中的地址,这一步是链接的核心,特别是重定位的过程。

2.1、编译源文件

编译源文件

cl /Zi /Od main.cpp util.cpp  # 生成符号信息并禁用优化

分别查看util.objmain.obj对应的反汇编代码

  • util.obj段反汇编信息:
E:\VS_Workspace\Example>dumpbin /DISASM util.obj
Microsoft (R) COFF/PE Dumper Version 14.43.34808.0
Copyright (C) Microsoft Corporation.  All rights reserved.


Dump of file util.obj

File Type: COFF OBJECT

?add@@YAHHH@Z (int __cdecl add(int,int)):
  0000000000000000: 89 54 24 10        mov         dword ptr [rsp+10h],edx
  0000000000000004: 89 4C 24 08        mov         dword ptr [rsp+8],ecx
  0000000000000008: 8B 44 24 10        mov         eax,dword ptr [rsp+10h]
  000000000000000C: 8B 4C 24 08        mov         ecx,dword ptr [rsp+8]
  0000000000000010: 03 C8              add         ecx,eax
  0000000000000012: 8B C1              mov         eax,ecx
  0000000000000014: C3                 ret
  • main.obj反汇编信息:
E:\VS_Workspace\Example>dumpbin /DISASM main.obj
Microsoft (R) COFF/PE Dumper Version 14.43.34808.0
Copyright (C) Microsoft Corporation.  All rights reserved.


Dump of file main.obj

File Type: COFF OBJECT

main:
  0000000000000000: 48 83 EC 38        sub         rsp,38h
  0000000000000004: C7 44 24 24 0A 00  mov         dword ptr [rsp+24h],0Ah
                    00 00
  000000000000000C: C7 44 24 20 14 00  mov         dword ptr [rsp+20h],14h
                    00 00
  0000000000000014: 8B 54 24 20        mov         edx,dword ptr [rsp+20h]
  0000000000000018: 8B 4C 24 24        mov         ecx,dword ptr [rsp+24h]
  000000000000001C: E8 00 00 00 00     call        ?add@@YAHHH@Z
  0000000000000021: 89 05 00 00 00 00  mov         dword ptr [?shared@@3HA],eax
  0000000000000027: 33 C0              xor         eax,eax
  0000000000000029: 48 83 C4 38        add         rsp,38h
  000000000000002D: C3                 ret

通过分析main.obj对应的反汇编代码,引用的addshared地址都是一个临时的地址,重定位过程就是把这些临时地址修改成实际的虚拟地址

在这里插入图片描述

2.2、重定位表

链接器怎么知道哪些指令需要调整?这些指令的那些部分需要被调整?如何进行调整?在重定位表里面有记录记录编译时未确定地址的符号,让链接器在链接时修正。

  • main.obj重定位表(简化版):
E:\VS_Workspace\Example>dumpbin /relocations main.obj
Microsoft (R) COFF/PE Dumper Version 14.43.34808.0
Copyright (C) Microsoft Corporation.  All rights reserved.

RELOCATIONS #3
                                                Symbol    Symbol
 Offset    Type              Applied To         Index     Name
 --------  ----------------  -----------------  --------  ------
 0000001D  REL32                      00000000         9  ?add@@YAHHH@Z (int __cdecl add(int,int))
 00000023  REL32                      00000000        12  ?shared@@3HA (int shared)
  • 重定位表字段解析
字段名说明
Symbol Name需要重定位的符号(如 sharedadd)。
Offset该符号在当前 .obj 文件中的偏移地址(相对于所在节的起始位置)。
Type重定位类型,常见的有:
DIR32:直接 32 位地址修正。
REL32:相对 32 位地址修正(用于函数调用)。
Applied To该重定位条目所属的目标文件(如 main.obj)。

2.3、符号重定位

main.cpp 中,有两个外部符号需要重定位:shared(全局变量)add(函数调用),重定位的流程如下:

  • 获取符号虚拟地址:根据全局符号表,确定 sharedadd 的最终虚拟地址,空间与地址分配后每个符号的虚拟地址已经确定(虚拟地址 = 基址 + 段偏移 + 符号偏移)。
  • 修正重定位条目
    • 对于 DIR32 类型(如 shared):
      • 找到 main.objOffset 指定的位置,填入 shared 的绝对地址(如 0x00403000)。
    • 对于 REL32 类型(如 add):
      • 计算 call add 指令与 add 函数地址的相对偏移。
      • 将计算出的偏移值写入 REL32 指定的位置。
  • 生成最终可执行文件
    • 所有重定位修正完成后,生成可执行文件。

2.4、链接生成可执行文件

main.objutil.obj链接生成main.exe,链接命令如下:

E:\VS_Workspace\Example>link /OUT:main.exe /DEBUG main.obj util.obj
  • 链接后main函数反汇编代码:

在这里插入图片描述

从反汇编代码可以看到,之前未确定的地址都已重定位到符号的实际虚拟地址。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值