Eingehende Analyse dynamischer Linux-Links

Dynamische Verknüpfung ist die Standardmethode zur Verwendung von Programmen in aktuellen Betriebssystemen und sehr wichtig. Aber um dynamische Zusammenhänge zu verstehen, müssen Sie statische Zusammenhänge wirklich beherrschen. Sonst wirst du es nicht verstehen

Warum dynamische Verbindung

In einer Welt mit ausschließlich statischen Verbindungen wird der gesamte Code vor der Ausführung vom Connector finalisiert und die laufende Adresse zugewiesen. Etwas wie das.

  4004ed:	55                   	push   %rbp
  4004ee:	48 89 e5             	mov    %rsp,%rbp
  4004f1:	48 83 ec 10          	sub    $0x10,%rsp
  4004f5:	c7 45 fc 01 00 00 00 	movl   $0x1,-0x4(%rbp)
  4004fc:	48 8d 45 fc          	lea    -0x4(%rbp),%rax
  400500:	48 89 c6             	mov    %rax,%rsi
  400503:	bf 2c 10 60 00       	mov    $0x60102c,%edi 
                                               ^------变量地址 绝对地址
  400508:	e8 07 00 00 00       	callq  400514 <swap>
                                               ^------函数地址,相对地址
  40050d:	b8 00 00 00 00       	mov    $0x0,%eax
  400512:	c9                   	leaveq 
  400513:	c3                   	retq   

Bei der statischen Verbindung werden alle oben genannten absoluten Adresssprünge und relativen Adresssprünge fest codiert, und das gesamte Programm wird als hochentwickelte Maschine ausgeführt.

Der Zweck der dynamischen Verbindung besteht darin, unabhängig von der Adresse den Code von Programm A zu laden. B, C, D... Programme können alle darauf zugreifen. (Wir nennen Programm A ein gemeinsam genutztes Objekt, und die Programme B, C und D werden als ausführbare Programme bezeichnet. Und eine Sammlung gemeinsam genutzter Objekte wird als gemeinsam genutzte Bibliothek oder dynamische Bibliothek bezeichnet.) Um dieses Ziel zu erreichen. Wir müssen 2 Probleme lösen.

  1. Gemeinsam genutzte Bibliotheken und ausführbare Programme sind getrennt. Wie sie einander finden und wie die ausführbare Datei den Code und die Daten der gemeinsam genutzten Bibliothek nutzt.
  2. Wie kann eine gemeinsam genutzte Bibliothek außerdem sicherstellen, dass der Code gemeinsam genutzt und nicht geklont wird?

Erstellen Sie eine gemeinsam genutzte Bibliothek

Um das erste Problem zu lösen, erstellen wir zunächst eine gemeinsam genutzte Bibliothek und greifen auf deren ausführbares Programm zu.

//lib.h
#ifndef LIB_H
#define LIB_H
int void fun(int i);
#endif

//lib.c
//#include<stdio.h>
int fun(int i){
//      printf("this msg from lib.so %d\n",i);
        return i;
}

Verwandeln Sie den obigen Code in eine gemeinsam genutzte Bibliothek.

gcc -shared -o lib.so lib.c

Erstellen Sie eine weitere ausführbare Datei, die darauf zugreift

#include"lib.h"
#include<stdio.h>
int main(){
        printf("%d\n",fun(0));
        sleep(-1);
        return 0;
}

 gcc -o program program.c ./lib.so

Führen Sie ./program aus. Alles ist normal. Denken Sie daran, dass Sie den Pfad angeben müssen, wenn Sie Ihre eigene gemeinsam genutzte Bibliothek verwenden. Andernfalls geht gcc davon aus, dass Sie diese gemeinsam genutzten Bibliotheken unter dem Standardpfad verwenden.

Als nächstes müssen wir herausfinden, wie das Programm die gemeinsam genutzte Bibliothek findet. Denken Sie daran, dass es beim statischen Linken auch darum geht, Code zu finden und zu verschieben. Das Gleiche gilt für dynamische Verbindungen. Aber die Details sind unterschiedlich.

Bei einer statischen Verbindung können wir nach dem Bild suchen, Elf-Datei-Header->Segmenttabelle->Symboltabelle->Verlagerungstabelle, und bei einer dynamischen Verbindung müssen wir zuerst die entsprechende Tabelle aus der Segmenttabelle finden, um die dynamische Verbindungsarbeit abzuschließen. Wir sagen es auch der Reihe nach.

.dynamisch

Wenn eine Elf-Datei einen dynamischen Abschnitt hat, muss sie dynamisch verknüpft werden. Es ist wie ein Verzeichnis. Es enthält die Adressen von allem, was zur Vervollständigung der dynamischen Verbindung benötigt wird.

(base) [root@10 桌面]# readelf  -d  program

Dynamic section at offset 0xe18 contains 25 entries:
  标记        类型                         名称/值
 0x0000000000000001 (NEEDED)             共享库:[./lib.so]
 0x0000000000000001 (NEEDED)             共享库:[libc.so.6]
 0x000000000000000c (INIT)               0x400518
 0x000000000000000d (FINI)               0x400744
 0x0000000000000019 (INIT_ARRAY)         0x600e00
 0x000000000000001b (INIT_ARRAYSZ)       8 (bytes)
 0x000000000000001a (FINI_ARRAY)         0x600e08
 0x000000000000001c (FINI_ARRAYSZ)       8 (bytes)
 0x000000006ffffef5 (GNU_HASH)           0x400298
 0x0000000000000005 (STRTAB)             0x4003d8
 0x0000000000000006 (SYMTAB)             0x4002d0
 0x000000000000000a (STRSZ)              118 (bytes)
 0x000000000000000b (SYMENT)             24 (bytes)
 0x0000000000000015 (DEBUG)              0x0
 0x0000000000000003 (PLTGOT)             0x601000
 0x0000000000000002 (PLTRELSZ)           120 (bytes)
 0x0000000000000014 (PLTREL)             RELA
 0x0000000000000017 (JMPREL)             0x4004a0
 0x0000000000000007 (RELA)               0x400488
 0x0000000000000008 (RELASZ)             24 (bytes)
 0x0000000000000009 (RELAENT)            24 (bytes)
 0x000000006ffffffe (VERNEED)            0x400468
 0x000000006fffffff (VERNEEDNUM)         1
 0x000000006ffffff0 (VERSYM)             0x40044e
 0x0000000000000000 (NULL)               0x0
 (base) [root@10 桌面]# readelf  -d  lib.so

Dynamic section at offset 0xe18 contains 24 entries:
  标记        类型                         名称/值
 0x0000000000000001 (NEEDED)             共享库:[libc.so.6]
 0x000000000000000c (INIT)               0x520
 0x000000000000000d (FINI)               0x664
 0x0000000000000019 (INIT_ARRAY)         0x200df8
 0x000000000000001b (INIT_ARRAYSZ)       8 (bytes)
 0x000000000000001a (FINI_ARRAY)         0x200e00
 0x000000000000001c (FINI_ARRAYSZ)       8 (bytes)
 0x000000006ffffef5 (GNU_HASH)           0x1f0
 0x0000000000000005 (STRTAB)             0x350
 0x0000000000000006 (SYMTAB)             0x230
 0x000000000000000a (STRSZ)              167 (bytes)
 0x000000000000000b (SYMENT)             24 (bytes)
 0x0000000000000003 (PLTGOT)             0x201000
 0x0000000000000002 (PLTRELSZ)           48 (bytes)
 0x0000000000000014 (PLTREL)             RELA
 0x0000000000000017 (JMPREL)             0x4f0
 0x0000000000000007 (RELA)               0x430
 0x0000000000000008 (RELASZ)             192 (bytes)
 0x0000000000000009 (RELAENT)            24 (bytes)
 0x000000006ffffffe (VERNEED)            0x410
 0x000000006fffffff (VERNEEDNUM)         1
 0x000000006ffffff0 (VERSYM)             0x3f8
 0x000000006ffffff9 (RELACOUNT)          3
 0x0000000000000000 (NULL)               0x0

(BENÖTIGT) < – Name der abhängigen gemeinsam genutzten Bibliothek
(INIT) < – Offset des Initialisierungscodes
(FINI) < – Offset des Endcodes
(GNU_HASH) < – Offset der Symbol-Hash-Tabelle. Beschleunigen Sie den Symbolsuchprozess
(STRTAB) < – Versatz der dynamischen Symbolzeichenfolgentabelle
(SYMTAB) < – Der Versatz der Symboltabelle, der auf dynamischen Link zeigt.dynsym ist eine Teilmenge von .symtab und enthält nur dynamisch verknüpfte Symbole.
(STRSZ) < – Größe der dynamischen Link-Zeichenfolge
(SYMENT) < – Größe eines einzelnen dynamischen Link-Symbols
(RELA) < – Versatz der dynamischen Link-Verlagerungstabelle
(RELASZ) < – Größe der dynamischen Link-Verlagerungstabelle
(RELAENT) < – Einzelnes dynamisches Verbindungs-Verlagerungstabellenelement Größe

Über .dynsym können wir die dynamische Symboltabelle und die dynamische Verschiebungstabelle finden

.Dynzyme

Über die .dynmic-Tabelle können wir .dynsym finden, eine dynamisch verknüpfte Symboltabelle. Diese Symbole befinden sich auch in .symtab (statisch verknüpfte Symboltabelle).

(base) [root@10 桌面]# readelf  -sD  lib.so  <----只显示自己已经定义的符号,

Symbol table of `.gnu.hash' for image:
  Num Buc:    Value          Size   Type   Bind Vis      Ndx Name
    6   0: 0000000000201028     0 NOTYPE  GLOBAL DEFAULT  21 _edata
    7   0: 0000000000201030     0 NOTYPE  GLOBAL DEFAULT  22 _end
    8   1: 0000000000201028     0 NOTYPE  GLOBAL DEFAULT  22 __bss_start
    9   1: 0000000000000520     0 FUNC    GLOBAL DEFAULT   9 _init
   10   2: 0000000000000664     0 FUNC    GLOBAL DEFAULT  12 _fini   <---这些global函数,可以被别人使用。也叫导出函数
   11   2: 0000000000000655    12 FUNC    GLOBAL DEFAULT  11 fun
(base) [root@10 桌面]# readelf  --dyn-sym  lib.so <---显示所有定义或者引用的符号

Symbol table '.dynsym' contains 12 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _ITM_deregisterTMCloneTab
     2: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__
     3: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _Jv_RegisterClasses
     4: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _ITM_registerTMCloneTable
     5: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND __cxa_finalize@GLIBC_2.2.5 (2)   
     6: 0000000000201028     0 NOTYPE  GLOBAL DEFAULT   21 _edata
     7: 0000000000201030     0 NOTYPE  GLOBAL DEFAULT   22 _end
     8: 0000000000201028     0 NOTYPE  GLOBAL DEFAULT   22 __bss_start
     9: 0000000000000520     0 FUNC    GLOBAL DEFAULT    9 _init
    10: 0000000000000664     0 FUNC    GLOBAL DEFAULT   12 _fini
    11: 0000000000000655    12 FUNC    GLOBAL DEFAULT   11 fun

(base) [root@10 桌面]# readelf  --dyn-sym  program

Symbol table '.dynsym' contains 11 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND fun
     2: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND printf@GLIBC_2.2.5 (2)
     3: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __libc_start_main@GLIBC_2.2.5 (2)
     4: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__
     5: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND sleep@GLIBC_2.2.5 (2)  <---这些引用别人的函数。也叫导入函数
     6: 0000000000601044     0 NOTYPE  GLOBAL DEFAULT   24 _edata
     7: 0000000000601048     0 NOTYPE  GLOBAL DEFAULT   25 _end
     8: 0000000000601044     0 NOTYPE  GLOBAL DEFAULT   25 __bss_start
     9: 0000000000400518     0 FUNC    GLOBAL DEFAULT   11 _init
    10: 0000000000400744     0 FUNC    GLOBAL DEFAULT   14 _fini

Die Symboltabellenstruktur der dynamischen Verbindung ist dieselbe wie die der statischen Verbindung, daher ist die Bedeutung jeder Spalte dieselbe, daher werde ich hier nicht auf Details eingehen. Aber es gibt eine Sache zu verstehen. Es handelt sich um den oben genannten Wert.

  • Zieldatei: Der Wert ist der Offset des Segments, in dem sich das Symbol befindet.
  • Ausführbare Datei: Da sie ausgeführt werden kann, ist der Wert die virtuelle Adresse, an der sich das Symbol befindet (sie kann auch als logische Adresse bezeichnet werden).

Gemeinsam genutzte Bibliotheken können direkt ausgeführt werden

Unabhängig davon, ob es sich um eine gemeinsam genutzte Bibliothek oder eine ausführbare Datei handelt, die auf eine gemeinsam genutzte Bibliothek verweist, können sie tatsächlich direkt ausgeführt werden. Sie haben richtig gehört, tatsächlich sind gemeinsam genutzte Bibliotheken und ausführbare Dateien für das Betriebssystem dasselbe. Beispielsweise können wir Folgendes direkt ausführen

 /lib64/ld-linux-x86-64.so.2 

Sie fragen sich vielleicht, warum wir nicht unsere eigene llib.so ausführen. Natürlich wird beim Ausführen ein Fehler gemeldet, daher werden wir sie nicht ausführen. Wenn Sie es nicht glauben, lesen Sie unten.

(base) [root@10 桌面]# ./lib.so 
段错误(吐核)

Warum passiert das? Die Antwort besteht aus zwei Aspekten

  1. Gemeinsam genutzte Bibliotheken verfügen ebenso wie ausführbare Dateien über Programm-Header und werden vom System in den Speicher geladen. Wir können einen Blick auf den Programm-Header und die Speicherzuordnung werfen
(base) [root@10 桌面]# readelf -l lib.so

Elf 文件类型为 DYN (共享目标文件)
入口点 0x570
共有 7 个程序头,开始于偏移量64

程序头:
  Type           Offset             VirtAddr           PhysAddr
                 FileSiz            MemSiz              Flags  Align
  LOAD           0x0000000000000000 0x0000000000000000 0x0000000000000000
  									     ^-----逻辑地址都是0 ,操作系统看见0就会把他装载到别的地方。但是代码段和数据段内容的相对位置不会变,这个过程叫做基址重置(bebasing)。
  LOAD           0x0000000000000df8 0x0000000000200df8 0x0000000000200df8
                 0x0000000000000230 0x0000000000000238  RW     200000
........

 Section to Segment mapping:
  Segment Sections...
   00     .note.gnu.build-id .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt .init .plt .text .fini .eh_frame_hdr .eh_frame 
   01     .init_array .fini_array .jcr .data.rel.ro .dynamic .got .got.plt .bss 
   02     .dynamic 
   03     .note.gnu.build-id 
   04     .eh_frame_hdr 
   05     
   06     .init_array .fini_array .jcr .data.rel.ro .dynamic .got 

。。。。。。

[root@paas-controller-2:/home/ubuntu]$ cat /proc/19830/maps
555555554000-555555555000 r-xp 00000000 fd:00 67113449                   /home/ubuntu/wen/lib.so
	^-----------------可以看见装载到这了。对于进程来说0附近地址根本就不在自己的虚拟地址中。所以你是不能访问的。
555555754000-555555756000 rw-p 00000000 fd:00 67113449                   /home/ubuntu/wen/lib.so
	^-----------------也可以看见数据段是紧接着代码段,所以代码和数据的相对位置是不变的
7ffff7ffd000-7ffff7fff000 r-xp 00000000 00:00 0                          [vdso]
7ffffffde000-7ffffffff000 rw-p 00000000 00:00 0                          [stack]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]
[root@paas-controller-2:/home/ubuntu]$ 

  1. Vom ersten Punkt an wissen wir, dass sich die Adresse einer gemeinsam genutzten Bibliothek ändert, wenn sie ausgeführt wird (dieser Vorgang wird als Zurücksetzen der Basisadresse, Bebasing) bezeichnet, die relative Position aller darin enthaltenen Inhalte ändert sich jedoch nicht. Mit anderen Worten: Die Die durch dynamische Verknüpfung generierte Datei enthält nur relative Adressen, die verwendet werden können. . Lassen Sie uns die laufende Logik unseres Programms analysieren.
(base) [root@10 桌面]# objdump -d  lib.so
。。。。。。。。。。。
Disassembly of section .text:
//可以 readelf -l 看程序的入口点
0000000000000570 <deregister_tm_clones>:                               <------程序入口
 570:   48 8d 05 b8 0a 20 00    lea    0x200ab8(%rip),%rax        # 20102f <_edata+0x7>
 577:   48 8d 3d aa 0a 20 00    lea    0x200aaa(%rip),%rdi        # 201028 <_edata>
 57e:   55                      push   %rbp
 57f:   48 29 f8                sub    %rdi,%rax                       <------%rax=%rax-%rdi=0x4
 582:   48 89 e5                mov    %rsp,%rbp
 585:   48 83 f8 0e             cmp    $0xe,%rax
 589:   77 02                   ja     58d <deregister_tm_clones+0x1d> <----- 因为$0xe>%rax 不跳转
 58b:   5d                      pop    %rbp
 58c:   c3                      retq                                   <----运行到这返回,返回退出
 58d:   48 8b 05 44 0a 20 00    mov    0x200a44(%rip),%rax        # 200fd8 <_ITM_deregisterTMCloneTable>
 594:   48 85 c0                test   %rax,%rax
 597:   74 f2                   je     58b <deregister_tm_clones+0x1b>
 599:   5d                      pop    %rbp
 59a:   ff e0                   jmpq   *%rax
 59c:   0f 1f 40 00             nopl   0x0(%rax)
。。。。。。
#现在来看返回到哪了
[root@paas-controller-2:/home/ubuntu/wen]$ gdb ./lib.so            <----使用调试器看看
###进图调试器后
(gdb) b *deregister_tm_clones                                      <----打断点
Breakpoint 1 at 0x570
(gdb) r                                                            <----r/run 运行
(gdb) info frame                                                   <----查看当前堆栈
Stack level 0, frame at 0x7fffffffe438:
 rip = 0x555555554570 in deregister_tm_clones; saved rip 0x1       <----函数的返回地址是0x1,访问不了,所以运行报错
 Arglist at 0x7fffffffe428, args: 
 Locals at 0x7fffffffe428, Previous frame's sp is 0x7fffffffe438
 Saved registers:
  rip at 0x7fffffffe430

#我们在看看寄存器和内存证实一下是不是对的,
(gdb) i registers  
。。。。
rbp            0x0      0x0                                         <---- 上一个栈帧的栈底,因为停在了进程初始化之后运行的第一个函数。所以上一个栈帧理论上是没有的。用一个非法地址填充。
rsp            0x7fffffffe430   0x7fffffffe430                      <---- 上一个栈帧的栈顶
。。。。。
rip            0x555555554570   0x555555554570 <deregister_tm_clones>
。。。。

#因为函数停在栈帧初始化之前所以这个rsp表示上个栈的栈顶。
(gdb) x  /1xg $sp
0x7fffffffe430: 0x0000000000000001                                  <---- 非法地址
 ........

Und gemeinsam genutzte Bibliotheken, die wie /lib64/ld-linux-x86-64.so.2, ./lib64/ibc-2.17.so ausgeführt werden können, kehren nicht zu nicht vorhandenen Stapelrahmen zurück, wie die gemeinsam genutzten Bibliotheken, die wir geschrieben haben. . Am Ende führen sie alle Systemaufrufe durch und lassen zu, dass der Kernel ihren gesamten Prozess zerstört. Warum können wir Kernelfunktionen nicht wie diese aufrufen? Wenn Sie wie sie beenden möchten, gibt es zwei Methoden: (1) handschriftliche Assemblierung (direktes Auslösen eines Interrupts), (2) Aufrufen der Funktionen anderer Personen (Systemaufrufe).

(1) Wir schreiben den folgenden Assemblercode von Hand und er wird normal ausgeführt und beendet. Eine Erklärung zum Assembler finden Sie unter diesem Link.

//lib.c

int fun(int i){
	asm(
		"movl $1, %eax \n\t"  
		"movl $40, %ebx \n\t"
		"int $0x80 "
	);
    return i;
}

#编译的时候记得一定要用 -e (entry) 指定入口到哦
gcc -shared -o lib.so -e fun lib.c

Entdeckung kann perfekt enden. Dies ist jedoch zu niedrig und zu primitiv. Wenn Ressourcen geöffnet sind, müssen Sie diese schließen. Daher ist dies nicht zu empfehlen.

(2) Versuchen wir, eine von anderen geschriebene Funktion aufzurufen.

#include<stdlib.h>
int fun(int i){
        exit(0);
        return i;
}

Sie werden feststellen, dass die Kompilierung der direkten Methode fehlschlägt und Sie zur Verwendung von -fPIC aufgefordert werden.

(base) [root@10 桌面]# gcc -shared -o lib.so  -e fun --save-temps lib.c 
                                                            ^--------------可以保存中间过程,下面会分析 
/bin/ld: lib.o: relocation R_X86_64_PC32 against symbol `exit@@GLIBC_2.2.5' can not be used when making a shared object; recompile with -fPIC
/bin/ld: 最后的链结失败: 错误的值
collect2: 错误:ld 返回 1
(base) [root@10 桌面]# 

Dann versuchen wir es hinzuzufügen

(base) [root@10 桌面]# gcc -shared -o lib.so  -e fun  -fPIC  lib.c 
#可以编译通过了。但是运行起来还是报错
(base) [root@10 桌面]# ./lib.so
段错误(吐核)

Habe festgestellt, dass es immer noch nicht funktioniert. Okay, schreiben wir einen Code, der erfolgreich ausgeführt werden kann.

#include<stdlib.h>
const char ldpath[] __attribute__ ((section (".interp")))  = "/lib64/ld-linux-x86-64.so.2";
int fun(int i){
        exit(0);
        return i;

(base) [root@10 桌面]# gcc -shared -o lib.so  -e fun  -fPIC  lib.c 
(base) [root@10 桌面]# ./lib.so

Es kann endlich normal ausgeführt werden. Warum müssen Sie also -fPIC und .interp hinzufügen, um es auszuführen? Dabei geht es um den Kerngedanken der dynamischen Verbindung. Denken Sie daran, was wir oben gesagt haben, dass in dynamisch verknüpften Dateien nur relative Adressen verwendet werden können. Daher sind -fPIC und .interp Methoden, mit denen Sie unter dieser Voraussetzung Funktionen mit unbekannten Adressen verwenden können. Wir werden -fPIC und .interp in den folgenden Kapiteln ausführlich erläutern

fPIC

PIC steht für Positionsunabhängiger Code (Adressunabhängiger Code). Um dies zu verstehen, werfen wir einen Blick auf die Objektdatei, die gespeichert wurde, als die Kompilierung im vorherigen Abschnitt fehlschlug.

(base) [root@10 桌面]# readelf -r lib.o

重定位节 '.rela.text' 位于偏移量 0x220 含有 1 个条目:
  偏移量          信息           类型           符号值        符号名称 + 加数
000000000011  000b00000002 R_X86_64_PC32     0000000000000000 exit - 4
                                ^-------报错说R_X86_64_PC32类型的exit 函数在共享对象中不能用。除非你用fPIC来编译。

重定位节 '.rela.eh_frame' 位于偏移量 0x238 含有 1 个条目:
  偏移量          信息           类型           符号值        符号名称 + 加数
000000000020  000200000002 R_X86_64_PC32     0000000000000000 .text + 0

Mal sehen, was passiert, wenn wir PIC hinzufügen

(base) [root@10 桌面]# gcc -shared -o lib.so  -e fun --save-temps -fPIC lib.c  

(base) [root@10 桌面]# readelf -r lib.o

重定位节 '.rela.text' 位于偏移量 0x250 含有 1 个条目:
  偏移量          信息           类型           符号值        符号名称 + 加数
000000000011  000c00000004 R_X86_64_PLT32    0000000000000000 exit - 4
                                  ^------变成这个了

重定位节 '.rela.eh_frame' 位于偏移量 0x268 含有 1 个条目:
  偏移量          信息           类型           符号值        符号名称 + 加数
000000000020  000200000002 R_X86_64_PC32     0000000000000000 .text + 0
(base) [root@10 桌面]# 

Um zu verstehen, wofür diese Typen verwendet werden, müssen Sie verstehen, was adressunabhängiger Code bedeutet. Fügen wir lib.so etwas Code hinzu

#include<stdlib.h>
const char ldpath[] __attribute__ ((section (".interp")))  = "/lib64/ld-linux-x86-64.so.2";
static int static_var; 
extern int inter_var;//可能在共享库外。也可能是在共享库的其他目标文件内
void inner_fun(){    
}

int fun(int i){
//      printf("this msg from lib.so %d\n",i);
        static_var=1;
        inter_var=100;
        inner_fun();
        exit(0);//定义在stdlib.h里面,是extern的
        return i;
}

(base) [root@10 桌面]# gcc -shared -o lib.so  -e fun --save-temps -fPIC lib.c  

Wir haben verschiedene Arten von Variablen und Funktionen definiert, sowohl innerhalb als auch außerhalb des Moduls. Sehen wir uns an, wie sie angesprochen werden.


。。。。。。
00000000000006e0 <fun>:
 6e0:   55                      push   %rbp
 6e1:   48 89 e5                mov    %rsp,%rbp
 6e4:   48 83 ec 10             sub    $0x10,%rsp
 6e8:   89 7d fc                mov    %edi,-0x4(%rbp)
 6eb:   c7 05 3f 09 20 00 01    movl   $0x1,0x20093f(%rip)        # 201034 <static_var>
 		          ^----- x86是小端存储3f 09 20 00  其实就是0x20093f,此时rip=0x6f5 ,这条命令的意思就是给0x201034处的static_var变量赋值1
 6f2:   00 00 00 
 6f5:   48 8b 05 e4 08 20 00    mov    0x2008e4(%rip),%rax        # 200fe0 <inter_var>
                                             ^-----------------------和上面一样获得inter_var的地址
 6fc:   c7 00 64 00 00 00       movl   $0x64,(%rax)
 702:   b8 00 00 00 00          mov    $0x0,%eax
 707:   e8 e4 fe ff ff          callq  5f0 <inner_fun@plt>
 				^----------------------------------------------------跳转到 下一条指令 0x70c+0xfffffee4=0x5f0 处
               
 70c:   bf 00 00 00 00          mov    $0x0,%edi
 711:   e8 ea fe ff ff          callq  600 <exit@plt>
 				^------------------------------------------------------跳转到 下一条指令 0x716+0xfffffeea=0x600 处

。。。。
00000000000005f0 <inner_fun@plt>: <------这种plt结尾的函数是为了延迟绑定设计的。主要是为了提升动态加载的性能.延迟绑定的时候细说
 5f0:   ff 25 22 0a 20 00       jmpq   *0x200a22(%rip)        # 201018 <inner_fun@@Base+0x20093f>
                                                                    ^-------------跳转到这个地址。
 5f6:   68 00 00 00 00          pushq  $0x0
 5fb:   e9 e0 ff ff ff          jmpq   5e0 <.plt>                    <----进行动态绑定的函数

0000000000000600 <exit@plt>:
 600:   ff 25 1a 0a 20 00       jmpq   *0x200a1a(%rip)        # 201020 <exit@GLIBC_2.2.5>
 606:   68 01 00 00 00          pushq  $0x1
 60b:   e9 d0 ff ff ff          jmpq   5e0 <.plt>

 。。。。。。。

#我们在看看它们在哪个分段
。。。。。。

[20] .got              PROGBITS         0000000000200fd8  00000fd8  <------外部变量在这
       0000000000000028  0000000000000008  WA       0     0     8
[21] .got.plt          PROGBITS         0000000000201000  00001000  <------引用的函数最终跳转到这里面了。
       0000000000000030  0000000000000008  WA       0     0     8
[22] .bss              NOBITS           0000000000201030  00001030  <------- 未初始化的静态变量在这
       0000000000000008  0000000000000000  WA       0     0     4

 。。。。。


Ich habe so viel geschrieben, nur um zu erklären, dass absolute Adressen nicht in dynamischen Links verwendet werden können . Obwohl R_X86_64_PC32 eine relative Adressierung verwendet, muss dieser Typ, wenn er sich in der Zieldatei befindet, während der Kompilierung verschoben werden, dh die Adresse muss während der Kompilierung ermittelt werden. Exit ist jedoch der Code der C-Laufzeitbibliothek und befindet sich überhaupt nicht in unserer Symboltabelle. Der Compiler hat also direkt einen Fehler gemeldet. Wenn Sie selbst einen Exit desselben Typs definieren (wie meine Lösung 1), können Sie ihn vom Compiler verschieben lassen. Es ist unrealistisch, darüber nachzudenken, eine Kernfunktion einer C-Bibliothek zu schreiben und sich mit der gesamten untersten Ebene zu befassen.

Aus den oben analysierten Ergebnissen. Du wirst es herausfinden. Alle referenzierten Funktionen befinden sich in einem Abschnitt namens .got.plt. Lass uns immer weitermachen

Mit dem folgenden Befehl können Sie prüfen, ob die dynamische Linkdatei von PIC kompiliert wurde
bash readelf -d /lib64/libc-2.17.so | grep TEXTREL

.got和.got.plt

Globale Offset-Tabelle .got und .got.plt (globale Offset-Tabelle, Prozedurverknüpfungstabelle), sie sind die Übergabestation für die Interaktion zwischen unserem Programm und externen Programmen. Ihr gesamter Zugriff nach außen erfolgt darüber. Was noch übertriebener ist: Wenn wir auf die Funktionen verweisen, die wir innerhalb des gemeinsam genutzten Objekts definieren, müssen wir es durchlaufen. Wenn Sie auf externe Daten und Funktionen zugreifen, trägt ein Programm (der dynamische Linker) im Betriebssystem die Adresse des externen Objekts in diese Struktur ein. Lesen Sie also einfach die Werte in .got und .got.plt, um die Adresse des entsprechenden Objekts zu erhalten.


##你会发现.got.plt 里面已经有值了。我们来看看这些地址都指向哪
(base) [[email protected]@LIN-107F2060E3C cc]$ readelf -x 21 lib.so

“.got.plt”节的十六进制输出:
 NOTE: This section has relocations against it, but these have NOT been applied to this dump.
  0x00201000 180e2000 00000000 00000000 00000000 .. .............
  				^------64位是8字节一组,且都是小端存储0x208e18,指向.dynamic  section
  0x00201010 00000000 00000000 f6050000 00000000 ................
                                    ^------0x5f6,指向<inner_fun@plt>的第二条指令
  0x00201020 06060000 00000000 16060000 00000000 ................
                ^                   ^------0x616,指向<__cxa_finalize@plt>的第二条指令
                ^------0x606,指向<exit@plt>的第二条指令

(base) [[email protected]@LIN-107F2060E3C cc]$ readelf -x 20 lib.so

“.got”节的十六进制输出:
  0x00200fd8 00000000 00000000 00000000 00000000 ................
  0x00200fe8 00000000 00000000 00000000 00000000 ................
  0x00200ff8 00000000 00000000                   ........

(base) [[email protected]@LIN-107F2060E3C cc]$ readelf -S lib.so
。。。。。。。
  [19] .dynamic          DYNAMIC          0000000000200e18  00000e18
                                                     ^-------.got.plt第一个元素
       00000000000001c0  0000000000000010  WA       4     0     8
。。。。。。。

(base) [[email protected]@LIN-107F2060E3C cc]$ objdump  -d  lib.so

。。。。。。。

Disassembly of section .plt:

00000000000005e0 <.plt>:                                                       
 5e0:   ff 35 22 0a 20 00       pushq  0x200a22(%rip)        # 201008 <_GLOBAL_OFFSET_TABLE_+0x8>
                                                                               ^-------.got.plt第二个元素,存着模块ID,之后延迟绑定细说
 5e6:   ff 25 24 0a 20 00       jmpq   *0x200a24(%rip)        # 201010 <_GLOBAL_OFFSET_TABLE_+0x10>
                                                                                ^-------.got.plt第三个元素,存着绑定函数地址。延迟绑定细说
 5ec:   0f 1f 40 00             nopl   0x0(%rax)

00000000000005f0 <inner_fun@plt>:
 5f0:   ff 25 22 0a 20 00       jmpq   *0x200a22(%rip)        # 201018 <inner_fun@@Base+0x20093f>
 5f6:   68 00 00 00 00          pushq  $0x0
  ^-------.got.plt第四个元素
 5fb:   e9 e0 ff ff ff          jmpq   5e0 <.plt>

0000000000000600 <exit@plt>:
 600:   ff 25 1a 0a 20 00       jmpq   *0x200a1a(%rip)        # 201020 <exit@GLIBC_2.2.5>
 606:   68 01 00 00 00          pushq  $0x1
  ^-------.got.plt第五个元素
 60b:   e9 d0 ff ff ff          jmpq   5e0 <.plt>

0000000000000610 <__cxa_finalize@plt>:
 610:   ff 25 12 0a 20 00       jmpq   *0x200a12(%rip)        # 201028 <__cxa_finalize@GLIBC_2.2.5>
 616:   68 02 00 00 00          pushq  $0x2
 ^-------.got.plt第六个元素
 61b:   e9 c0 ff ff ff          jmpq   5e0 <.plt>

Nach dem oben beschriebenen Kämmen sollten Sie die Wirkung von Got spüren können. Wir haben in früheren Kapiteln oft betont, dass die Ladeadresse der gemeinsam genutzten Bibliothek ungewiss ist und der Code in der gemeinsam genutzten Bibliothek nur relative Adressen verwenden kann, während die relativen Positionen des Codesegments und des Datensegments festgelegt sind. Sie können also got verwenden, um die externe absolute Adresse zu speichern, und relative Adressierung verwenden, um got zu finden. Dies kann das Problem lösen, dass sich jeder Code in der gemeinsam genutzten Bibliothek gegenseitig findet.

Fassen wir kurz zusammen. .got speichert die Adresse externer Daten und .got.plt speichert die Adresse externer Funktionen. Die Adressen in diesen Strukturen werden von einem Programm (dynamischer Linker) im Betriebssystem gefüllt, bevor der Code ausgeführt wird.

späte Bindung

Lassen Sie uns als nächstes über die verzögerte Bindung sprechen. Aus der obigen Analyse erhalten wir einen Auftrag. Wenn in einem dynamischen Link auf eine Funktion xxxx verwiesen wird, zeigt sie auf die verzögerte Bindungsfunktion xxxx@plt. Die erste Anweisung in xxxx@plt besteht darin, zu der in .got.plt gespeicherten Adresse zu springen. Die in .got.plt gespeicherte Adresse ist die Adresse der zweiten Anweisung von xxxx@plt, der Push-Funktions-ID. Die dritte Anweisung von xxxx@plt ruft die Funktion <.plt> auf. Die Funktion <.plt> übergibt die Funktions-ID und die Modul-ID an die gebundene Funktion. Ich habe oben gesagt, dass es im Betriebssystem ein Programm gibt, das als dynamischer Linker bezeichnet wird. Es kann die Tabellen .got und .got.plt füllen. Diese Füllarbeit übernimmt die Bindefunktion. Solange wir die Funktion einmal aufrufen, wird diese gebundene Funktion ausgelöst. Ohne Aufruf des dynamischen Connectors findet keine Bindung statt. Dadurch kann die anfängliche Arbeit des dynamischen Connectors reduziert und die Leistung verbessert werden.

Wie ist der dynamische Connector gebunden?

.rel.dyn und .rel.plt

bevor das Programm läuft. Durch die dynamische Verknüpfung werden alle verwendeten gemeinsam genutzten Bibliotheken in den Programmprozess geladen. Dann holen Sie sich alle Symboltabellen. Auf diese Weise kennen Sie die Definitionsadresse jedes Symbols. Auf diese Weise wird die Adresse überall dort eingefügt, wo auf die entsprechende Funktion und die entsprechenden Daten verwiesen wird. Die referenzierten Speicherorte sind .rel.dyn und .rel.plt. Die entsprechenden in statischen Links sind .rel.data und .rel.text. Dieser Füllvorgang wird als Umlagerung bezeichnet.


(base) [[email protected]@LIN-107F2060E3C cc]$ readelf -r lib.so

重定位节 '.rela.dyn' at offset 0x4b8 contains 8 entries:
  偏移量          信息           类型           符号值        符号名称 + 加数
000000200e00  000000000008 R_X86_64_RELATIVE                    6d0
000000200e08  000000000008 R_X86_64_RELATIVE                    690
000000200e10  000000000008 R_X86_64_RELATIVE                    200e10
000000200fd8  000100000006 R_X86_64_GLOB_DAT 0000000000000000 _ITM_deregisterTMClone + 0 
	^---------------从这开始重定位地方都在.pot表中,这里面都应该被填充外部数据的地址

000000200fe0  000200000006 R_X86_64_GLOB_DAT 0000000000000000 inter_var + 0
000000200fe8  000300000006 R_X86_64_GLOB_DAT 0000000000000000 __gmon_start__ + 0
000000200ff0  000500000006 R_X86_64_GLOB_DAT 0000000000000000 _ITM_registerTMCloneTa + 0
000000200ff8  000600000006 R_X86_64_GLOB_DAT 0000000000000000 __cxa_finalize@GLIBC_2.2.5 + 0

重定位节 '.rela.plt' at offset 0x578 contains 3 entries:
  偏移量          信息           类型           符号值        符号名称 + 加数
000000201018  000b00000007 R_X86_64_JUMP_SLO 00000000000006d9 inner_fun + 0
      ^---------------从这开始重定位地方都在.pot.plt表中,这里面都应该被填充外部函数的地址
000000201020  000400000007 R_X86_64_JUMP_SLO 0000000000000000 exit@GLIBC_2.2.5 + 0
000000201028  000600000007 R_X86_64_JUMP_SLO 0000000000000000 __cxa_finalize@GLIBC_2.2.5 + 0
(base) [[email protected]@LIN-107F2060E3C cc]$ readelf -S lib.so

(base) [[email protected]@LIN-107F2060E3C cc]$ readelf  --dyn-sym  lib.so

Symbol table '.dynsym' contains 13 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 
                                                        ^------------- UND表示目标文件中引用的对象
     1: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _ITM_deregisterTMCloneTab
     2: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND inter_var
     3: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__
     4: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND exit@GLIBC_2.2.5 (2)
     5: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _ITM_registerTMCloneTable
     6: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND __cxa_finalize@GLIBC_2.2.5 (2)
     7: 0000000000000730    28 OBJECT  GLOBAL DEFAULT   13 ldpath 
                                                         ^------------- 数字表示目标文件中定义的对象
     8: 0000000000201030     0 NOTYPE  GLOBAL DEFAULT   21 _edata
     9: 0000000000201038     0 NOTYPE  GLOBAL DEFAULT   22 _end
    10: 0000000000201030     0 NOTYPE  GLOBAL DEFAULT   22 __bss_start
    11: 00000000000006d9     7 FUNC    GLOBAL DEFAULT   11 inner_fun
    12: 00000000000006e0    54 FUNC    GLOBAL DEFAULT   11 fun
(base) [[email protected]@LIN-107F2060E3C cc]$ 

.rel.dyn und .rel.plt, auch als dynamische Verschiebungstabellen bekannt. Möglicherweise bemerken Sie die Typen R_X86_64_RELATIVE, R_X86_64_GLOB_DAT, R_X86_64_JUMP_SLO. Der Zweck dieser Typen besteht darin, dem dynamischen Connector mitzuteilen, wie er die Adresse eingeben soll. R_X86_64_GLOB_DAT, R_X86_64_JUMP_SLO Typ, geben Sie ein, was auch immer die Symboladresse ist, und die Art und Weise, die Adresse von R_X86_64_RELATIVE einzugeben, wird als Zurücksetzen der Basisadresse (Bebasing) bezeichnet. Dies ist die Adresse A, auf die das Programm geladen wird, plus der Offset B des Symbols im Programm. Beispielsweise beziehen sich die ersten drei Relocation-Symbole vor allem auf die Adresse des Codesegments bzw. Datensegments im Datensegment. Denn die Adressen dieser referenzierten Codesegmente und Datensegmente ändern sich nach dem Laden, ihre relative Position ändert sich jedoch nicht. Verwenden Sie also das Zurücksetzen der Basisadresse

.interp

Wir haben oben erklärt, dass der dynamische Connector dabei hilft, die Arbeit der Referenzierung externer Objekte zu vervollständigen. Dieser dynamische Linker muss im Abschnitt .interp angegeben werden.


#看有没有.interp段
readelf -S | grep interp
#程序头也能看连接器
readelf -l /lib64/libc-2.17.so  | grep interpret
#看依赖的连接库
ldd /lib64/libc-2.17.so

dynamischer Anschluss

Die Elf-Datei wird von execve (Benutzermodus) -> sys_execve (Kernel) -> do_execve -> search_binary_handle -> Load_elf_binary ausgeführt

  • sys_execve, Funktionsparameter prüfen
  • do_execve, überprüfen Sie den Elf-Dateipfad und entnehmen Sie die eindeutige Kennung des Dateityps (die ersten paar Bytes am Anfang).
  • search_binary_handle, finden Sie die entsprechende Analysefunktion basierend auf der eindeutigen Kennung
  • Load_elf_binary, die Analysefunktion von elf. Diese Funktion ordnet Dateien und Speicher zu, initialisiert die Prozessumgebung (z. B. Registerwerte). Wenn Sie eine dynamische Verbindung herstellen möchten, müssen Sie den entsprechenden dynamischen Linker von interp finden und die Suche nach gemeinsam genutzten Bibliotheken festlegen Weg. Ändern Sie abschließend den Ort, an dem die CPU nach dem Ende von sys_execve ausgeführt wird (legen Sie den Ort fest, an dem das Programm ausgeführt werden soll).
  • Nachdem die oben genannten Schritte abgeschlossen sind, wird die statisch verknüpfte ausführbare Datei von e_entry (Programmeinstiegspunkt) ausgeführt. Die dynamisch verknüpften Dateien werden vom e_entry des dynamischen Linkers ausgeführt.

Die Suchpfadpriorität gemeinsam genutzter Bibliotheken ist LD_PRELOAD Umgebungsvariable>LD_LIBRAR_PATH Umgebungsvariable>/etc/ld.so.config Konfigurationsdatei>/>/usr/local/lib64>/usr/lib64>lib64

Der dynamische Linker wird ausgeführt, bevor unser Programm ausgeführt wird. Zu diesem Zeitpunkt ist es wie vor dem Urknall. Es gibt nichts und kann keine Funktionen oder externen Daten aufrufen. Er muss sich selbst verschieben. Laden Sie dann alle abhängigen gemeinsam genutzten Bibliotheken, verschieben Sie sie und initialisieren Sie sie.

Wiederverwendung desselben Codes

Wir haben Zehntausende von Wörtern aufgewendet, um zu erklären, wie auf externen Code verwiesen wird. Schauen wir uns nun an, ob mehrere Programme auf dieselbe externe Funktion verweisen und ob ihre Adressen identisch sind.

//建2个一样的文件分别叫做program1.c和program2.c
#include<stdio.h>
int main(){
        printf("program ");
}
#编译
 gcc -fno-builtin -o program1 program1.c 
 gcc -fno-builtin -o program2 program2.c 
 #2个文件的重定位表是一样的,这里以 program1举例子了
[root@paas-controller1:/home/ubuntu/wen]$ readelf -r program1

重定位节 '.rela.dyn' 位于偏移量 0x380 含有 1 个条目:
  偏移量          信息           类型           符号值        符号名称 + 加数
000000600ff8  000300000006 R_X86_64_GLOB_DAT 0000000000000000 __gmon_start__ + 0

重定位节 '.rela.plt' 位于偏移量 0x398 含有 3 个条目:
  偏移量          信息           类型           符号值        符号名称 + 加数
000000601018  000100000007 R_X86_64_JUMP_SLO 0000000000000000 printf@GLIBC_2.2.5 + 0
     ^---------------------可执行文件中这个值就是逻辑地址
000000601020  000200000007 R_X86_64_JUMP_SLO 0000000000000000 __libc_start_main@GLIBC_2.2.5 + 0
000000601028  000300000007 R_X86_64_JUMP_SLO 0000000000000000 __gmon_start__ + 0

[root@paas-controller1:/home/ubuntu/wen]$ gdb ./program1  <-------调试这个程序
(gdb) b *main              <-------------------------------------打断点
(gdb) r                    <-------------------------------------运行
(gdb) display /20i $pc  <----------------------------------------输出提示显示更多的汇编指令
1: x/20i $pc
=> 0x40052d <main>:     push   %rbp
   0x40052e <main+1>:   mov    %rsp,%rbp
   0x400531 <main+4>:   mov    $0x4005e0,%edi
   0x400536 <main+9>:   mov    $0x0,%eax
   0x40053b <main+14>:  callq  0x400410 <printf@plt>
   0x400540 <main+19>:  pop    %rbp
   0x400541 <main+20>:  retq   
   0x400542:    nopw   %cs:0x0(%rax,%rax,1)
   0x40054c:    nopl   0x0(%rax)
(gdb) b *main+20             <-------------------------------------我们要让延迟绑定触发不然看不到print函数地址。
(gdb) n                       <-------------------------------------接着运行
(gdb) x /1xg 0x601018
0x601018:       0x00007ffff7a604a0     <----------------------------这个就是printf的地址,内核空间

#用上面同样的方法你会发现program2中的printf 也是这个地址

Durch dynamische Verknüpfung kann nicht nur Code in gemeinsam genutzten Bibliotheken referenziert werden. Und es spart Speicher als statisches Verknüpfen. An dieser Stelle haben wir die zweite eingangs gestellte Frage beantwortet.

Initialisierung des Prozessstapels

Werfen wir einen Blick darauf, was als Erstes in einen Prozessstapel verschoben wird.

//lib.c
void fun(){
//建一个超级简单的可运行共享库
}
#编译成直接运行的共享库程序
 gcc -shared -o lib.so  -e fun --save-temps  lib.c  


#用gdb调试器调试一下
[root@paas-controller1:/home/ubuntu/wen]$ gdb ./lib.so
(gdb) b *fun         <------------------------------------------入口打一个断点                          
Breakpoint 1 at 0x655
(gdb) r              <-------------------------------------------让程序跑起来
Starting program: /home/ubuntu/wen/./lib.so 
(gdb) i f              <-------------------------------------------看一下栈帧
Stack level 0, frame at 0x7fffffffe558:
 rip = 0x555555554655 in fun; saved rip 0x1
 Arglist at 0x7fffffffe548, args: 
 Locals at 0x7fffffffe548, Previous frame's sp is 0x7fffffffe558    <---上一个栈帧的栈顶指针
 Saved registers:
  rip at 0x7fffffffe550


(gdb) x /550xg  0x7fffffffe550                                     <--------用16进制显示栈上面550个8字节内存单元
0x7fffffffe550: 0x0000000000000001      0x00007fffffffe7a7         <--------文件名地址,指向下面的那些字符串
0x7fffffffe560: 0x0000000000000000      0x00007fffffffe7bf         <---------环境变量存储的地址
                     ^-------------------------全是零表示分隔区域
0x7fffffffe570: 0x00007fffffffe7d2      0x00007fffffffe7ec
0x7fffffffe580: 0x00007fffffffe7f7      0x00007fffffffe807
0x7fffffffe590: 0x00007fffffffe815      0x00007fffffffe81f
0x7fffffffe5a0: 0x00007fffffffedbb      0x00007fffffffedcc
0x7fffffffe5b0: 0x00007fffffffedda      0x00007fffffffede8
0x7fffffffe5c0: 0x00007fffffffedf4      0x00007fffffffee10
0x7fffffffe5d0: 0x00007fffffffee33      0x00007fffffffee3e
0x7fffffffe5e0: 0x00007fffffffee53      0x00007fffffffee64
0x7fffffffe5f0: 0x00007fffffffee6d      0x00007fffffffeea0
0x7fffffffe600: 0x00007fffffffeeab      0x00007fffffffeec0
0x7fffffffe610: 0x00007fffffffeec8      0x00007fffffffeed5
0x7fffffffe620: 0x00007fffffffeef8      0x00007fffffffefb7
0x7fffffffe630: 0x00007fffffffefc5      0x0000000000000000          <---------分隔区域
0x7fffffffe640: 0x0000000000000021      0x00007ffff7ffd000          <--------- auxiliary vector 开始的地方,每一个元素由2个子元素组成。类型和值。
					^--- auxiliary vector 类型  ^--- auxiliary vector 值,下面同理
0x7fffffffe650: 0x0000000000000010      0x00000000bfebfbff
0x7fffffffe660: 0x0000000000000006      0x0000000000001000
0x7fffffffe670: 0x0000000000000011      0x0000000000000064
0x7fffffffe680: 0x0000000000000003      0x0000555555554040            <------AT_PHDR,程序头的地址。
0x7fffffffe690: 0x0000000000000004      0x0000000000000038             <------AT_PHENT,程序头中元素的大小(字节)。
0x7fffffffe6a0: 0x0000000000000005      0x0000000000000007              <------AT_PHENT,程序头中元素数量 。
0x7fffffffe6b0: 0x0000000000000007      0x0000000000000000             <------AT_BASE,动态连接器的地址
0x7fffffffe6c0: 0x0000000000000008      0x0000000000000000
0x7fffffffe6d0: 0x0000000000000009      0x0000555555554655             <------AT_ENTRY ,入口地址,我们关注的
0x7fffffffe6e0: 0x000000000000000b      0x0000000000000000
0x7fffffffe6f0: 0x000000000000000c      0x0000000000000000
0x7fffffffe700: 0x000000000000000d      0x0000000000000000
0x7fffffffe710: 0x000000000000000e      0x0000000000000000
0x7fffffffe720: 0x0000000000000017      0x0000000000000000
---Type <return> to continue, or q <return> to quit---
0x7fffffffe730: 0x0000000000000019      0x00007fffffffe789
0x7fffffffe740: 0x000000000000001a      0x0000000000000000
0x7fffffffe750: 0x000000000000001f      0x00007fffffffefe0
0x7fffffffe760: 0x000000000000000f      0x00007fffffffe799
0x7fffffffe770: 0x0000000000000000      0x0000000000000000       <--------- auxiliary vector 结束的的地方,AT_NULL
0x7fffffffe780: 0x0000000000000000      0x74477c3edae94600       <---------UNspecified 的地方,未定义的地方
                      ^---------分隔区域
0x7fffffffe790: 0x5b4dfd1f55163060      0x0034365f36387853
0x7fffffffe7a0: 0x2f00000000000000      0x7562752f656d6f68      <---------information block 的地方。环境变量在这里面。我们用字符串来显示他们
0x7fffffffe7b0: 0x2f6e65772f75746e      0x58006f732e62696c

。。。。。。。。。....。.。。。。。。。。。。。。。。。
(gdb) x /550sg  0x7fffffffe550                                 <--用字符串显示栈上面550个8字节内存单元
。。。。。。。。。....。.。。。。。。。。。。。。。。。

0x7fffffffe786: ""
0x7fffffffe787: ""
0x7fffffffe788: ""
0x7fffffffe789: "F\351\332>|Gt`0\026U\037\375M[Sx86_64"
0x7fffffffe7a0: ""
0x7fffffffe7a1: ""
0x7fffffffe7a2: ""
0x7fffffffe7a3: ""
0x7fffffffe7a4: ""
0x7fffffffe7a5: ""
0x7fffffffe7a6: ""
0x7fffffffe7a7: "/home/ubuntu/wen/lib.so"
0x7fffffffe7bf: "XDG_SESSION_ID=298"
0x7fffffffe7d2: "HOSTNAME=paas-controller1"
0x7fffffffe7ec: "TERM=xterm"
0x7fffffffe7f7: "SHELL=/bin/bash"
0x7fffffffe807: "HISTSIZE=1000"
0x7fffffffe815: "USER=root"
0x7fffffffe81f: "LS_COLORS=rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:mi=01;05;37;41:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31---Type <return> to continue, or q <return> to quit---
:*.tgz=01"...
0x7fffffffe8e7: ";31:*.arc=01;31:*.arj=01;31:*.taz=01;31:*.lha=01;31:*.lz4=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.tzo=01;31:*.t7z=01;31:*.zip=01;31:*.z=01;31:*.Z=01;31:*.dz=01;31:*.gz=01;31:*.lrz=01;"...
0x7fffffffe9af: "31:*.lz=01;31:*.lzo=01;31:*.xz=01;31:*.bz2=01;31:*.bz=01;31:*.tbz=01;31:*.tbz2=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.war=01;31:*.ear=01;31:*.sar=01;31:*.rar=01;31:*.alz=01;31:*.ace=01"...
0x7fffffffea77: ";31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.cab=01;31:*.jpg=01;35:*.jpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.ti"...
0x7fffffffeb3f: "ff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.webm=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=0"...
0x7fffffffec07: "1;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.cgm=01;35:*.emf="...
0x7fffffffeccf: "01;35:*.axv=01;35:*.anx=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=01;36:*.au=01;36:*.flac=01;36:*.mid=01;36:*.midi=01;36:*.mka=01;36:*.mp3=01;36:*.mpc=01;36:*.ogg=01;36:*.ra=01;36:*.wav=01;36:*.axa=01;36:*."...
0x7fffffffed97: "oga=01;36:*.spx=01;36:*.xspf=01;36:"
0x7fffffffedbb: "SUDO_USER=ubuntu"
0x7fffffffedcc: "SUDO_UID=1000"
0x7fffffffedda: "USERNAME=root"
0x7fffffffede8: "COLUMNS=208"
0x7fffffffedf4: "MAIL=/var/spool/mail/ubuntu"
0x7fffffffee10: "PATH=/sbin:/bin:/usr/sbin:/usr/bin"
0x7fffffffee33: "_=/bin/gdb"
0x7fffffffee3e: "PWD=/home/ubuntu/wen"
0x7fffffffee53: "LANG=zh_CN.UTF-8"
0x7fffffffee64: "LINES=31"
0x7fffffffee6d: "HISTIGNORE=*adduser*--user=*-p=*:*openssl*passwd *"
0x7fffffffeea0: "HOME=/root"
0x7fffffffeeab: "SUDO_COMMAND=/bin/su"
0x7fffffffeec0: "SHLVL=1"
0x7fffffffeec8: "LOGNAME=root"
0x7fffffffeed5: "LESSOPEN=||/usr/bin/lesspipe.sh %s"
---Type <return> to continue, or q <return> to quit---
0x7fffffffeef8: "PROMPT_COMMAND={ msg=$(history 1 | { read x y; echo $x $y | grep -v -E \"password|passwd|\\-\\-email|\\-\\-description\"; });logger -p local6.notice \"[euid=$(whoami)]\":$(who am i):[`pwd`]\"$msg\"; }"
0x7fffffffefb7: "SUDO_GID=1000"
0x7fffffffefc5: "HISTTIMEFORMAT=%F %T root "
0x7fffffffefe0: "/home/ubuntu/wen/lib.so"
0x7fffffffeff8: ""
0x7fffffffeff9: ""
0x7fffffffeffa: ""

Wir wissen es aus der obigen Analyse. Der Stapelspeicher des Prozesses wird vor der Ausführung mit Dingen gefüllt. wobei der Hilfsvektor wie ein Verzeichnis ist. Unter ihnen ist AT_ENTRY der Eintrag des Programms

SO-NAME und ldconfig

so-name ist der Befehlsmodus der gemeinsam genutzten Bibliothek
und ldconfig ist das Installationsprogramm der gemeinsam genutzten Bibliothek. Sie können einen Index für so erstellen und die Suchzeit für so verkürzen.

[参考]
https://refspecs.linuxfoundation.org/ELF/zSeries/lzsabi0_zSeries/x895.html 【Linux Foundation Process Prozessinitialisierung

https://ftp.gnu.org/old-gnu/Manuals/bfd-2.9.1/html_mono/bfd.html 【linux bfd 】
https://www.gnu.org/software/binutils/ 【linux binutils】

Guess you like

Origin blog.csdn.net/HideInTime/article/details/128132318