2011-03-29 15 views
49
0x00000000004004b6 <main+30>: callq 0x400398 <[email protected]> 

¿Alguien sabe?¿Qué significa @plt aquí?

ACTUALIZACIÓN

por qué dos disas printf me dan resultado diferente?

(gdb) disas printf 
Dump of assembler code for function [email protected]: 
0x0000000000400398 <[email protected]+0>: jmpq *0x2004c2(%rip)  # 0x600860 <_GLOBAL_OFFSET_TABLE_+24> 
0x000000000040039e <[email protected]+6>: pushq $0x0 
0x00000000004003a3 <[email protected]+11>: jmpq 0x400388 

(gdb) disas printf 
Dump of assembler code for function printf: 
0x00000037aa44d360 <printf+0>: sub $0xd8,%rsp 
0x00000037aa44d367 <printf+7>: mov %rdx,0x30(%rsp) 
0x00000037aa44d36c <printf+12>: movzbl %al,%edx 
0x00000037aa44d36f <printf+15>: mov %rsi,0x28(%rsp) 
0x00000037aa44d374 <printf+20>: lea 0x0(,%rdx,4),%rax 
0x00000037aa44d37c <printf+28>: lea 0x3f(%rip),%rdx  # 0x37aa44d3c2 <printf+98> 
+0

¿De dónde vino esa primera línea de salida? 'objdump' Imagino? –

Respuesta

83

Es una manera de conseguir fixups de código (ajuste de direcciones en función de dónde se encuentra el código en la memoria virtual) sin tener que mantener una copia independiente del código para cada proceso. El PLT es la tabla de vinculación de procedimientos, una de las estructuras que hace que la carga dinámica y los enlaces sean más fáciles de usar.

[email protected] es en realidad un pequeño trozo que (finalmente) llama a la función real printf.

Esta función real se puede mapear en cualquier ubicación en un proceso determinado (espacio de direcciones virtuales) como el código que lo llama.

Por lo tanto, con el fin de permitir el intercambio de código apropiado del código de llamada (a la izquierda abajo), que no desea aplicar ningún fixups directamente a ella desde que restringirá donde puede ser localizado en otros procesos.

El PLT es una pequeña proceso específico área en una dirección fiable calculada en tiempo de ejecución, que no es compartida entre los procesos por lo que cualquier proceso dado puede cambiarlo sin embargo que quiere.

En otras palabras, examinar el siguiente diagrama que muestra tanto su código y el código de la biblioteca asignada a diferentes direcciones virtuales en dos procesos:

  Mapped to: 0x1234  0x9000  0x8888 
     +-----------------+ +----------+ +----------+ 
     |     | | Private | |   | 
ProcA |     | | PLT/GOT | |   | 
     |     | | area | |   | 
     | Shared   | +----------+ | Shared | 
========| application |==============| library |== 
     | code   | +----------+ | code | 
     |     | | Private | |   | 
ProcB |     | | PLT/GOT | |   | 
     |     | | area | |   | 
     +-----------------+ +----------+ +----------+ 
     Mapped to: 0x2020  0x9000  0x6666 

Este ejemplo particular muestra un caso sencillo donde los mapas PLT a una ubicación fija. En su escenario, que está situado en relación con el contador de programa actual como se evidencia por su programa de búsqueda de venta libre-relativa:

<[email protected]+0>: jmpq *0x2004c2(%rip) ; 0x600860 <_GOT_+24> 

Un buen artículo se puede encontrar here, que detalla cómo glibc se carga en tiempo de ejecución.

Básicamente, la forma original en que se creó el código compartido significaba que tenían que cargarse en la misma ubicación de memoria en el espacio de direcciones virtuales de cada proceso que lo usaba. O eso o no se pudo compartir, ya que el acto de arreglar la sola copia compartida de para un proceso podría llenar por completo otro proceso donde se asignó a una ubicación diferente.

Mediante el uso de código independiente de posición, junto con el PLT y una tabla de desplazamiento mundial (GOT), el primera llamada a una función [email protected] (en el PLT) es una operación de varias etapas, en las que:

  • llama al [email protected] en el PLT.
  • llama a la versión GOT (a través del puntero) que inicialmente indica algún código de configuración en el PLT.
  • Este código de configuración carga la biblioteca compartida pertinente si aún no se ha realizado, entonces modifica GOT para que las llamadas posteriores se realicen directamente al printf real en lugar del código de configuración.

en llamadas posteriores, debido a que el GOT se ha modificado, el enfoque de múltiples etapas se simplifica:

  • se llama [email protected] en el PLT.
  • llama a la versión GOT (a través del puntero) que apunta al realprintf.
+0

¿Por qué dos 'disas printf' me dan un resultado diferente? – gdb

+1

El stub está antes de que escriba 'r', y el otro está después de' break' – gdb

+0

Se puede reproducir con cualquier ejecutable con gdb. – gdb

4

No estoy seguro, pero probablemente lo que ha visto tiene sentido. La primera vez que ejecuta el comando disas, aún no se llama a printf, por lo que no se resuelve. Una vez que su programa llama al método printf la primera vez que se actualiza el GOT y ahora se resuelve el printf y el GOT apunta a la función real. Por lo tanto, la siguiente llamada al comando disas muestra el ensamblaje printf real.