`

第4章 静态链接

 
阅读更多

静态链接

       一、空间与地址分配

       这里的“空间和地址”有两个含义:第一,在输出的可执行文件ab.o中的空间;第二,是在装载后的虚拟地址中的虚拟地址空间。

       现在的链接器的策略基本上都是:将a.o和b.o中相似段合并(如.text和.text段合并),然后再分配空间(ab文件中分配空间基本上是两个.text段加起来的大小;类似地,在虚拟地址空间中也去指定一段空间)。见P102图

实验:观察静态链接过程的“空间和地址分配”:

1. 将a.o和b.o链接成ab.o

两个源代码文件:

/*a.c*/
extern int shared;
int main(){
    int a=100;
    swap(&a, &shared);
}

 和

/*b.c*/
int shared =1 ;
void swap(int *a, int* b){
    *a^=*b^=*a^=*b;
}

$gcc -c a.c b.c ==> 使用-c参数只完成预处理、编译、汇编,(不链接),产生a.o和b.o

$ld a.o b.o -e main -o ab ==> 链接a.o和b.o,指定main()作为程序入口(默认入口是_start),指定输出ab

 

2. 观察“空间和地址分配情况”

$ readelf -S a.o  或  objdump -h a.o

$ readelf -S b.o  或  objdump -h b.o

$ readelf -S ab  或  objdump -h ab

 

       二、符号解析与重定位

        上面虽然解决了地址和空间的分配,但是对于a.o目标文件引用到b.o中“变量shared”和"函数swap"的问题还为解决。每个目标文件都可能定义一些符号,也可能引用到其他目标文件的符号,输入链接器的目标文件的符号表会组成一个“全局符号表”;在“符号解析”的过程中找到了一些在a.o中未定义、引用其他目标文件中定义的符号;而将a.o中这些未定义的符号的占位符(目标文件中某些偏移处),修改为正确的符号变量地址/函数入口地址(注:也是“虚拟地址空间中的地址”),进而产生可执行文件ab的过程,称为“指令修正/重定位”。

 

实验:符号解析和指令修正

 1. 通过查看a.o的重定位段(又称为“重定位表”),可以找到未定义的符号。这些符号必须通过ld链接从其他模块获取,否则报错:一般地,UND这种未定义的符号都是因为该目标文件中有关于它们的重定位项,所以在链接器扫描完所有的输入目标文件之后,所有这些未定义的符号都应该能够在全局符号表中找到,否则链接器就会报“符号未定义”错。

[hadoop@sam1 mydir]$ readelf -s a.o

Symbol table '.symtab' contains 10 entries:

   Num:    Value  Size Type    Bind   Vis      Ndx Name

     0: 00000000     0 NOTYPE  LOCAL  DEFAULT  UND 

     1: 00000000     0 FILE    LOCAL  DEFAULT  ABS a.c

     2: 00000000     0 SECTION LOCAL  DEFAULT    1 

     3: 00000000     0 SECTION LOCAL  DEFAULT    3 

     4: 00000000     0 SECTION LOCAL  DEFAULT    4 

     5: 00000000     0 SECTION LOCAL  DEFAULT    6 

     6: 00000000     0 SECTION LOCAL  DEFAULT    5 

     7: 00000000    39 FUNC    GLOBAL DEFAULT    1 main

     8: 00000000     0 NOTYPE  GLOBAL DEFAULT  UND shared

     9: 00000000     0 NOTYPE  GLOBAL DEFAULT  UND swap

 

2. 反汇编a.o和ab,可以看到指令修正的痕迹。

 

[hadoop@sam1 mydir]$ objdump -r a.o   ==> 查看“重定位表”(即“重定位段”),可知文件a.o中分别偏移0x15和0x21两个位置的4个字节需要“重定位”(或称“符号修正”)。

a.o:     file format elf32-i386

RELOCATION RECORDS FOR [.text]:

OFFSET   TYPE              VALUE 

00000015 R_386_32          shared          ==> 每个需要重定位之处(目标文件中某个偏移)叫“重定位入口”

00000021 R_386_PC32        swap

 

Sam: 从上面可以看到重定位表的结构:一个需要重定位的section对应有一个重定位段;是一个数组,每一个元素记录的是某个偏移处需要被重定位的变量(函数)名称。

 

[hadoop@sam1 mydir]$ objdump -d a.o

a.o:     file format elf32-i386

Disassembly of section .text:

00000000 <main>:

   0: 55                   push   %ebp

   1: 89 e5                 mov    %esp,%ebp

   3: 83 e4 f0             and    $0xfffffff0,%esp

   6: 83 ec 20             sub    $0x20,%esp

   9: c7 44 24 1c 64 00 00 movl   $0x64,0x1c(%esp)

  10: 00 

  11: c7 44 24 04 00 00 00 movl   $0x0,0x4(%esp)

  18: 00 

  19: 8d 44 24 1c           lea    0x1c(%esp),%eax

  1d: 89 04 24             mov    %eax,(%esp)

  20: e8 fc ff ff ff       call   21 <main+0x21>

  25: c9                   leave  

  26: c3                   ret  

  

[hadoop@sam1 mydir]$ objdump -d ab

ab:     file format elf32-i386

Disassembly of section .text:

08048094 <main>:

 8048094: 55                   push   %ebp

 8048095: 89 e5                 mov    %esp,%ebp

 8048097: 83 e4 f0             and    $0xfffffff0,%esp

 804809a: 83 ec 20             sub    $0x20,%esp

 804809d: c7 44 24 1c 64 00 00 movl   $0x64,0x1c(%esp)

 80480a4: 00 ==> 这里4个字节本类被修正为“shared的地址” (P109 “绝对近址32位寻址”方式,重定位入口修正)

 80480a5: c7 44 24 04 f8 90 04 movl   $0x80490f8,0x4(%esp)

 80480ac: 08 

 80480ad: 8d 44 24 1c           lea    0x1c(%esp),%eax

 80480b1: 89 04 24             mov    %eax,(%esp)

 80480b4: e8 03 00 00 00       call   80480bc <swap>  ==> 这里4个字节被修正为“swap的入口地址”(P109 “相对近址32位寻址”方式,重定位入口修正)

 80480b9: c9                   leave  

 80480ba: c3                   ret    

 80480bb: 90                   nop

 

080480bc <swap>:

 80480bc: 55                   push   %ebp

 80480bd: 89 e5                 mov    %esp,%ebp

 80480bf: 53                   push   %ebx

 80480c0: 8b 45 08             mov    0x8(%ebp),%eax

 80480c3: 8b 10                 mov    (%eax),%edx

 80480c5: 8b 45 0c             mov    0xc(%ebp),%eax

 80480c8: 8b 08                 mov    (%eax),%ecx

 80480ca: 8b 45 08             mov    0x8(%ebp),%eax

 80480cd: 8b 18                 mov    (%eax),%ebx

 80480cf: 8b 45 0c             mov    0xc(%ebp),%eax

 80480d2: 8b 00                 mov    (%eax),%eax

 80480d4: 31 c3                 xor    %eax,%ebx

 80480d6: 8b 45 08             mov    0x8(%ebp),%eax

 80480d9: 89 18                 mov    %ebx,(%eax)

 80480db: 8b 45 08             mov    0x8(%ebp),%eax

 80480de: 8b 00                 mov    (%eax),%eax

 80480e0: 31 c1                 xor    %eax,%ecx

 80480e2: 8b 45 0c             mov    0xc(%ebp),%eax

 80480e5: 89 08                 mov    %ecx,(%eax)

 80480e7: 8b 45 0c             mov    0xc(%ebp),%eax

 80480ea: 8b 00                 mov    (%eax),%eax

 80480ec: 31 c2                 xor    %eax,%edx

 80480ee: 8b 45 08             mov    0x8(%ebp),%eax

 80480f1: 89 10                 mov    %edx,(%eax)

 80480f3: 5b                   pop    %ebx

 80480f4: 5d                   pop    %ebp

 80480f5: c3                   ret  

 

注:对于32位x86平台的ELF文件的重定位入口修正的指令寻址方式只有以上两种。这两种方式 每个被修正的位置的长度都是4个字节。 

 

       三、COMMON块 (P112)

(1)编译为目标文件时,未初始化的局部静态变量就在.bss段中分配空间了;

(2)编译为目标文件时,未初始化的全局变量是弱符号,占用空间大小未知(因为其他编译单元中所占用的空间可能比本编译单元所占空间要大,最终分配空间取最大的一个),因此暂时标记为common;当链接器读取所有输入目标文件之后,才能最终确定在.bss段中分配的空间——因此,最终还是被要在.bss段中分配空间的。

 

实验:链接之前,未初始化的全局变量尚未放在.bss段中(而是被标记为COMMON)

 

C代码test.c

int printf(const char* format, ...);

int global_init_var=84;                 //.data
int global_uninit_var;                  //COM, 可能在别的文件中被定义

static int static_global_init_var=84;   //.data
static int static_global_uninit_var;    //.bss, 只在本文件中被用到

void func1(int i){
    printf("%d\n",i);
}

int main(void){
    static int static_var=85;           //.data
    static int static_var2;             //.bss, 只在本文件中被用到

    int a=1;                    //stack
    int b;                      //stack

    func1(static_var + static_var2 + a + b);
    return a;
}
$ gcc -c test.c -o test.o
$ readelf -s test.o
Symbol table '.symtab' contains 18 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND
     1: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS test.c
     2: 0000000000000000     0 SECTION LOCAL  DEFAULT    1
     3: 0000000000000000     0 SECTION LOCAL  DEFAULT    3
     4: 0000000000000000     0 SECTION LOCAL  DEFAULT    4
     5: 0000000000000004     4 OBJECT  LOCAL  DEFAULT    3 static_global_init_var
     6: 0000000000000000     0 SECTION LOCAL  DEFAULT    5
     7: 0000000000000008     4 OBJECT  LOCAL  DEFAULT    3 static_var.0
     8: 0000000000000000     4 OBJECT  LOCAL  DEFAULT    4 static_var2.1
     9: 0000000000000004     4 OBJECT  LOCAL  DEFAULT    4 static_global_uninit_var
    10: 0000000000000000     0 SECTION LOCAL  DEFAULT    6
    11: 0000000000000000     0 SECTION LOCAL  DEFAULT    8
    12: 0000000000000000     0 SECTION LOCAL  DEFAULT    9
    13: 0000000000000000     4 OBJECT  GLOBAL DEFAULT    3 global_init_var
    14: 0000000000000000    31 FUNC    GLOBAL DEFAULT    1 func1
    15: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND printf
    16: 000000000000001f    45 FUNC    GLOBAL DEFAULT    1 main
    17: 0000000000000004     4 OBJECT  GLOBAL DEFAULT  COM global_uninit_var

  

       四、C++相关问题 (略,P113~)

  

       五、静态库链接

       一种语言的开发环境往往附带自己的“语言库”(Language Library),这些库是对操作系统API的封装。如C语言标准库中的printf()函数,最终会调用Linux下的write系统调用/或Windows下的WriteConsole系统API. 

       一个静态(链接)库可以简单看作是一组目标文件经过压缩打包形成的一个文件。

       Linux中最常用的C语言静态库libc是/usr/lib/libc.a (属于glibc项目一部分,貌似要安装才行), C语言集成开发环境VC2008附带的一些C运行库则放在$VC2008/lib/下。

 

       六、链接过程控制

       $ld -verbose  ==> 查看ld默认链接脚本

实验:使用自己的ld链接脚本——将三个Section合并到一个名为"tinytext"的Section,并抛弃.comment段

 

C程序TinyHelloWorld.c

char* str = "Hello world\n";

void print()
{	//使用write的系统调用, write的调用号为4,原型为int write(int filedesc, char* buffer, int size)
	//这里的系统调用,先将参数写入寄存器,之后传入write调用
	asm(
	"movl $13, %%edx \n\t"		//str的长度
	"movl %0, %%ecx \n\t"           //缓冲区,这里的%0,指的是“r”后面的字符串地址,也就是传入到这段汇编的参数。
	"movl $0, %%ebx \n\t"		//打印到标准输出, 也就是0
	"movl $4, %%eax	\n\t"		//将系统调用号传入eax。 
	"int $0x80		\n\t"	//执行中断, 调用write函数
	::"r" (str):"edx","ecx", "ebx"	//传入的参数列表, 被重命名的寄存器列表。
	);
}

void exit()
{
	asm(
		"movl $42,%ebx \n\t "			
		"movl $1, %eax \n\t"
		"int $0x80		\n\t"
	);
}
//这里是程序的入口
void nomain()
{
	print();	
	exit();
}

 

ld链接脚本TinyHelloWorld.lds  

ENTRY(nomain)
SECTIONS
{
. = 0x00008000 + SIZEOF_HEADERS;
tinytext : { *(.text) *(.data) *(.rodata) }
/DISCARD/ : { *(.comment) }
}

 

[hadoop@sam1 test]$ gcc -c -fno-builtin TinyHelloWorld.c

-c: 预处理、编译、汇编到.o文件,不链接

-fno-builtin:关闭GCC内置函数优化

[hadoop@sam1 test]$ ld -static -T TinyHelloWorld.lds -o TinyHelloWorld TinyHelloWorld.o

-static:使用静态链接,而非动态链接

[hadoop@sam1 test]$ ls -l

-rwxrwxr-x 1 hadoop hadoop    604 Jan 21 23:38 TinyHelloWorld

-rw-rw-r-- 1 hadoop hadoop    870 Jan 21 23:37 TinyHelloWorld.c

-rw-rw-r-- 1 hadoop hadoop    132 Jan 21 23:37 TinyHelloWorld.lds

-rw-rw-r-- 1 hadoop hadoop   1008 Jan 21 23:38 TinyHelloWorld.o

[hadoop@sam1 test]$ readelf -S TinyHelloWorld
There are 5 section headers, starting at offset 0xec:
Section Headers:
  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            00000000 000000 000000 00      0   0  0
  [ 1] tinytext          PROGBITS        00008074 000074 000051 00 WAX  0   0  4
  [ 2] .shstrtab         STRTAB          00000000 0000c5 000024 00      0   0  1
  [ 3] .symtab           SYMTAB          00000000 0001b4 000080 10      4   4  4
  [ 4] .strtab           STRTAB          00000000 000234 000028 00      0   0  1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings)
  I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)
  O (extra OS processing required) o (OS specific), p (processor specific)

        七、BFD库

       BFD库(Binary File Descriptor library)是一个GNU项目,他的目标是通过一种统一的接口处理不同格式的目标文件,需要安装binutils-dev。

 

 

 

分享到:
评论

相关推荐

    《计算机操作系统(第四版)》第四章课后习题答案.docx

    计算机操作系统第四章习题答案 本资源共计13道题,涵盖计算机操作系统的多个方面,包括存储器管理、链接方式、动态分区分配、内存管理等。此资源可以帮助学习者更好地理解计算机操作系统的基本概念和原理。 1. 为...

    HTML静态网页的设计

    `&lt;h1&gt;`至`&lt;h6&gt;`定义标题,`&lt;p&gt;`创建段落,`&lt;a&gt;`创建链接,`&lt;img&gt;`插入图片,`&lt;ul&gt;`、`&lt;ol&gt;`和`&lt;li&gt;`组织列表,以及`&lt;form&gt;`和`&lt;input&gt;`处理表单数据。 CSS(Cascading Style Sheets)是负责网页外观和布局的样式...

    北大青鸟HTML第四章

    通过北大青鸟HTML第四章的学习,学生应能独立编写简单的静态网页,理解网页的构成原理,为后续的CSS和JavaScript学习打下坚实基础。在实际练习中,学生需要不断实践,以提高对HTML语法规则的理解和应用能力。

    计算机操作系统第四章作业详细版.doc

    本资源摘要信息涵盖了计算机操作系统第四章的关键内容,包括层次式存储器、程序装入方式、静态链接、装入时动态链接、运行时动态链接、动态分区分配方式和动态重定位。 层次式存储器 计算机操作系统中,存储器的...

    Python全栈数据工程师养成攻略

    资源名称:Python全栈数据工程师养成攻略 资源目录:内容提要前言第1章 写在前面第2章 学会Python第3章 获取数据第4章 存储数据第5章 静态可视化第6章 自然语言理解第7章 Web基础第8章 Web进阶第9章 动态可视化第10...

    H3CNE V7.0 培训视频教程【MP4版】【共37集】.rar

    第4章 局域网原理-2&广域网原理 第5章 IP基本原理-1 第6章 网络设备调试&交换机工作原理-1 第7章 命令行操作基础 第8章 交换机工作原理-2 第9章 vlan-1 第9章 vlan-2 第9章 VLAN间路由 第9章 WLAN&企业网...

    计算机操作系统第四章作业详细版.pdf

    本资源摘要信息来自计算机操作系统第四章作业详细版.pdf,主要讲述计算机操作系统中存储器的配置、程序装入内存的方式、静态链接和动态链接、动态分区分配方式、动态重定位等知识点。 一、存储器的配置 计算机操作...

    操作系统课件:第4章 存储器管理.ppt

    在第四章“存储器管理”中,主要讨论了程序的装入和链接,以及不同的内存分配方式。 首先,程序的装入和链接是程序执行前的两个关键步骤。编辑和编译阶段产生的是目标模块,这些模块需要经过链接才能形成可执行的...

    第四章 存储器管理第四章 存储器管理.doc

    第四章 存储器管理是计算机科学中关于操作系统的核心内容之一,主要探讨如何有效地管理和利用有限的内存资源。本章涵盖了多个关键知识点: 1. **层次式存储器配置**:层次式存储器是为了平衡速度、容量和成本之间的...

    计算机操作系统第4章.ppt

    "计算机操作系统第四章存储器管理" 计算机操作系统第四章存储器管理是计算机操作系统中最重要的一部分,它管理着计算机系统的存储资源,包括程序的装入和链接、连续分配方式、基本分页存储管理方式、基本分段存储...

    华为数通(RS)路由交换-TCPIP协议栈 培训视频.rar

    TCPIP路由技术第一卷第四章-3-链路状态协议概述avi TCPIP路由技术第一卷第四章-2距离适量协议&部分 RIP av TCPIP路由技术第一卷第四章-1路由基础avi TCPIP路由技术第一卷第三章静态路由2av TCPIP路由技术第一卷第三...

    操作系统第四章答案.docx

    第四章的内容主要讨论了存储器的层次结构、程序装入内存的方式、链接技术、动态重定位以及分区存储管理的策略。 首先,配置层次式存储器的原因在于平衡性能和成本。多层次的存储器体系包括高速缓存(Cache)、主存...

    Cisco网络工程师和网络安全课程PPT

    教程名称:Cisco网络工程师和网络安全课程PPT课程目录:【】第10章 IPv6【】第11章 广域网【】第1章 计算机网络详解v2【】第2章 TCPIP协议和网络安全【】第3章 IP地址和子网划分【】第4章 配置Cisco网络设备【】第5...

    H3CNE V7.0 培训视频.zip

    第4章 WAN基本原理 第5章 IP基本原理 第6章 TCPUDP原理 第7章 路由器,交换机和操作系统 第8章 命令行特性 第9章 网络设备的文件管理 第10章 网路设备基本调试 第11章 以太网交换机工作原理 第12章 配置VLAN...

    计算机网络原理创新教程配套PPT

    资源名称:计算机网络原理创新教程 配套PPT资源目录:【】第1章计算机网络详解2【】第2章物理层【】第3章GNS3网络模拟器【】第4章数据链路层【】第5章IP地址和子网划分【】第6章静态路由和动态路由【】第7章网络层...

    H3CNE-RS V7.0视频30讲.zip

    第4章 WAN基本原理 第5章 IP基本原理 第6章 TCPUDP原理 第7章 路由器,交换机和操作系统 第8章 命令行特性 第9章 网络设备的文件管理 第10章 网路设备基本调试 第11章 以太网交换机工作原理 第12章 配置...

    深入浅出Visual C++动态链接库(DLL)编程(pdf版+doc版)

    第四章:VC++动态链接库编程之MFC扩展 DLL 6.1概论 6.2 MFC扩展DLL导出MFC派生类 6.3 MFC扩展DLL的加载 6.4 MFC扩展DLL加载MFC扩展DLL 6.5 MFC扩展DLL导出函数和变量 6.6 MFC扩展DLL的应用 第五章:VC++动态链接库...

    第4章 存储器管理.ppt

    《第4章 存储器管理》主要探讨的是操作系统如何管理和优化计算机的内存资源,确保程序的高效执行和系统的稳定性。存储器管理涉及到以下几个关键知识点: 1. **存储分配和回收**:这是存储管理的基础,包括如何有效...

    BB平台第4、5章1

    本资源摘要信息将涵盖BB平台第4、5章的知识点,涉及地址映射、动态重定位、虚拟存储器、静态链接、对换、静态重定位、动态分区式内存管理、回收内存、存储管理方式、请求调页系统、页面置换算法、内存分配、虚拟...

Global site tag (gtag.js) - Google Analytics