En primer lugar, una breve reseña
plataforma 1.Linux, 64
2. Los archivos .so, enlace dinámico
3. Descripción del archivo ELF base
Elf (ejecutable y Linking File Format), es decir, tiene dos vistas, una vista se realiza (también conocido como la carga de programa puede ver, para cargar el archivo en la memoria y puede ejecutar una vista) de la derecha en la figura. Otro vínculo es una vista para su uso en un enlace a la etapa de engarce, a la izquierda en la figura. Figura:
En segundo lugar, el ejemplo
fun.h
#ifndef _FUN_H_
#define _FUN_H_
extern int fun_num1;
int fun_print();
#endif
fun.c
#include <stdio.h>
int fun_num1 = 5;
int fun_print() {
printf("fun_num1 %d\n", fun_num1);
}
fun1.h
#ifndef _FUN1_H_
#define _FUN1_H_
extern int fun1_num1;
int fun1_print();
#endif
fun1.c
#include "fun1.h"
#include <stdio.h>
int fun1_num1 = 3;
int fun1_print() {
printf("fun1_num1:%d\n", fun1_num1);
}
main.c
#include "fun.h"
#include "fun1.h"
int main() {
fun1_print();
fun_print();
fun1_num1 = 1;
fun_num1 = 2;
fun1_print();
fun_print();
return 0;
}
makefile
flag=-g
all:main
main.o:main.c
gcc -o $@ -c $^ $(flag)
main:main.o libfun.so fun1.o
gcc -o main_s main.o fun1.o -L ./ -lfun
fun_s.o:fun.h fun.c
gcc -fPIC -o fun_s.o -c fun.c $(flag)
libfun.so:fun_s.o
gcc --shared -o $@ $^
fun1.o:fun1.h fun1.c
gcc -o fun1.o -c fun1.c $(flag)
clean:
rm -rf *.o *.so
.PHONY:clean
ejecutar:
make
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:./
./main_s
El resultado:
fun1_num1:3
fun_num1 5
fun1_num1:1
fun_num1 2
En tercer lugar, la función de enlace dinámico
Estática enlace de función: fun1_print
Dynamic Link función: fun_print
1.gdb main_s, DISA principal
(gdb) disas main
Dump of assembler code for function main:
0x0000000000400716 <+0>: push %rbp
0x0000000000400717 <+1>: mov %rsp,%rbp
0x000000000040071a <+4>: mov $0x0,%eax
0x000000000040071f <+9>: callq 0x40075d <fun1_print>
0x0000000000400724 <+14>: mov $0x0,%eax
0x0000000000400729 <+19>: callq 0x4005f0 <fun_print@plt>
0x000000000040072e <+24>: movl $0x1,0x200908(%rip) # 0x601040 <fun1_num1>
0x0000000000400738 <+34>: movl $0x2,0x200906(%rip) # 0x601048 <fun_num1>
0x0000000000400742 <+44>: mov $0x0,%eax
0x0000000000400747 <+49>: callq 0x40075d <fun1_print>
0x000000000040074c <+54>: mov $0x0,%eax
0x0000000000400751 <+59>: callq 0x4005f0 <fun_print@plt>
0x0000000000400756 <+64>: mov $0x0,%eax
0x000000000040075b <+69>: pop %rbp
0x000000000040075c <+70>: retq
End of assembler dump.
2. Comprobar los enlaces de las funciones estáticas fun1_print
0x000000000040071f <9>: callq 0x40075d <fun1_print>
3.disas 0x40075d, para ver si la dirección es la dirección de una función fun1_print
Dump of assembler code for function fun1_print:
0x000000000040075d <+0>: push %rbp
0x000000000040075e <+1>: mov %rsp,%rbp
0x0000000000400761 <+4>: mov 0x2008d9(%rip),%eax # 0x601040 <fun1_num1>
0x0000000000400767 <+10>: mov %eax,%esi
0x0000000000400769 <+12>: mov $0x400804,%edi
0x000000000040076e <+17>: mov $0x0,%eax
0x0000000000400773 <+22>: callq 0x4005e0 <printf@plt>
0x0000000000400778 <+27>: nop
0x0000000000400779 <+28>: pop %rbp
0x000000000040077a <+29>: retq
End of assembler dump.
Puede verse en GDB, dirección fun1_print 0x40075d es función estática, por lo que para el enlace función estática, será capaz de saber la dirección virtual absoluto de la función, se puede llevar a cabo directamente por las llamadas a funciones callq en una etapa de enlace estático.
4. Véase fun_print función dinámica
0x0000000000400729 <19>: callq 0x4005f0 <fun_print @ PLT>
5.disas 0x4005f0, para ver si la dirección es la dirección de una función fun_print
Dump of assembler code for function fun_print@plt:
0x00000000004005f0 <+0>: jmpq *0x200a2a(%rip) # 0x601020
0x00000000004005f6 <+6>: pushq $0x1
0x00000000004005fb <+11>: jmpq 0x4005d0
End of assembler dump.
Podemos ver aquí sólo tres instrucciones, y no hay ningún comando asociado con printf, así que esto no es una función de la dirección de fun_print real.
Las siguientes explicaciones
1) callq 0x4005f0, callq 0x40072e dirección es la siguiente dirección en la pila, a continuación, la ejecución salta a 0x4005f0 una función, cuando un valor por el empuje, el código de dirección será saltar esta función se ha completado, ejecución.
Dump of assembler code for function main:
...
0x0000000000400729 <+19>: callq 0x4005f0 <fun_print@plt>
0x000000000040072e <+24>: movl $0x1,0x200908(%rip) # 0x601040 <fun1_num1>
...
End of assembler dump.
2) Para fun_print @ plt, esta función es un enlazador dinámico enlazará cada función generada de forma automática, uno a uno. Esto significa que si usted tiene una función como la diversión en la biblioteca compartida es así, entonces el enlace generará automáticamente la diversión @ función PLT, y PLT a la mesa vinculación procedimiento (tablas procedimiento de vinculación, los procedimientos deben estar en lenguaje C es una función de la media), plt medio de la diversión es una función de la agencia. La función principal de esta función está por debajo de dicha.
Dump of assembler code for function fun_print@plt:
0x00000000004005f0 <+0>: jmpq *0x200a2a(%rip) # 0x601020
0x00000000004005f6 <+6>: pushq $0x1
0x00000000004005fb <+11>: jmpq 0x4005d0
End of assembler dump.
jmpq * 0x200a2a (% RIP)
jmpq: un salto hacia atrás como un valor de dirección de acuerdo con el valor de
*: Valor medio se toma como la parte posterior de direcciones, se accede al valor de la dirección utilizando el valor de la dirección de la final
% Rip: es la dirección de la instrucción actual, es necesario tomar nota aquí, cuando después de la instrucción operación de extracción de jmpq * 0x200a2a (% RIP) esta instrucción,% rasgar se ha cambiado para hacer frente a la próxima instrucción salto, y por lo tanto la corriente% rip es 0x4005f6, por lo 0x200a2a (% RIP) = 0x200a2a + 0x4005f6 = 0x601020.
Largo aliento-sobre, 0x601020 dirección es la sección .got.plt, nos dieron media tabla de desplazamiento global, esto se utiliza principalmente para la sección de enlace dinámico, que abordará esta memoria de variables y funciones de enlace dinámico, pero es .got.plt sección PLT a la función global tabla de desplazamiento que utiliza.
Por instrucciones: readelf -SW main_s, .got.plt la dirección (direcciones virtuales) la dirección de comienzo 0x601000, tamaño: 0x30, así 0x601000 ~ 0x601030, 0x601020 en la dirección para que el .got.plt
Section Headers:
[Nr] Name Type Address Off Size ES Flg Lk Inf Al
...
[12] .plt PROGBITS 00000000004005d0 0005d0 000040 10 AX 0 0 16
[13] .plt.got PROGBITS 0000000000400610 000610 000008 00 AX 0 0 8
[14] .text PROGBITS 0000000000400620 000620 0001d2 00 AX 0 0 16
...
[16] .rodata PROGBITS 0000000000400800 000800 000012 00 A 0 0 4
...
[23] .got PROGBITS 0000000000600ff8 000ff8 000008 08 WA 0 0 8
[24] .got.plt PROGBITS 0000000000601000 001000 000030 08 WA 0 0 8
[25] .data PROGBITS 0000000000601030 001030 000014 00 WA 0 0 8
[26] .bss NOBITS 0000000000601048 001044 000008 00 WA 0 0 8
...
3) Comprobar el valor almacenado en la dirección 0x601020, x / 2x 0x601020
(gdb) x/2x 0x601020
0x601020: 0x004005f6 0x00000000
Dirección almacenada valor 0x00000000004005f6, con cuidado se dará cuenta de que ésta es realmente la función fun_print @ PLT jmpq * 0x200a2a (% RIP) la dirección de la siguiente instrucción pushq $ 0x1:
Dump of assembler code for function fun_print@plt:
...
0x00000000004005f6 <+6>: pushq $0x1
0x00000000004005fb <+11>: jmpq 0x4005d0
End of assembler dump.
Aquí el proceso de vinculación dinámica función puede decir brevemente a continuación, cuando la primera llamada a la función fun_print dinámica, va a saltar en medio de la función de proxy fun_print @ PLT, PLT fun_print @ saltará a .got.plt almacenado valor de dirección, por primera vez, se guarda cuando la dirección de salto fun_print @ plt la siguiente orden, la dirección de la siguiente comando se ejecuta posterior al código enlazador, el enlazador se .so archivo de consulta, la consulta si existen incluido la función fun_print, cuando una consulta a la función fun_print actualizará la dirección correspondiente a la ubicación de .got.plt, entonces la próxima vez que la función se llama, saltará directamente a la dirección de la función, sin la necesidad de investigar atada dado que este retardo es una función de la dinámica de unión
6 una vista esquemática fun_print @ plt donde posterior a la ejecución de la instrucción, por objdump -d main_s (no el BGF)
Disassembly of section .plt:
00000000004005d0 <printf@plt-0x10>:
4005d0: ff 35 32 0a 20 00 pushq 0x200a32(%rip) # 601008 <_GLOBAL_OFFSET_TABLE_+0x8>
4005d6: ff 25 34 0a 20 00 jmpq *0x200a34(%rip) # 601010 <_GLOBAL_OFFSET_TABLE_+0x10>
4005dc: 0f 1f 40 00 nopl 0x0(%rax)
00000000004005e0 <printf@plt>:
4005e0: ff 25 32 0a 20 00 jmpq *0x200a32(%rip) # 601018 <_GLOBAL_OFFSET_TABLE_+0x18>
4005e6: 68 00 00 00 00 pushq $0x0
4005eb: e9 e0 ff ff ff jmpq 4005d0 <_init+0x28>
Se puede ver saltará a jmpq 0x4005d0, que es una función de impresión @ PLT-0x10, en una función pública, activado por la llamada de la función enlazador, entonces aquí se puede ver que continuará para saltar a 0x601010, esta vez gDB volver a la vista, ejecute el siguiente comando en el BGF
1) b * _start + 0 (inicio de la ejecución de código de entrada)
2) r principal (para hacer el código realmente cargado, también lo hará el mapeo enlazador ld)
3) x / 2x 0x601010
(gdb) x/2x 0x601010
0x601010: 0xf7dee870 0x00007fff
4) DISA 0x00007ffff7dee870
(gdb) disas 0x00007ffff7dee870
Dump of assembler code for function _dl_runtime_resolve_avx:
...
0x00007ffff7dee91e <+174>: callq 0x7ffff7de69f0 <_dl_fixup>
...
End of assembler dump.
Aquí se puede ver que va a entrar en la función _dl_runtime_resolve_avx enlazador, y pide además _dl_fixup, donde fui a ver ninguna aplicación específica más allá, pero aquí ya saben dará lugar a la ejecución de un enlazador posterior, a continuación, la dirección real es cómo demostrar ante la especulación fun_print cambios a .got.plt en ella, la siguiente continuará
1.1) b * principal de +24 (0x40072e yacía punto de interrupción en su lugar, es la siguiente instrucción después de la finalización de la llamada fun_print @ PLT)
1.2) c (permitir la ejecución de código para 0x40072e)
1.3) x / 2x 0x601020 (ver fun_print @ PLT salto a la dirección almacenada .got.plt valor, lo siguiente puede saber de la actualización 0x00007ffff7bd56e0 0x4005e6 el original)
(gdb) x/2x 0x601020
0x601020: 0xf7bd56e0 0x00007fff
1.4) DISA 0x00007ffff7bd56e0 (se puede saber esto es una función de la fun_print real)
Dump of assembler code for function fun_print:
0x00007ffff7bd56e0 <+0>: push %rbp
0x00007ffff7bd56e1 <+1>: mov %rsp,%rbp
0x00007ffff7bd56e4 <+4>: mov 0x2008ed(%rip),%rax # 0x7ffff7dd5fd8
0x00007ffff7bd56eb <+11>: mov (%rax),%eax
0x00007ffff7bd56ed <+13>: mov %eax,%esi
0x00007ffff7bd56ef <+15>: lea 0x17(%rip),%rdi # 0x7ffff7bd570d
0x00007ffff7bd56f6 <+22>: mov $0x0,%eax
0x00007ffff7bd56fb <+27>: callq 0x7ffff7bd55c0 <printf@plt>
0x00007ffff7bd5700 <+32>: nop
0x00007ffff7bd5701 <+33>: pop %rbp
0x00007ffff7bd5702 <+34>: retq
End of assembler dump.
1.5) Después de la operación anterior, también confirmado por primera vez para realizar disparadores de función dinámicas la dirección real de la función de consulta dinámica enlazador, a continuación, se actualiza el valor de la dirección almacenada en .got.plt, después de la llamada a la función puede ser directamente salta a la dirección de la función real
Dinámica que une cuatro variables
Las variables estáticas enlace: fun1_num1
Las variables dinámicas enlace: fun_num1
0. pre-operación
1) main_s GDB (depuración main_s)
2) b * _start + 0 (punto de rotura en el punto de entrada de código)
3) r (código de ejecución)
1.gdb principales, DISA principal
(gdb) disas main
Dump of assembler code for function main:
0x0000000000400716 <+0>: push %rbp
0x0000000000400717 <+1>: mov %rsp,%rbp
0x000000000040071a <+4>: mov $0x0,%eax
0x000000000040071f <+9>: callq 0x40075d <fun1_print>
0x0000000000400724 <+14>: mov $0x0,%eax
0x0000000000400729 <+19>: callq 0x4005f0 <fun_print@plt>
0x000000000040072e <+24>: movl $0x1,0x200908(%rip) # 0x601040 <fun1_num1>
0x0000000000400738 <+34>: movl $0x2,0x200906(%rip) # 0x601048 <fun_num1>
0x0000000000400742 <+44>: mov $0x0,%eax
0x0000000000400747 <+49>: callq 0x40075d <fun1_print>
0x000000000040074c <+54>: mov $0x0,%eax
0x0000000000400751 <+59>: callq 0x4005f0 <fun_print@plt>
0x0000000000400756 <+64>: mov $0x0,%eax
0x000000000040075b <+69>: pop %rbp
0x000000000040075c <+70>: retq
End of assembler dump.
2 Ver llamada variable estática
0x000000000040072e <24>: movl $ 0x1,0x200908 (% RIP) # 0x601040 <fun1_num1>
3 x 0x601040
0x601040 <fun1_num1>: 0x00000003
El valor es 3, por lo que los main_s archivo ejecutable, es fun1_num1 direcciones virtuales absolutos para acceder a ella
Ver 4 dinámica de las variables de llamada
0x0000000000400738 <34>: movl $ 0x2,0x200906 (% RIP) # 0x601048 <fun_num1>
5 x 0x601048
0x601048 <fun_num1>: 0x00000005
El valor es 5, por lo que las variables dinámicas main_s archivos factibles, es fun_num1 direcciones virtuales absolutos para acceder a ella, parece que hay una pregunta aquí, no debe ser lo mismo que usted? Cómo va a ser el mismo. Esto es porque los main_s documento final generado, si los enlaces estáticos a útil a una variable global, a continuación, las variables globales se guardan directamente en los main_s segmento de datos, posiblemente en .data (variable inicializada, la estática relativa a si hay un enlace inicialización), puede ser en .bss (variables no inicializadas, si existe un enlace con respecto a la inicialización estática). E independientemente de si estas variables globales se definen en .so, o se definen en el archivo de objeto estático, siempre y cuando hay un uso del fichero objeto estático, no siempre se guarda en un segmento de datos en el archivo ejecutable final. La dirección de destino reubicación entre otros archivos estáticos utilizando direcciones virtuales absolutos de acceso, mientras que otros fichero .so para acceder a las variables globales usando el acceso indirecto. Así que para ver la diferencia, tiene que mirar en el archivo del código de función .so.
6 DISA fun_print
(gdb) disas fun_print
Dump of assembler code for function fun_print:
0x00007ffff7bd56e0 <+0>: push %rbp
0x00007ffff7bd56e1 <+1>: mov %rsp,%rbp
0x00007ffff7bd56e4 <+4>: mov 0x2008ed(%rip),%rax # 0x7ffff7dd5fd8
0x00007ffff7bd56eb <+11>: mov (%rax),%eax
0x00007ffff7bd56ed <+13>: mov %eax,%esi
0x00007ffff7bd56ef <+15>: lea 0x17(%rip),%rdi # 0x7ffff7bd570d
0x00007ffff7bd56f6 <+22>: mov $0x0,%eax
0x00007ffff7bd56fb <+27>: callq 0x7ffff7bd55c0 <printf@plt>
0x00007ffff7bd5700 <+32>: nop
0x00007ffff7bd5701 <+33>: pop %rbp
0x00007ffff7bd5702 <+34>: retq
End of assembler dump.
0x00007ffff7bd56e4 <4>: mov 0x2008ed (% RIP),% rax # 0x7ffff7dd5fd8
0x00007ffff7bd56eb <11>: mov (% rax),% eax
Aquí se puede ver que hay dos instrucciones, el primer valor se 0x2008ed (rip%) como una dirección 0x7ffff7dd5fd8, el valor de dirección se almacena en el registro rax%, entonces el valor almacenado de% rax como la dirección, a continuación, se refiere al valor almacenado en la dirección de registro en eax, eax es donde impreso el valor final efectivo fun_num1.
1) x / 2x 0x7ffff7dd5fd8
(gdb) x/2x 0x7ffff7dd5fd8
0x7ffff7dd5fd8: 0x00601048 0x00000000
Usted encontrará que la dirección almacenada es 0x601048, exactamente en el valor de la dirección anterior fun_num1
(gdb) disas main
Dump of assembler code for function main:
...
0x000000000040072e <+24>: movl $0x1,0x200908(%rip) # 0x601040 <fun1_num1>
0x0000000000400738 <+34>: movl $0x2,0x200906(%rip) # 0x601048 <fun_num1>
...
End of assembler dump.
Por lo tanto .so variables globales de acceso a archivos se almacenan la dirección de la variable global por una posición, y que la posición es en realidad la sección .got, la función .got.plt por lo tanto, se puede deducir se utiliza para enlazar dinámicamente y .got es un links variable dinámica. A continuación, la función de enlace dinámico es el retraso obligado (que es, por primera vez, antes de que la búsqueda de direcciones de llamada de función), las variables dinámicas que enlace es correcto? Después de mi práctica, nos encontramos con una variable de enlace dinámico se carga en el programa se han llevado a cabo para hacer frente a las variables dinámicas se inicializan cuando la entrada de código _start, y por lo tanto no estará vinculada dinámicamente la unión de retardo variable. Otro puede demostrar mis observaciones
1.1) ps -ef | grep main_s (Consulte ID del proceso)
1.2) cat / proc / PID} {/ mapas (ver la asignación de direcciones a cabo)
00400000-00401000 r-xp 00000000 fd:01 1455595 /tmp/test/main_s
00600000-00601000 r--p 00000000 fd:01 1455595 /tmp/test/main_s
00601000-00602000 rw-p 00001000 fd:01 1455595 /tmp/test/main_s
7ffff780b000-7ffff79cb000 r-xp 00000000 fd:01 402248 /lib/x86_64-linux-gnu/libc-2.23.so
7ffff79cb000-7ffff7bcb000 ---p 001c0000 fd:01 402248 /lib/x86_64-linux-gnu/libc-2.23.so
7ffff7bcb000-7ffff7bcf000 r--p 001c0000 fd:01 402248 /lib/x86_64-linux-gnu/libc-2.23.so
7ffff7bcf000-7ffff7bd1000 rw-p 001c4000 fd:01 402248 /lib/x86_64-linux-gnu/libc-2.23.so
7ffff7bd1000-7ffff7bd5000 rw-p 00000000 00:00 0
7ffff7bd5000-7ffff7bd6000 r-xp 00000000 fd:01 1455592 /tmp/test/libfun.so
7ffff7bd6000-7ffff7dd5000 ---p 00001000 fd:01 1455592 /tmp/test/libfun.so
7ffff7dd5000-7ffff7dd6000 r--p 00000000 fd:01 1455592 /tmp/test/libfun.so
7ffff7dd6000-7ffff7dd7000 rw-p 00001000 fd:01 1455592 /tmp/test/libfun.so
7ffff7dd7000-7ffff7dfd000 r-xp 00000000 fd:01 402246 /lib/x86_64-linux-gnu/ld-2.23.so
7ffff7fea000-7ffff7fed000 rw-p 00000000 00:00 0
7ffff7ff6000-7ffff7ff7000 rw-p 00000000 00:00 0
7ffff7ff7000-7ffff7ffa000 r--p 00000000 00:00 0 [vvar]
7ffff7ffa000-7ffff7ffc000 r-xp 00000000 00:00 0 [vdso]
7ffff7ffc000-7ffff7ffd000 r--p 00025000 fd:01 402246 /lib/x86_64-linux-gnu/ld-2.23.so
7ffff7ffd000-7ffff7ffe000 rw-p 00026000 fd:01 402246 /lib/x86_64-linux-gnu/ld-2.23.so
7ffff7ffe000-7ffff7fff000 rw-p 00000000 00:00 0
7ffffffde000-7ffffffff000 rw-p 00000000 00:00 0 [stack]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall]
Se puede ver 7ffff7dd5000-7ffff7dd6000 r - p 00000000 fd: 01 1455592 /tmp/test/libfun.so es de sólo lectura
La dirección de inicio libfun.so es 0x7ffff7bd5000, se puede calcular que corresponde a la dirección virtual 7ffff7dd5000-7ffff7dd6000 (0x20000-0x21000)
1.3) readelf -SW libfun.so
Section Headers:
[Nr] Name Type Address Off Size ES Flg Lk Inf Al
...
[21] .got PROGBITS 0000000000200fd0 000fd0 000030 08 WA 0 0 8
[22] .got.plt PROGBITS 0000000000201000 001000 000020 08 WA 0 0 8
...
.Got se puede ver entre 0x2000-0x21000, por lo que se .got, por lo que el programa se está ejecutando, no puede ser modificada de sólo lectura, por lo que no se puede retrasar la unión.
.Got.plt se puede ver, mientras que un segmento de la siguiente rw-p, por lo tanto .got.plt es escribible, los datos se puede cambiar mientras se ejecuta el programa, por lo que puede completar el retardo requerido unido condición
En quinto lugar, la pregunta y la respuesta
1. Dado que los archivos se comparten archivos, sólo una copia en la memoria física, y el otro es el archivo de modo de asignación de dirección mmap (direcciones virtuales se asignan a la implementación física), entonces se ha de ejecutar el programa, que el proceso de vinculado dinámicamente, como inevitablemente modificar .got archivo de modo mmap asignada y .got.plt los valores almacenados correspondientes para lograr un enlace dinámico, entonces esto no modificar el mmap asignación de dirección dirección física correspondiente para que el archivo todavía? En aquel entonces ejecutar este programa no va a leer el valor de dirección se modifica y se ve afectada por ella?
R: Debido al utilizar mmap, básicamente mediante la propiedad MAP_PRIVATE, es decir, en la copia, copia en escritura con escritura, por lo tanto, una sección de asignación operación de escritura se produce, la copia a una nueva dirección física, esto no será por lo que afectará el valor real de la dirección física del archivo. Por lo tanto, se puede entender como cada proceso que .got, .got.plt son únicos para el proceso, o el proceso de su segmento de datos es exclusivo.