【软件开发底层知识修炼】九 链接器-可重定位文件与可执行文件

上几篇文章学习了Binutils辅助工具里面的几个实用的工具,那些工具对于以后的学习都是非常有帮助的,尤其是C语、C++语言的学习以及调试是非常有帮助的。点击链接查看上一篇文章:点击查看

本篇文章开始一个新的知识的学习,链接器的学习。学习完链接器的系列文章,我们将全面了解链接器的工作原理。

注意:本文讲解的并不是很详细,有些关键词例如符号、重定位、段等都没有具体说。这些比较原始的知识最好先去阅读相关书籍,进行补充。本文只是通过实验来理解链接器的作用!!!

文章目录

  • 1、可重定位文件与可执行文件
  • 2、通过代码分析,具体了解链接器的作用
  • 3、链接器的意义
  • 4、总结

1、可重定位文件与可执行文件

我们都知道,源程序在经过gcc编译器编译后,实际上是经过四个步骤—预处理,编译,汇编,链接。最终得到一个可执行程序。这个可执行程序最终将会被操作系统的加载器加载带内存中去执行。

在经过汇编之后,生成的文件是可重定位文件,然后可重定位文件经过链接器的链接,最终生成可执行文件。 今天我们就是来学习这个链接器的。

那么可重定位文件是一个什么样的文件?为什么它不能执行?

经过汇编后的文件是可重定位文件。它的文件格式与可执行文件很像(对于Linux,都是elf文件格式)。对于可重定位文件,它里面的代码与数据,都是各个文件独立的代码与数据,在一个工程中,会存在多个C文件,每个C文件都会被首先编译生成一个可重定位文件,然后经过链接器将这些可重定位文件进行链接,从而生成最终的可执行文件。

对于可重定位文件:

  • 各个段没有具体的起始地址,只要段大小信息
  • 各个标识符没有实际地址,只有在段中的偏移地址(相对地址)
  • 段和标识符的实际地址都需要链接器具体制定,这也是链接器的主要作用

对于可执行文件:

  • 各个段有自己的起始地址,这些地址就是将来要被加载到内存中的地址(虚拟内存),有了起始地址,才能说加载到内存,不然都不知道加载到哪里,何来的执行呢?这就是可执行文件与可重定位文件一个区别
  • 可执行文件中的各个符号,都有了正确的地址,以及符号被引用的地方也正确填上了符号的地址

以上内容,说的很简单,如果不懂,参考《程序员的自我修养》与《深入理解计算机系统》第7章

2、通过代码分析,具体了解链接器的作用

链接器的作用简单的说就是:

  • 符号解析
  • 重定位

下面我们以具体的程序例子来说明:

test.c

#include <stdio.h>int g_global = 0;
int g_test = 1;extern int* g_pointer;
extern void func();int main(int argc, char *argv[])
{printf("&g_global = %p\n", &g_global);printf("&g_test = %p\n", &g_test);printf("&g_pointer = %p\n", &g_pointer);printf("g_pointer = %p\n", g_pointer);printf("&func = %p\n", &func);printf("&main = %p\n", &main);func();return 0;
}

func.c

#include <stdio.h>int* g_pointer;void func()
{g_pointer = (int*)"D.T.Software";return;
}

对上述两个源程序进行编译生成两个可重定位文件:

  • gcc -c func.c -o func.o
  • gcc -c test.c -o test.o

生成了可可重定位文件func.o与test.o

  1. 我们使用上几篇文章的学习的Binutils辅助工具来查看这两个可重定位文件的符号信息:
  • nm func.o
    在这里插入图片描述

  • nm test.o
    在这里插入图片描述

可以看到,在test.o与func.o中,各个符号的地址都是0,而且有的符号还是未定义的。地址为0是因为,在没有链接之前,各个可重定位文件是独立的,他们无法加载到内存中去执行,各个符号还没有进行重定位。而又的符号未定义是因为该文件中引用了外部文件的代码或者数据。比如上述代码test.c程序中引用了func.c程序中的g_pointer变量与func()函数,那么在test.c程序中他们就是未定义的,需要将test.o与func.o链接,才能使整个程序是完整的。

  1. 还可以查看他们的段信息
  • objdump -h test.o
    在这里插入图片描述

  • objdump -h func.o
    在这里插入图片描述

可以看到,各个段的地址都是0(VMA与LMA),所以这种可重定位文件是不可执行的,它连加载地址都没有怎么执行???

最后我们将上述两个可重定位文件进行链接生成可执行文件,看看可执行文件里面是什么样子的?

  • gcc func.o test.o -o lyy

生成了可执行文件lyy

运行可执行文件:

  • ./lyy
    在这里插入图片描述
  1. 现在查看可执行文件lyy的符号信息
  • nm lyy
    在这里插入图片描述

看到画红框的地方,是我们程序中有的,左边的地址都是各个符号的地址,此时不为0了,每个符号都有自己的加载地址。其他多余不认识的符号,我们再后面的文章会进行讲解。

  1. 查看可执行文件lyy的段信息,看看它与可重定位文件有什么区别
  • objdump -h lyy

在这里插入图片描述

以上图片显示的不全!

可以看出,可执行文件的各个段,也都有了加载地址。那么他就可以加载到内存中进行执行了。

上面我们没有分析符号的引用。由于分析符号的引用需要查看反汇编代码,这里反汇编代码太长了,就不贴了。直接说原理。

实际上经过链接后,可执行文件中,对于符号的引用,已经可以将正确的符号地址填写到符号引用处(因为符号经过重定位已经有了运行时的地址,将这个地址填写到它被引用的地方即可)。当符号引用处是正确的符号地址,在运行时,引用才能够正确得到结果。

3、链接器的意义

从上面的实验,大致可以理解链接器的意义:

链接器的主要作用是将各个可重定位目标模块之间的引用部分处理好,使得各个模块之间可以正确衔接。类似于下图:
在这里插入图片描述

链接器的工作内容:

  • 将目标文件(可重定位文件)与库文件整合成最终的可执行文件

    • 合并各个目标文件的段(.text .data .bss等)以及使得符号与付哈引用之间进行一个关联—符号解析
    • 确定各个段以及各个段中符号的最终地址—重定位

4、总结

上述内容,不够系统,也不够细致,只是从实验的角度来具体看可执行文件与可重定位文件,然后理解链接器到底做了什么。前提是你已经理解了一些基础的知识。如果不理解,还需要回头去看看编译链接过程中的基础知识。

本文参考狄泰软件学院相关课程
想学习的可以加狄泰软件学院群,
群聊号码:199546072

学习探讨加个人(可以免费帮忙下载CSDN资源):
qq:1126137994
微信:liu1126137994

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/423030.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【软件开发底层知识修炼】十 链接器-main函数不是第一个被执行的函数

上一篇文章&#xff0c;大概了解了链接器的工作内容就是&#xff1a;符号解析和重定位。点击上一篇文章查看&#xff1a;点击查看。 本片文章其实还是围绕链接器来学习。只不过不是很明显&#xff0c;当你学到下一篇文章时&#xff0c;就明白了。 本篇文章来弄明白一个问题&a…

分享博文摘要图标【11/16更新】

经常写文章&#xff0c;希望给朴素的浏览界面添加一些生动的图标&#xff0c;让浏览者直接通过图片来得知这篇文章讲的是什么&#xff1f; &#xff08;效果预览...&#xff09; 博主特意搜集并上传了一些大家可能会用到的图标&#xff0c;提供16、32、48、64、128、甚至256像素…

【OS学习笔记】十二 现代处理器的结构和特点

本文是一个衔接点&#xff0c;上一篇文章以前都是学习8086实模式的知识。本文开始学习80386这种现代处理器的编程架构。由此进入保护模式的学习。点击链接查看上一篇文章&#xff1a;上一篇文章 1、现代处理器的结构和特点 1 流水线技术 处理器可以做很多事情&#xff0c;能…

前端学习(172):格式化文本

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/ html4/strict.dtd"> <html><head><meta http-equiv"content-type" content"text/html; charsetutf-8"><title>格式化</…

输出星号平行四边形

输出星号平行四边形 代码 #include <stdio.h>intmain(){inti, j, k, max; printf("Input max:"); scanf("%d", &max);for(i 1; i <max /21; i) /*1~max / 2 1 行 */{for(j max /2-i; j >0; j--) /*第i行的空白字符 */{ putchar(); }for(j…

投资理财分享

投资理财分享理财是作为工作之外的另一个技能&#xff0c;一直想去学习&#xff0c;但是由于种种原因&#xff0c;推迟&#xff0c;最近开始了自己理财课程&#xff0c;适合小白入手&#xff0c;持续更新中&#xff1b;

【OS学习笔记】十三 保护模式一:全局描述符表(GDT)

上一篇文章&#xff0c;我们大致领略了现代处理器的结构和特点。点解链接查看上一篇文章&#xff1a;现代处理器的结构和特点 本篇文章开始&#xff0c;学习保护模式下的的各种机制。什么是保护模式呢&#xff1f; 一般来说&#xff0c;操作系统负责整个计算机软硬件的的管理…

众多SEO专家集体盛赞

《SEO实战密码》将给你思路、眼界和方法的拓展之道。选一个适宜阅读的角落&#xff0c;一杯咖啡&#xff0c;一支笔&#xff0c;听Zac系统地讲SEO知识、见解和经验分享吧。我们都知道&#xff0c;你的竞争对手也会是Zac的粉丝。 葛小飞&#xff08;天真&#xff09;&#xff0c…

前端学习(173):格式化文本二

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/ html4/strict.dtd"> <html><head><meta http-equiv"content-type" content"text/html; charsetutf-8"><title>格式化</…

【OS学习笔记】十四 保护模式二:段描述符

上一篇文章初步进入保护模式的学习。首先学习了全局描述符表GDT。点击链接查看上一篇文章&#xff1a;全局描述符表 本篇文章继续学习&#xff0c;GDT中存放的条目&#xff1a;描述符&#xff0c;确切的说是段描述符。学习段描述符的作用以及段描述符的格式。 1、段描述符的格…

前端学习(174):图片热区链接

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/ html4/strict.dtd"> <html><head><meta http-equiv"content-type" content"text/html; charsetutf-8"><title>超链接——图…

软件基础思维导图

软件基础1.进程和线程的多种状态2.软件基础1.进程和线程的多种状态 2.软件基础

【OS学习笔记】十五 保护模式三:保护模式下的内存访问机制

上一篇文章学习了段描述符与段描述符各个标志位的含义&#xff1a;段描述符 本篇文章学习如何进入保护模式&#xff0c;并学习如何在保护模式下进行内存访问。 1、如何进入保护模式 假设我们已经用汇编语言将段描述符安装到GDT中&#xff08;具体的汇编代码在后面的文章中会…

前端学习(175):弹窗

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/ html4/strict.dtd"> <html><head><meta http-equiv"content-type" content"text/html; charsetutf-8"><title>超链接——J…

前端学习(176):列表元素

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/ html4/strict.dtd"> <html><head><meta http-equiv"content-type" content"text/html; charsetutf-8"><title>有序列表<…

设计模式-装饰者模式

1. 场景 很经典的一个场景 咖啡类型 espresso&#xff08;意大利咖啡&#xff09;&#xff0c;shortblack,LongBlack(美食咖啡)&#xff0c;Decaf(无因咖啡)调料 Milk ,Soy(豆浆)&#xff0c;Chocolate费用 不同的咖啡价格是不同的&#xff0c;而且有 咖啡调料的类型组合 每个咖…

【OS学习笔记】十七 保护模式五:保护模式下如何进行内存保护 与 别名段的意义与作用

上一篇文章学习了如何进入保护模式&#xff0c;以及如何在保护模式下进行内存访问。点击链接查看上一篇文章&#xff1a;进入保护模式与在保护模式下访问内存 首先说明本片文章有对应的汇编代码&#xff0c;点击链接查看&#xff1a;点击查看 本篇文章接着学习。我们已经知道…

前端学习(177):定义列表

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/ html4/strict.dtd"> <html><head><meta http-equiv"content-type" content"text/html; charsetutf-8"><title>定义列表<…

oracle 数据库工作总结思维导图

oracle 数据库工作中使用总1. oracle 数据库 工作中使用总结思维导图2 解析oracle执行计划1. oracle 数据库 工作中使用总结思维导图 2 解析oracle执行计划 执行计划树的基本规则如下&#xff1a; 执行计划将包含一个根&#xff0c;没有父&#xff08;操作&#xff09; 父&…

【软件开发底层知识修炼】十一 链接器-链接脚本

上一篇文章学习了链接器之-main函数不是第一个执行的函数:main函数不是第一个执行的函数 今天继续学习链接器&#xff0c;学习链接是如何动作的&#xff0c;从而引入链接脚本的概念。本文就学习链接脚本的概念。 1、链接脚本的作用 我们都知道可重定位文件经过链接器链接后最…