it-swarm.com.de

Linux C-Programm: So finden Sie die Bibliothek, zu der eine Funktion gehört

Sagen wir zur Laufzeit, ich möchte herausfinden, wo eine Funktion "printf" definiert ist. Wie würde ich das tun? Mein erster Versuch war, die Adresse von "printf" auszudrucken und mit der virtuellen Adresszuordnung des Prozesses zu vergleichen:

mein Programm:

#include <stdio.h>
#include <unistd.h>

void main()
{
    printf("address of printf is 0x%X\n", printf);
    printf("pid is  %d\n", getpid());
    while (1);
}

ausgabe:

-bash-4.1$ ./a &
[1] 28837
-bash-4.1$ address of printf is 0x4003F8
pid is  28837

Dies besagt jedoch, dass die Funktion in meinem eigenen Programm definiert ist! 

-bash-4.1$ head /proc/28837/maps 
00400000-00401000 r-xp 00000000 08:06 6946857                            /data2/temp/del/a      <<<<<<< Address 0x4003F8 is in my own program?
00600000-00601000 rw-p 00000000 08:06 6946857                            /data2/temp/del/a
397ec00000-397ec20000 r-xp 00000000 08:11 55837039                       /lib64/ld-2.12.so
397ee1f000-397ee20000 r--p 0001f000 08:11 55837039                       /lib64/ld-2.12.so
397ee20000-397ee21000 rw-p 00020000 08:11 55837039                       /lib64/ld-2.12.so
397ee21000-397ee22000 rw-p 00000000 00:00 0 
397f000000-397f18a000 r-xp 00000000 08:11 55837204                       /lib64/libc-2.12.so
397f18a000-397f38a000 ---p 0018a000 08:11 55837204                       /lib64/libc-2.12.so
397f38a000-397f38e000 r--p 0018a000 08:11 55837204                       /lib64/libc-2.12.so
397f38e000-397f38f000 rw-p 0018e000 08:11 55837204                       /lib64/libc-2.12.so

Sollte es nicht ein Aufruf in libc sein? Wie erfahre ich, woher dieses "printf" oder eine andere Funktion stammt?

36
Sush

Zur Laufzeit können Sie gdb dafür verwenden:

(terminal 1)$ ./a
pid is  16614
address of printf is 0x400450

(terminal 2)$ gdb -p 16614
(...)
Attaching to process 16614
(...)
0x00000000004005a4 in main ()
(gdb)

(gdb) info sym printf
printf in section .text of /lib/x86_64-linux-gnu/libc.so.6

Wenn Sie Ihr Programm nicht unterbrechen möchten oder gdb nur ungern verwenden möchten, können Sie auch ld.so zur Ausgabe einiger Debugging-Informationen auffordern:

(terminal 1)$ LD_DEBUG=bindings LD_DEBUG_OUTPUT=syms ./a
pid is  17180
address of printf is 0x400450

(terminal 2)$ fgrep printf syms.17180
    17180:  binding file ./a [0] to /lib/x86_64-linux-gnu/libc.so.6 [0]: normal symbol `printf' [GLIBC_2.2.5]
23
xhienne

Die Adresse, die Sie beobachten, befindet sich in der Procedure Linkage Table (PLT). Dieser Mechanismus wird verwendet, wenn der Ort eines externen (dynamisch verknüpften) Symbols zu dem Zeitpunkt nicht bekannt ist, wenn Ihre Binärdatei kompiliert und verknüpft wird.

Der Zweck besteht darin, dass die externe Verknüpfung nur an einer Stelle stattfindet, am PLT, und nicht an allen Stellen in Ihrem Code, an denen das Symbol aufgerufen wird. Wenn also printf() aufgerufen wird, lautet der Weg:

main -> printf @ PLT -> printf @ libc

Zur Laufzeit können Sie nicht leicht herausfinden, in welcher externen Bibliothek sich die von Ihnen aufgerufene Funktion befindet. Sie müssten die Opcodes am Ziel (dem PLT) parsen, was normalerweise die Adresse aus dem Abschnitt .dynamic abruft und dorthin springt, dann nachsehen, wo sich das Symbol wirklich befindet und schließlich/proc/pid/maps analysieren die externe Bibliothek.

31
Ctx

Sagen wir zur Laufzeit, ich möchte herausfinden, wo eine Funktion "printf" definiert ist.

Im Allgemeinen und in absoluten Zahlen können Sie wahrscheinlich nicht (zumindest nicht leicht). Eine gegebene Funktion kann in mehreren Bibliotheken definiert sein (für printf ist dies unwahrscheinlich, da sie sich in der C-Standardbibliothek befindet).

Wenn Sie Ihr Linux-System erstellen von Grund auf , können Sie davon träumen, dass jede Bibliothek zur Erstellungszeit verarbeitet wird (wenn Sie beispielsweise jede gemeinsam genutzte Bibliothek erstellen, können Sie alle öffentlichen Namen mit nm) abrufen (1) und in eine Datenbank stellen). Dies ist heute noch nicht wirklich geschehen, aber einige Forschungsprojekte gehen in diese Richtung (insbesondere softwareheritage und andere im Jahr 2019).

Übrigens könnten Sie mehrere Bibliotheken haben, die printf definieren. Zum Beispiel, wenn Sie GNU glibc und musl-libc auf Ihrem Computer installieren (oder wahrscheinlicher, wenn Sie mehrere Varianten von glibc). Ein bestimmtes Programm wird wahrscheinlich nicht beide verwenden (könnte theoretisch jedoch dlopen beide verwenden) Sie).

Vielleicht möchten Sie die Linux-spezifische dladdr (3) -Funktion. Anhand einer bestimmten Adresse wird Ihnen mitgeteilt, dass das freigegebene Objekt über diese Adresse verfügt.

die Funktion ist in meinem eigenen Programm definiert

Ja. Lesen Sie mehr über dynamisches Verlinken . Lesen Sie insbesondere Dreppers How to Write Shared Libraries Paper. Verstehen Sie was ist der Zweck der Prozedur-Verknüpfungstabelle .

  1. zeiger sind printfed, wobei %p verwendet wird, nicht %X:

    printf("address of printf is 0x%p\n", printf);
    
  2. Beim Kompilieren gegen statische libc wird printf in Ihre Binärdatei verlinkt

  3. wenn mit kompiliert

    gcc -fPIC a.c # (older gccs)
    ...
    gcc -fno-plt a.c # (gcc 6 and above)
    

    ausgänge:

    address of printf is 0x0x7f40acb522a0
    

    welches ist innerhalb von 

    7f40acaff000-7f40accc2000 r-xp 00000000 fd:00 100687388                  /usr/lib64/libc-2.17.so
    

Lesen Was bedeutet @plt hier? um mehr darüber zu erfahren.

9
fukanchik

Sie können dies statisch ableiten. Keine Notwendigkeit, auszuführen:

$ readelf -Ws a.out | grep printf
      1: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND [email protected]_2.2.5 (2)
     51: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND [email protected]@GLIBC_2.2.5
0
KevinZ

Analysieren Sie die elf-Datei nach den dynamisch verknüpften Bibliotheken. Dann können Sie sie nach dem gewünschten Symbol analysieren

0
P__J__