Análisis en profundidad de los enlaces dinámicos de Linux.

Los enlaces dinámicos son la forma predeterminada de utilizar programas en los sistemas operativos actuales y son muy importantes. Pero para comprender las conexiones dinámicas, es necesario dominar verdaderamente las conexiones estáticas. De lo contrario no lo entenderás

Por qué conexión dinámica

En un mundo con solo conexiones estáticas, todo el código será finalizado por el conector antes de ejecutarse y se asignará la dirección de ejecución. Algo como esto.

  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   

La conexión estática consiste en codificar todos los saltos de direcciones absolutas y relativos anteriores, y todo el programa se ejecuta como una máquina sofisticada.

El propósito de la conexión dinámica es no importar en qué dirección se cargue el código del programa A. Los programas B, C, D... todos pueden acceder a él. (Llamamos al programa A un objeto compartido, y los programas B, C y D se llaman programas ejecutables. Y una colección de objetos compartidos se llama biblioteca compartida o biblioteca dinámica). Para lograr este objetivo. Tenemos que resolver 2 problemas.

  1. Las bibliotecas compartidas y los programas ejecutables están separados. Cómo se encuentran y cómo el ejecutable utiliza el código y los datos de la biblioteca compartida.
  2. Además, ¿cómo puede una biblioteca compartida garantizar que el código se comparta en lugar de clonarse?

Crear una biblioteca compartida

Para resolver el primer problema, primero creemos una biblioteca compartida y accedamos a su programa ejecutable.

//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;
}

Convierta el código anterior en una biblioteca compartida.

gcc -shared -o lib.so lib.c

Crea otro archivo ejecutable que acceda a él.

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

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

Ejecute el programa . todo es normal. Recuerde que si usa su propia biblioteca compartida, debe especificar la ruta; de lo contrario, gcc pensará que está usando esas bibliotecas compartidas en la ruta predeterminada.

A continuación tenemos que descubrir cómo el programa encuentra la biblioteca compartida. Recuerde que los enlaces estáticos también tienen un proceso de búsqueda de código y reubicación. Lo mismo ocurre con las conexiones dinámicas. Pero los detalles son diferentes.

En la conexión estática, podemos buscar de acuerdo con la imagen, encabezado del archivo elf->tabla de segmentos->tabla de símbolos->tabla de reubicación, y la conexión dinámica primero debe encontrar la tabla correspondiente en la tabla de segmentos para completar el trabajo de conexión dinámica. También lo decimos en orden.

.dinmico

Si un archivo elf tiene una sección dinámica, entonces debe vincularse dinámicamente. Es como un directorio. Contiene las direcciones de todo lo necesario para completar la conexión dinámica.

(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

(NECESARIO) <—Nombre de biblioteca compartida dependiente
(INIT) <—Desplazamiento del código de inicialización
(FINI) <—Desplazamiento del código final
(GNU_HASH) <—Desplazamiento de la tabla hash de símbolos. Acelere el proceso de búsqueda de símbolos
(STRTAB) <—Desplazamiento de la tabla de cadenas de símbolos dinámicos
(SYMTAB) <—El desplazamiento de la tabla de símbolos que apunta al enlace dinámico.dynsym es un subconjunto de .symtab y solo contiene símbolos vinculados dinámicamente.
(STRSZ) <—Tamaño de cadena de enlace dinámico
(SYMENT) <—Tamaño de símbolo de enlace dinámico único
(RELA) <—Desplazamiento de tabla de reubicación de enlace dinámico
(RELASZ) <—Tamaño de tabla de reubicación de enlace
dinámico (RELAENT) <—Elemento de tabla de reubicación de conexión dinámica única tamaño

A través de .dynsym podemos encontrar la tabla de símbolos dinámicos y la tabla de reubicación dinámica.

.dynzima

A través de la tabla .dynmic podemos encontrar .dynsym, que es una tabla de símbolos vinculada dinámicamente. Estos símbolos también están en .symtab (tabla de símbolos vinculada estáticamente).

(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

La estructura de la tabla de símbolos de la conexión dinámica es la misma que la de la conexión estática, por lo que el significado de cada columna es el mismo, por lo que no entraré en detalles aquí. Pero hay una cosa que entender. Es el valor de arriba.

  • Archivo de destino: el valor es el desplazamiento del segmento donde se encuentra el símbolo.
  • Archivo ejecutable: debido a que se puede ejecutar, el valor es la dirección virtual donde se encuentra el símbolo (también se puede llamar dirección lógica)

Las bibliotecas compartidas se pueden ejecutar directamente

Ya sea una biblioteca compartida o un archivo ejecutable que hace referencia a una biblioteca compartida, en realidad se pueden ejecutar directamente. Me escuchaste bien, de hecho, las bibliotecas compartidas y los archivos ejecutables son los mismos para el sistema operativo. Por ejemplo, podemos ejecutar directamente lo siguiente para

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

Quizás se pregunte por qué no ejecutamos nuestro propio llib.so. Por supuesto, se informará un error al ejecutarlo, por lo que no lo ejecutaremos. Si no lo cree, lea a continuación.

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

¿Por qué sucede esto? La respuesta es 2 aspectos.

  1. Las bibliotecas compartidas, como los archivos ejecutables, también tienen encabezados de programa y el sistema las cargará en la memoria. Podemos echar un vistazo al encabezado del programa y al mapa de memoria.
(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. Desde el primer punto, sabemos que la dirección de una biblioteca compartida cambiará cuando se ejecute (este proceso se llama restablecimiento de la dirección base, bebasing), pero la posición relativa de todos los contenidos en ella no cambiará. El archivo generado mediante enlaces dinámicos solo contiene direcciones relativas que se pueden utilizar. . Analicemos la lógica de ejecución de nuestro programa.
(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                                  <---- 非法地址
 ........

Y las bibliotecas compartidas que pueden ejecutarse como /lib64/ld-linux-x86-64.so.2, ./lib64/ibc-2.17.so no volverán a marcos de pila inexistentes como las bibliotecas compartidas que escribimos. Al final, todos hacen llamadas al sistema y dejan que el kernel destruya todo su proceso. Entonces, ¿por qué no podemos llamar funciones del kernel como estas? Si desea salir como ellos, existen dos métodos: (1) ensamblaje escrito a mano (activando directamente una interrupción), (2) llamando a funciones de otras personas (llamadas al sistema).

(1) Escribimos a mano el siguiente código ensamblador y se ejecutará normalmente y saldrá. Para obtener una explicación del ensamblador, consulte este enlace.

//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

El descubrimiento puede terminar perfectamente. Pero esto es de nivel demasiado bajo y demasiado primitivo. Si tienes recursos abiertos, tienes que cerrarlos. Entonces esto no es recomendable.

(2) Intentemos llamar a una función escrita por otros.

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

Descubrirá que el método directo no se compila y le solicita que utilice -fPIC.

(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 桌面]# 

Entonces intentemos agregarlo.

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

Descubrí que todavía no funciona. Bien, escribamos un código que pueda ejecutarse correctamente.

#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

Finalmente puede ejecutarse normalmente, entonces, ¿por qué necesita agregar -fPIC y .interp para ejecutarlo? Esto implica la idea central de la conexión dinámica. Recuerde lo que dijimos anteriormente de que solo se pueden usar direcciones relativas en archivos vinculados dinámicamente. Entonces -fPIC y .interp son métodos que le permiten usar funciones con direcciones desconocidas bajo esta premisa. Explicaremos -fPIC y .interp en detalle en los siguientes capítulos.

fPIC

PIC significa código independiente de la posición (código independiente de la dirección). Para comprender esto, echemos un vistazo al archivo objeto guardado cuando la compilación falló en la sección anterior.

(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

Veamos qué pasa si agregamos PIC

(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 桌面]# 

Para comprender para qué se utilizan estos tipos, debe comprender qué significa el código independiente de la dirección. Agreguemos algo de código a lib.so

#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  

Hemos definido varios tipos de variables y funciones, tanto dentro del módulo como fuera del módulo, veamos cómo se abordan.


。。。。。。
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

 。。。。。


He escrito mucho sólo para explicar que las direcciones absolutas no se pueden utilizar en enlaces dinámicos . Aunque R_X86_64_PC32 usa direccionamiento relativo, si este tipo está en el archivo de destino, debe reubicarse durante la compilación, es decir, la dirección debe determinarse durante la compilación. Pero la salida es el código de la biblioteca de ejecución de C y no está en nuestra tabla de símbolos en absoluto. Entonces el compilador informó un error directamente. Si usted mismo define una salida del mismo tipo (como mi solución 1), puede dejar que el compilador la reubique. No es realista pensar en escribir una función central de una biblioteca C y ocuparse de toda la capa inferior.

De los resultados analizados anteriormente. Tu lo descubrirás. Todas las funciones a las que se hace referencia se encuentran en una sección llamada .got.plt. Sigamos y sigamos

Puede utilizar el siguiente comando para ver si el archivo de enlace dinámico está compilado por PIC
bash readelf -d /lib64/libc-2.17.so | grep TEXTREL

.got和.got.plt

Tabla de compensación global .got y .got.plt (tabla de compensación global, tabla de vinculación de procedimientos), son la estación de transferencia para la interacción entre nuestro programa y los programas externos. Todo tu acceso al exterior pasa por él, lo que es aún más exagerado es que cuando hacemos referencia a las funciones que definimos dentro del objeto compartido, tenemos que pasar por él. Cuando accede a datos y funciones externos, un programa (el enlazador dinámico) en el sistema operativo completa la dirección del objeto externo en esta estructura. Así que simplemente lea los valores en .got y .got.plt para obtener la dirección del objeto correspondiente.


##你会发现.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>

Después del peinado anterior, deberías poder sentir el efecto de got. Hemos enfatizado muchas veces en capítulos anteriores que la dirección de carga de la biblioteca compartida es incierta: el código en la biblioteca compartida solo puede usar direcciones relativas, mientras que las posiciones relativas del segmento de código y el segmento de datos son fijas. Por lo tanto, puede usar got para almacenar la dirección absoluta externa y usar direcciones relativas para encontrar got. Esto puede resolver el problema de que cada código en la biblioteca compartida se encuentre entre sí.

Resumamos brevemente. .got almacena la dirección de datos externos y .got.plt almacena la dirección de funciones externas. Las direcciones en estas estructuras serán completadas por un programa (enlazador dinámico) en el sistema operativo antes de ejecutar el código.

encuadernación tardía

Hablemos a continuación de la vinculación retrasada. Del análisis anterior obtenemos un orden. Cuando se hace referencia a una función xxxx en un enlace dinámico, apunta a la función de enlace retrasado xxxx@plt. La primera instrucción en xxxx@plt es saltar a la dirección almacenada en .got.plt. La dirección almacenada en .got.plt es la dirección de la segunda instrucción de xxxx@plt, la identificación de la función push. La tercera instrucción de xxxx@plt llama a la función <.plt>. La función <.plt> pasará la identificación de la función y la identificación del módulo a la función vinculada. Dije anteriormente que hay un programa en el sistema operativo llamado enlazador dinámico. Puede completar las tablas .got y .got.plt. Este trabajo de llenado lo realiza la función de encuadernación. Entonces, siempre que llamemos a la función una vez, esta función vinculada se activará. No habrá vinculación sin llamar al conector dinámico. Puede reducir el trabajo inicial del conector dinámico y mejorar el rendimiento.

Entonces, ¿cómo se vincula el conector dinámico?

.rel.dyn y .rel.plt

antes de que se ejecute el programa. Los enlaces dinámicos cargarán todas las bibliotecas compartidas utilizadas en el proceso del programa. Luego obtenga todas las tablas de símbolos. De esta forma conocerás la dirección de definición de cada símbolo. De esta forma, dondequiera que se haga referencia a la función y datos correspondientes, allí se rellenará la dirección. Las ubicaciones a las que se hace referencia existen .rel.dyn y .rel.plt. Los correspondientes en enlaces estáticos son .rel.data y .rel.text. Este proceso de llenado se llama reubicación.


(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 y .rel.plt, también conocidas como tablas de reubicación dinámica. Puede notar los tipos R_X86_64_RELATIVE, R_X86_64_GLOB_DAT, R_X86_64_JUMP_SLO. El propósito de estos tipos es indicarle al conector dinámico cómo completar la dirección. Tipo R_X86_64_GLOB_DAT, R_X86_64_JUMP_SLO, complete cualquiera que sea la dirección del símbolo, y la forma de completar la dirección de R_X86_64_RELATIVE se llama restablecimiento de la dirección base (bebasing), que es la dirección A en la que se carga el programa más el desplazamiento B del símbolo en el programa. Por ejemplo, los primeros tres símbolos de reubicación se refieren sobre todo a la dirección del segmento de código o segmento de datos en el segmento de datos. Porque las direcciones de estos segmentos de código y segmentos de datos a los que se hace referencia cambiarán después de la carga, pero sus posiciones relativas no cambiarán. Así que use el restablecimiento de la dirección base

.interp

Anteriormente presentamos que el conector dinámico ayudará a completar el trabajo de hacer referencia a objetos externos. Este vinculador dinámico debe especificarse en la sección .interp.


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

conector dinámico

El archivo elf se ejecuta desde execve (modo de usuario)->sys_execve (kernel)->do_execve->search_binary_handle->load_elf_binary

  • sys_execve, verifica los parámetros de la función
  • do_execve, verifique la ruta del archivo elf y extraiga el identificador único del tipo de archivo (los primeros bytes al principio)
  • search_binary_handle, busque la función de análisis correspondiente según el identificador único
  • load_elf_binary, la función de análisis de elf. Esta función asigna archivos y memoria, inicializa el entorno del proceso (como los valores de registro) y, si desea conectarse dinámicamente, necesita encontrar el vinculador dinámico correspondiente desde interp y configurar la búsqueda de biblioteca compartida. camino. Finalmente modifique el lugar donde se ejecuta la CPU después de que finalice sys_execve (establezca el lugar donde comienza a ejecutarse el programa).
  • Una vez completado lo anterior, el archivo ejecutable vinculado estáticamente se ejecutará desde e_entry (punto de entrada del programa). Los archivos vinculados dinámicamente se ejecutarán desde la entrada electrónica del vinculador dinámico.

La prioridad de la ruta de búsqueda de las bibliotecas compartidas es la variable de entorno LD_PRELOAD>variable de entorno LD_LIBRAR_PATH>/etc/ld.so.config archivo de configuración>/>/usr/local/lib64>/usr/lib64>lib64

El enlazador dinámico se ejecutará antes de que se ejecute nuestro programa. En este momento, es como antes del Big Bang. No hay nada y no puede llamar a ninguna función o dato externo. Debe reubicarse. Luego cargue todas las bibliotecas compartidas dependientes, reubique e inicialice.

Reutilización del mismo código

Dedicamos decenas de miles de palabras a explicar cómo se hace referencia al código externo. Ahora veamos si varios programas hacen referencia a la misma función externa y si sus direcciones son las mismas.

//建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 也是这个地址

No solo se puede hacer referencia al código de bibliotecas compartidas mediante enlaces dinámicos. Y ahorra memoria que los enlaces estáticos. Llegados a este punto hemos respondido a la segunda pregunta planteada al principio.

Inicialización de la pila de procesos.

Echemos un vistazo a qué es lo primero que se inserta en una pila de procesos.

//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: ""

Lo sabemos por el análisis anterior. El espacio de pila del proceso se llenará de cosas antes de ejecutarse. donde el vector auxiliar es como un directorio. Entre ellos, AT_ENTRY es la entrada del programa.

SO-NOMBRE y ldconfig

so-name es el modo de comando de la biblioteca compartida
y ldconfig es el programa de instalación de la biblioteca compartida. Puede crear un índice de so y reducir el tiempo de búsqueda de so.

[参考]
https://refspecs.linuxfoundation.org/ELF/zSeries/lzsabi0_zSeries/x895.html 【Proceso básico de Linux Inicialización del proceso

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】

Supongo que te gusta

Origin blog.csdn.net/HideInTime/article/details/128132318
Recomendado
Clasificación