《深入理解计算机系统》笔记(三)链接知识【附图】

2023-12-04 12:48

本文主要是介绍《深入理解计算机系统》笔记(三)链接知识【附图】,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

欢迎查看《深入理解计算机系统》系列博客

《深入理解计算机系统》笔记(一)栈

《深入理解计算机系统》笔记(二)内存和高速缓存的原理

《深入理解计算机系统》笔记(三)链接知识(本篇)

《深入理解计算机系统》笔记(四)虚拟存储器,malloc,垃圾回收

《深入理解计算机系统》笔记(五)并发、多进程和多线程【Final】

——————————————————————————————————————–

概述

        ●该章节主要讲解的是ELF文件的结构。    

        ●静态库的概念

        ●动态库(又叫共享库)的概念,一般用于操作系统,普通应用程序作用不大。

        ●程序的加载过程。

        该书中对链接的解释也不够详细。在章节最后,作者也承认:在计算机系统文献中并没有很好的记录链接。因为链接是处在编译器、计算机体系结构和操作系统的交叉点上,他要求理解代码生成、机器语言编程、程序实例化和虚拟存储器。它恰好不落在某个通常的计算机系统领域中。

        该章节讲述Linux的X86系统,使用标准的ELF目标文件,无论是什么样的操作系统,细节可能不尽相同,但是概念是相同的。

        读完这一章节后,对“符号”的概念很是模糊。

7.1编译驱动程序

    这里再说一下编译系统。大多数编译系统提供编译驱动程序,它代表用户在需要的时候调用语言预处理、编译器、汇编器、和链接器。我自己画了一个结构图。


7.2静态链接

7.3目标文件

目标文件有三种:可重定位目标文件、可执行目标文件和共享目标文件(即动态链接库),个个系统上对目标文件的叫法不一致,Unix叫a.out,Windows NT叫PE(Portable Executable)。现代Unix使用ELF格式(EXecutable and Linkable Format 即可执行和可链接格式)。
下面详细介绍“可重定位目标文件”,下图最左边的一个图。

上图说明了,一个目标文件生成可执行文件,然后加载到内存后的映射等,三个步骤。
ELF头描述了生成该文件的系统的字的大小和字节序。ELF和节头部表之间每个部分都称为一个节(section)
.text:已编译程序的机器代码
.rodada:只读数据,比如printf语句中的格式串。
.data:已经初始化的全局C变量。局部变量在运行时保存在栈中。即不再data节也不在bss节
.bss:未初始化的全局C变量。不占据实际的空间,仅仅是一个占位符。所以未初始化变量不需要占据任何实际的磁盘空间。C++弱化BSS段。可能是没有,也可能有。
.symtab:一个符号表,它存放“在程序中定义和引用的函数和全局变量的信息”。
.rel.text:一个.text节中位置的列表。(将来重定位使用的)
.rel.data:被模块引用或定义的任何全局变量的重定位信息。
.debug:调试符号表,其内容是程序中定义的局部变量和类型定义。
.line:原始C源程序的行号和.text节中机器指令之间的映射。
.strtab:一个字符串表.

可定位目标文件的结构:让你深入了解程序段,数据段,bss段,符号表等等。

7.4可重定位目标文件——参考7.3

7.5符号和符号表

符号表是一个数组,数组里存放一个结构体。

[cpp] view plain copy
print ?
  1. typedef struct {  
  2.     int name;       /*String table offset*/  
  3.     int value;      /*Section offset, or VM address*/  
  4.     int size;       /*Object size in bytes*/  
  5.     char type:4,    /*Data, fund,section,or src file name (4 bits)*/  
  6.         binding:4;  /* Local of global(4bits)*/  
  7.     char reserved;  /*Unused*/  
  8.     char section;   /*Section header index ABS UNDEF*/  
  9. }Elf_Symbol;  
typedef struct {int name;       /*String table offset*/int value;      /*Section offset, or VM address*/int size;       /*Object size in bytes*/char type:4,    /*Data, fund,section,or src file name (4 bits)*/binding:4;  /* Local of global(4bits)*/char reserved;  /*Unused*/char section;   /*Section header index ABS UNDEF*/
}Elf_Symbol;

7.6符号解析

原则是:编译器只允许每个模块中每个本地符号只有一个定义。而且对全局的符号的解析很棘手,因为多个目标文件可能会定义相同的符号。C++和Java使用mangling手段来支持重载。

    多重定义的全局符号,请看下面的程序:

[cpp] view plain copy
print ?
  1. /*foo.c*/                      /*bar.c*/  
  2. #include <stdio.h>             int x;  
  3. void f(void);                  void f()  
  4. int x =15213;                  {  
  5. int main()                         x = 15212;  
  6. {                               }  
  7.     f();  
  8.     printf(”x=%d\n”,x);  
  9.     return 0;  
  10. }  
/*foo.c*/                      /*bar.c*/

include <stdio.h> int x;

void f(void); void f()
int x =15213; {
int main() x = 15212;
{ }
f();
printf("x=%d\n",x);
return 0;
}

大家能猜到输出的结果是15212;这是因为:bar.c中的x全局变量没有初始化,导致函数f中使用的是foo文件中的x变量。

根据Unix连接器使用下面的规则来处理多重定义的符号:

    ●规则1:不允许有多个强符号。

    ●规则2:如果有一个强符号和多个弱符号,那么选择强符号(这就是上面这道题的答案,初始化的int x=15213是强符号,而int x;是弱符号)

    ●规则3:如果有多个弱符号,那么从这些弱符号中任意选择一个(多么可怕啊)

静态库

    事先写好的一些可重定位的目标文件打包成一个单独的文件,它可以用作连接器的输入。当连接器构造一个输出的可执行文件时,它只拷贝静态库里被应用程序引用的目标模块。(稍后讲解动态链接库,也称之为共享库)。

    在Unix系统中,静态库以一种成为存档(archive)的特殊文件格式存放在磁盘中。存档文件是一组连接起来的可重定位目标文件的集合。有一个头部用来描述每个成员目标文件的大小和位置。存档文件的后缀是.a标识。是否可以这么理解.a文件的结构呢?(自己画图)


    下面用展示一个静态库连接的过程:


7.7重定位

7.8可执行文件

    参考7.3节图中央部分。可执行文件跟可重定位目标文件非常相似。只是可执行文件多了“init”和“段头部表”少了,”.rel.text“和”.rel.data“两个节。

7.9加载可执行文件。

    从7.3节图中可以发现,右部是加载后的程序结构。ELF目标文件被设计的非常容易加载到存储器。需要注意的是Unix中,程序总的代码段总是从0x0804800处开始(这就是虚拟存储器的作用)。数据段是在接下来的下一个4KB对齐的地址处。运行时堆在”读/写段”之后接下来的第一个4KB对齐的地址处,并通过malloc库往上增长。而栈总是往下生长。

7.10动态库(共享库)

    动态库是为了解决静态库的两个弊端而出现的,静态库的两个弊端:1)静态库更新后,程序要获得该静态库然后再编译。2)不同程序可能使用相同的静态库,导致很多静态库中的代码重复被加载到存储器中。

    共享库是致力于解决静态库的缺陷而出现的现代创新型产物。共享库是一块目标模块,在运行时,可以加载到任意的存储器地址,并和一个在存储其中的程序链接起来。这个过程称之为”动态链接“,是由一个叫做”动态链接器“的程序来完成的。

    共享库是以梁总方式来共享的:1)所有引用该库的程序都共享一个.so文件中的代码和数据,而不是静态库一样拷贝一份。2)在存储器中,一个共享库的.text节的一个副本可以被不同正在运行的进城共享,从而节约宝贵的存储器资源。(Unix中动态库以.so后缀表示。)

    理解动态库=共享库的概念非常重要。动态库一般是大型软件或者操作系统的最爱,因为对于普通应用来说,没有那么多库给别人使用,绝大多数都是自己用,所以静态库就够了。

7.11从应用程序中加载和链接共享库

    应用程序还可能从应用程序中加载和链接任意共享库,而无需编译时链接那些库到应用中(这个牛逼大了)!

    Windows中的更新大部分是这个技术。另外还有构建高性能web服务器。

    Linux为动态链接器提供了一系列简单的接口:

[cpp] view plain copy
print ?
  1. #include <dlfcn.h>  
  2. void *dlopen(const char *filename, int flag);//加载共享库  
  3. void *dlsym(void *handle, char *symbol);    //指向一个共享库的句柄和一个符号名字。  
  4. int dlclose(void *handle);  //下载共享库  
  5. const char *dlerror(void);  //容错  
    #include <dlfcn.h>void *dlopen(const char *filename, int flag);//加载共享库void *dlsym(void *handle, char *symbol);    //指向一个共享库的句柄和一个符号名字。int dlclose(void *handle);  //下载共享库const char *dlerror(void);  //容错

    Java定义了一个标准的调用规则,叫做Java本地接口(Java NativeInterface,JNI),它允许Java程序调用本地的C和C++函数。JNI的基本思想是将本地的C函数,如foo,编译到共享库中,如foo.so .当一个正在运行的Java程序试图调用函数foo时,Java解析程序利用dlopen接口(或者类似的接口)动态链接和加载foo.so,然后调用foo。

7.12与位置无关的代码(PIC)

7.13处理目标文件的工具  

            </div>

这篇关于《深入理解计算机系统》笔记(三)链接知识【附图】的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/453509

相关文章

一文带你理解Python中import机制与importlib的妙用

《一文带你理解Python中import机制与importlib的妙用》在Python编程的世界里,import语句是开发者最常用的工具之一,它就像一把钥匙,打开了通往各种功能和库的大门,下面就跟随小... 目录一、python import机制概述1.1 import语句的基本用法1.2 模块缓存机制1.

深入理解C语言的void*

《深入理解C语言的void*》本文主要介绍了C语言的void*,包括它的任意性、编译器对void*的类型检查以及需要显式类型转换的规则,具有一定的参考价值,感兴趣的可以了解一下... 目录一、void* 的类型任意性二、编译器对 void* 的类型检查三、需要显式类型转换占用的字节四、总结一、void* 的

深入理解Redis大key的危害及解决方案

《深入理解Redis大key的危害及解决方案》本文主要介绍了深入理解Redis大key的危害及解决方案,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着... 目录一、背景二、什么是大key三、大key评价标准四、大key 产生的原因与场景五、大key影响与危

深入理解C++ 空类大小

《深入理解C++空类大小》本文主要介绍了C++空类大小,规定空类大小为1字节,主要是为了保证对象的唯一性和可区分性,满足数组元素地址连续的要求,下面就来了解一下... 目录1. 保证对象的唯一性和可区分性2. 满足数组元素地址连续的要求3. 与C++的对象模型和内存管理机制相适配查看类对象内存在C++中,规

Java架构师知识体认识

源码分析 常用设计模式 Proxy代理模式Factory工厂模式Singleton单例模式Delegate委派模式Strategy策略模式Prototype原型模式Template模板模式 Spring5 beans 接口实例化代理Bean操作 Context Ioc容器设计原理及高级特性Aop设计原理Factorybean与Beanfactory Transaction 声明式事物

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

认识、理解、分类——acm之搜索

普通搜索方法有两种:1、广度优先搜索;2、深度优先搜索; 更多搜索方法: 3、双向广度优先搜索; 4、启发式搜索(包括A*算法等); 搜索通常会用到的知识点:状态压缩(位压缩,利用hash思想压缩)。

深入探索协同过滤:从原理到推荐模块案例

文章目录 前言一、协同过滤1. 基于用户的协同过滤(UserCF)2. 基于物品的协同过滤(ItemCF)3. 相似度计算方法 二、相似度计算方法1. 欧氏距离2. 皮尔逊相关系数3. 杰卡德相似系数4. 余弦相似度 三、推荐模块案例1.基于文章的协同过滤推荐功能2.基于用户的协同过滤推荐功能 前言     在信息过载的时代,推荐系统成为连接用户与内容的桥梁。本文聚焦于

sqlite3 相关知识

WAL 模式 VS 回滚模式 特性WAL 模式回滚模式(Rollback Journal)定义使用写前日志来记录变更。使用回滚日志来记录事务的所有修改。特点更高的并发性和性能;支持多读者和单写者。支持安全的事务回滚,但并发性较低。性能写入性能更好,尤其是读多写少的场景。写操作会造成较大的性能开销,尤其是在事务开始时。写入流程数据首先写入 WAL 文件,然后才从 WAL 刷新到主数据库。数据在开始

安卓链接正常显示,ios#符被转义%23导致链接访问404

原因分析: url中含有特殊字符 中文未编码 都有可能导致URL转换失败,所以需要对url编码处理  如下: guard let allowUrl = webUrl.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) else {return} 后面发现当url中有#号时,会被误伤转义为%23,导致链接无法访问