首页 > 其他分享 >链接-从链接到目标文件

链接-从链接到目标文件

时间:2022-12-12 22:23:24浏览次数:41  
标签:文件 00 mov 目标 0000000000000000 虚拟地址 链接

读 <<程序员的自我修改--链接,装载与库>> 和 <<深入理解java虚拟机>>

前阵子复习了一下 final , 然后发现 final 有一个知识点和 JMM 有关 ,然后又想起了 JVM 相关的知识有点模糊 ,然后我又想起了之前看过一部分的一本书 . <<程序员的自我修改>>
img

这两本书陷入了我思考 , 就 jvm 这一部分的内容和 <<程序员的自我修改>> 的东西是不是有什么共通性 , 还是说这根本就是两种东西 , 那么是不同的东西 , 那么 java 出现的动机又是什么 ?

程序的生成过程

多个模块链接形成一个可执行文件,才可以执行
一个典型程序的转换处理过程

1297993-20191125170650044-290363345.png

可以看到我们即将学的链接在最后的一个步骤 ,

  • 前面 : 汇编生成的 可重定位目标程序
  • 后面 : 链接生成的可执行目标程序(例如 window 下的 .exe文件)

下面解释预处理,编译,汇编,链接的几个重要过程。

预处理

1297993-20191125171438711-311734481.png

1297993-20191125171652363-586854684.png

编译

1297993-20191125171746850-721996807.png

汇编

1297993-20191125171829712-685084201.png

链接

1297993-20191125171923297-1211672660.png

链接操作的步骤

1297993-20191125172119139-840015843.png

其中步骤2中的合并可以说是归类代码的数据类型,代码和数据分开,最后重写写入符号对应的物理地址。 也就是说链接完成后,物理地址已经是固定了。

需要说明的是经过链接后的文件实际已经是“0101001”的二进制序列了,只是图为了让我们更加好理解,依然用汇编语言来表示.

第1,2,3步可以统称为合并,第4步为重定位,实际可以总结为两个操作。
需要注意的是第四步中的指令中填入新地址,这个地址指的是虚拟地址

链接的本质

1297993-20191125173300747-1172364918.png

链接的本质就是合并相同的“节”,例如 : .text 节, .data节,.bss 节,这样形成的一个文件,在liunx 中称之为 EIF 文件,可以说 EIF 文件就是打包好的文件,可以被引入其他的模块中(有点像java 中的 jar ,当需要引入其他模块时,就引入jar包就可以了)



目标文件

目标文件是编译器和汇编器最终生成的产物 , 那么我们先来看一下这种产物到底是长什么样的

目标文件的格式

目标的格式分为 :
1297993-20191128132910725-1821987377.png

我们链接这本书主要讲的是 linux平台下的 ELF

三类目标文件

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

1297993-20191128122016418-1446256025.png

window中

  • .exe 是可执行目标文件
  • .dll 共享目标文件

ELF 文件表示的两种视图

两种视图代表的是一个东西的不同的表现形式 . (链接视图和执行视图)

1297993-20191128131927869-1050211866.png

我们先来看一下链接视图

1297993-20191128133505905-1915023214.png

链接视图表示的是**可被链接(合并)生成可执行文件或共享目标文件 **

可重定位目标文件格式

趁着上面讲到的链接视图 ,我们接着介绍该视图所对应的目标文件--可重定位目标文件

可以说由三部分组成 :

  • ELF header
  • 各种节
  • section header table

1297993-20191128134412256-1572298592.png

可重定位目标文件--节的信息

这里简单介绍 

bss节,就像是java中的

	private	String str;

它没有实际赋值,只是一个占位符,没有在这个节里的(例子中的 str)分配磁盘空间 。

1297993-20191128134726006-1503349706.png

剩下的节含义

1297993-20191128135019185-297307043.png

这些需要注意的是每个节都有重要的作用 ,可重定位目标文件中, 特有的重定向节 : .rel.txt , .rel.data ,这两个节非常重要 ,后面我们在重定向的时候会学东西到

可执行目标文件格式

可执行目标文件是可以被载入到内存中去执行的文件,它主要与上面的可重定位文件有什么不同呢?

1297993-20191128144051965-887682799.png

可执行文件是需要载入到内存中某一个进程中去的,某个进程的虚拟空间有各个段,

1297993-20191128153629398-915316170.png

程序头表描述了该文件和虚拟地址的映射 (大白话就是我这个文件的哪个节应该映射到哪个虚拟地址)

关于一些重要节和table 的介绍我们留到下一篇文章

目标文件转载到进程的虚拟空间

下图描述的是可执行文件存储映射到某个进程的虚拟空间中,该映射过程主要靠的是ELF头(即程序头表),简单点表述就是各个节映射到进程的指定区域。需要注意的是虚存空间(虚拟地址)是每个进程自己编定的,不是内存的物理地址,这里涉及到我们后面讲到的页表,每个进程它的虚拟地址与真实的内存物理地址有个映射表叫页表,存储在每个进程中。

然后操作系统只会去判断 ,这个页的内容有没有被加载, 要是没有被加载 ,那么就将页对应的虚拟地址的内容加载到页中 (这不就是缺页的处理过程吗)

1297993-20191128120636841-473906428.png

我们可以看到代码段的地址总是从0x08048000 (虚拟地址)开始的,是只读代码段的首地址。下面看一下一个例子。

1297993-20191128121603622-2013154924.png

上面一段代码,地址从0开始的是没链接前的,而0804*开头则是已经链接过的。

通过例子查看目标文件内容

汇编生成文件

/* simpleSeciont.c */
int printf( const char* format, ...);
//#include <stdio.h>

// 这两个变量是全局变量 
int global_init_var = 84;
int global_uninit_var;
 
 
void func(int i)
{
  printf("hello %d\n", i);
}
 
int main(void)
{
    //这两个是静态变量
  static int static_var = 85;
  static int static_var2;
    // 这两个是局部变量  
  int a = 1;
  int b;
  func1(static_var + static_var2 + a + b);
  return a;
}
gcc -c SimpleSection.c

然后我们得到了

[root@k8s-master c_compile_test]# ls
SimpleSection.c  SimpleSection.o

我用了 winhex 这个工具看了 SimpleSection.o 这个文件的文件内容, 如图所示 :

img

图片一

打印各个段的信息

[root@k8s-master c_compile_test]# objdump -h SimpleSection.o

SimpleSection.o:     文件格式 elf64-x86-64

节:
Idx Name          Size      VMA               LMA               File off  Algn
  0 .text         00000059  0000000000000000  0000000000000000  00000040  2**0
                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
  1 .data         00000008  0000000000000000  0000000000000000  0000009c  2**2
                  CONTENTS, ALLOC, LOAD, DATA
  2 .bss          00000004  0000000000000000  0000000000000000  000000a4  2**2
                  ALLOC
  3 .rodata       0000000a  0000000000000000  0000000000000000  000000a4  2**0
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  4 .comment      0000002e  0000000000000000  0000000000000000  000000ae  2**0
                  CONTENTS, READONLY
  5 .note.GNU-stack 00000000  0000000000000000  0000000000000000  000000dc  2**0
                  CONTENTS, READONLY
  6 .eh_frame     00000058  0000000000000000  0000000000000000  000000e0  2**3
                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, DATA

VMA虚拟内存区域是空的 ,因为这是可重定向目标文件 ,所以是空的, 也就是说目前还是半成品, 是不会分配虚拟地址的, 只有经过链接以后才会生成虚拟地址空间

查看每个段的大小

[root@k8s-master c_compile_test]# size SimpleSection.o
   text    data     bss     dec     hex filename
    187       8       4     199      c7 SimpleSection.o

把各段内容用 16进制打印出来 , -d 则是反汇编

[root@k8s-master c_compile_test]# objdump -s -d  SimpleSection.o

SimpleSection.o:     文件格式 elf64-x86-64

Contents of section .text:
 0000 554889e5 4883ec10 897dfc8b 45fc89c6  UH..H....}..E...
 0010 bf000000 00b80000 0000e800 000000c9  ................
 0020 c3554889 e54883ec 10c745fc 01000000  .UH..H....E.....
 0030 8b150000 00008b05 00000000 01c28b45  ...............E
 0040 fc01c28b 45f801d0 89c7b800 000000e8  ....E...........
 0050 00000000 8b45fcc9 c3                 .....E...
Contents of section .data:
 0000 54000000 55000000                    T...U...
Contents of section .rodata:
 0000 68656c6c 6f202564 0a00               hello %d..
Contents of section .comment:
 0000 00474343 3a202847 4e552920 342e382e  .GCC: (GNU) 4.8.
 0010 35203230 31353036 32332028 52656420  5 20150623 (Red
 0020 48617420 342e382e 352d3434 2900      Hat 4.8.5-44).
Contents of section .eh_frame:
 0000 14000000 00000000 017a5200 01781001  .........zR..x..
 0010 1b0c0708 90010000 1c000000 1c000000  ................
 0020 00000000 21000000 00410e10 8602430d  ....!....A....C.
 0030 065c0c07 08000000 1c000000 3c000000  .\..........<...
 0040 00000000 38000000 00410e10 8602430d  ....8....A....C.
 0050 06730c07 08000000                    .s......

Disassembly of section .text:

0000000000000000 <func>:
   0:   55                      push   %rbp
   1:   48 89 e5                mov    %rsp,%rbp
   4:   48 83 ec 10             sub    $0x10,%rsp
   8:   89 7d fc                mov    %edi,-0x4(%rbp)
   b:   8b 45 fc                mov    -0x4(%rbp),%eax
   e:   89 c6                   mov    %eax,%esi
  10:   bf 00 00 00 00          mov    $0x0,%edi
  15:   b8 00 00 00 00          mov    $0x0,%eax
  1a:   e8 00 00 00 00          callq  1f <func+0x1f>
  1f:   c9                      leaveq
  20:   c3                      retq

0000000000000021 <main>:
  21:   55                      push   %rbp
  22:   48 89 e5                mov    %rsp,%rbp
  25:   48 83 ec 10             sub    $0x10,%rsp
  29:   c7 45 fc 01 00 00 00    movl   $0x1,-0x4(%rbp)
  30:   8b 15 00 00 00 00       mov    0x0(%rip),%edx        # 36 <main+0x15>
  36:   8b 05 00 00 00 00       mov    0x0(%rip),%eax        # 3c <main+0x1b>
  3c:   01 c2                   add    %eax,%edx
  3e:   8b 45 fc                mov    -0x4(%rbp),%eax
  41:   01 c2                   add    %eax,%edx
  43:   8b 45 f8                mov    -0x8(%rbp),%eax
  46:   01 d0                   add    %edx,%eax
  48:   89 c7                   mov    %eax,%edi
  4a:   b8 00 00 00 00          mov    $0x0,%eax
  4f:   e8 00 00 00 00          callq  54 <main+0x33>
  54:   8b 45 fc                mov    -0x4(%rbp),%eax
  57:   c9                      leaveq
  58:   c3                      retq

其中 .data段 8个字节,存放着是我们变量 static int static_var = 85int global_init_var = 84 , 然后我们对比这前面的 , .data段的 offset9c , 就是这里

img

图片二

链接以后

链接上面例子的文件
img

使用打印,打印出关于ELF的消息
img

其他

每次生成可执行目标文件的虚拟地址会不会不同 ??

我们做一下实验就知道了, 上面的地址是 :

img

然后我们删除以后, 在链接生成一个新的可执行目标文件,并查看其虚拟地址

img

我们看这两个需要加载进虚拟空间的内存的虚拟地址 , 前后两次是一样的

参考

  • <<程序员的自我修改--链接,装载与库>> 书

标签:文件,00,mov,目标,0000000000000000,虚拟地址,链接
From: https://www.cnblogs.com/Benjious/p/16977281.html

相关文章