Invocando funciones externas al binario

En linux, los ejecutables invocan funciones externas mediante dos mecanismos:

¿Cuáles son algunas de las funciones que provee el linux kernel (los llamados system calls?) ?

Invocaciones a funciones de librerías compartidas

Un programa que realizará invocaciones a funciones de una librería compartida muestra esas funciones en su sección de símbolos. Esto es parte de lo obtenido con readelf -s hw, donde hw es un programa que invoca a printf. Nota que el símbolo printf@@GLIBC_2.2.5 dice UND (undefined). Esto quiere decir que la función printf no está implementada en nuestro programa, i.e. es una función externa a nuestro programa y su implementación será leida de la librería compartida libc durante la ejecución.

50: 0000000000601038     0 NOTYPE  GLOBAL DEFAULT   25 _edata
51: 00000000004005b4     0 FUNC    GLOBAL DEFAULT   15 _fini
52: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND printf@@GLIBC_2.2.5
53: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __libc_start_main@@GLIBC_

Otra forma de desplegar los símbolos de un programa es usando el comando nm:

$nm ./hw
0000000000601038 B __bss_start
. . . . 
                 U __libc_start_main@@GLIBC_2.2.5
0000000000400526 T main
                 U printf@@GLIBC_2.2.5

Nuevamente vemos a printf. La letra U indica que no está definida (implementada) en nuestro programa.

El disassembly del programa muestra que en la función main se está invocando a una tal función printf@plt.

0000000000400526 <main>:
  400526:   55                      push   %rbp
  400527:   48 89 e5                mov    %rsp,%rbp
  40052a:   bf c4 05 40 00          mov    $0x4005c4,%edi
  40052f:   b8 00 00 00 00          mov    $0x0,%eax
  400534:   e8 c7 fe ff ff          callq  400400 <printf@plt>
  400539:   b8 00 00 00 00          mov    $0x0,%eax
  40053e:   5d                      pop    %rbp
  40053f:   c3                      retq   

El @plt son las siglas de procedure linkage table y es un mecanismo que facilita el uso de funciones de librerías compartidas. Si buscas el disasembly de printf@plt, encuentras lo siguiente.

0000000000400400 <printf@plt>:
  400400:   ff 25 12 0c 20 00       jmpq   *0x200c12(%rip)        # 601018 <_GLOBAL_OFFSET_TABLE_+0x18>
  400406:   68 00 00 00 00          pushq  $0x0
  40040b:   e9 e0 ff ff ff          jmpq   4003f0 <_init+0x28>

Obviamente esta no es la implementación de printf. Es meramente un trampolin que ayuda a que el flujo del programa se redirija a la dirección en memoria donde realmente está la implementación de printf. El brinco que dará nuestro programa para llegar a printf está determinado por una información que vive en el GOT (global offset table) de nuestro programa. La primera vez que printf es invocado por nuestro programa, una función del sistema operativo (dl_runtime_resolve) averigua dónde vive la implementación de printf en memoria y ajusta el contenido del GOT para printf.

Cada una de las funciones externas tiene su stub PLT y una entrada en el GOT, aun las funciones externas que son llamadas por las funciones que inserta el linker en nuestro ejecutable, por ejemplo libc_start_main:

0000000000400410 <__libc_start_main@plt>:
  400410:   ff 25 0a 0c 20 00       jmp    QWORD PTR [rip+0x200c0a]        # 601020 <_GLOBAL_OFFSET_TABLE_+0x20>
  400416:   68 01 00 00 00          push   0x1
  40041b:   e9 d0 ff ff ff          jmp    4003f0 <_init+0x28>

Al ejecutar ldd ./hw podemos ver las librerías compartidas de las que hace uso. Alli vemos listada a libc.so.6.

ldd ./hw     
  linux-vdso.so.1 =>  (0x00007ffd89bb8000)
  libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f6945477000)
  /lib64/ld-linux-x86-64.so.2 (0x00007f6945841000)

Conocimiento util?

Si corres nm sobre una librería compartida, puedes averiguar los nombres de todas las funciones que comparte:

$ nm -D /lib/x86_64-linux-gnu/libc.so.6
...
0000000000055800 T printf
00000000001168e0 T __printf_chk
0000000000052bc0 T __printf_fp
...

strace and ltrace