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

Lesezeit: 5 Minuten

Benutzeravatar von Sush
Suss

Angenommen, ich möchte zur Laufzeit herausfinden, wo eine Funktion “printf” definiert ist. Wie würde ich das tun? Mein erster Versuch bestand darin, 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);
}

Ausgang:

-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 finde ich heraus, woher dieses “printf” oder eine andere Funktion kommt?

  • Haha. Wie würde ich es zur Laufzeit finden? Bitte beachten Sie, dass ‘printf’ nur ein vereinfachtes Beispiel ist.

    – Pusch

    1. Oktober 2018 um 21:42 Uhr


  • Pseudocode system("man %s | grep \.h") (nur ein Scherz)

    – ti7

    1. Oktober 2018 um 21:43 Uhr

  • Was Sie möglicherweise bei Ihrer Adressübernahme gefunden haben, ist ein Stub, den der Linker verwendet, um den Aufruf in Ihrem Programm mit der Implementierung in der Bibliothek zu verbinden. Solche Stubs können für Dinge wie Umzüge, schwache Symbole und dergleichen nützlich sein. Ich kenne die unterschiedlichen Fälle nicht. Aber der Stub selbst ist im Allgemeinen kaum mehr als nur eine einfache Verzweigungsanweisung, die den Programmfluss zu seinem eigentlichen Ziel umleitet.

    – Cmaster – Wiedereinsetzung von Monica

    1. Oktober 2018 um 21:54 Uhr

  • @ti7 (und andere). Versuchen wir, Bibliotheken nicht mit Headern zu verwechseln.

    – Rici

    1. Oktober 2018 um 21:57 Uhr

  • @weather: wo sagt diese Manpage, dass printf in libc.so ist?

    – Rici

    1. Oktober 2018 um 21:58 Uhr

Benutzeravatar von Ctx
Ctx

Die beobachtete Adresse befindet sich in der Procedure Linkage Table (PLT). Dieser Mechanismus wird verwendet, wenn die Position eines externen (dynamisch gelinkten) Symbols zu dem Zeitpunkt, an dem Ihre Binärdatei kompiliert und gelinkt wird, nicht bekannt ist.

Der Zweck ist, dass die externe Verknüpfung nur an einer Stelle, dem PLT, erfolgt und nicht an allen Stellen in Ihrem Code, an denen ein Aufruf des Symbols erfolgt. Also, wenn printf() heißt, der Weg ist:

main -> printf@PLT -> printf@libc

Zur Laufzeit können Sie nicht ohne weiteres herausfinden, in welcher externen Bibliothek sich die aufgerufene Funktion befindet; Sie müssten die Opcodes am Ziel (dem PLT) parsen, das normalerweise die Adresse aus der .dynamic-Sektion holt und dorthin springt, dann schauen, wo sich das Symbol wirklich befindet, und schließlich /proc/pid/maps parsen, um es zu bekommen die externe Bibliothek.

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

(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 nicht verwenden möchten gdbdarfst du auch fragen ld.so um einige Debugging-Informationen auszugeben:

(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]

Benutzeravatar von fukanchik
fukanchik

  1. Zeiger sind printfed mit %pnicht %X:

    printf("address of printf is 0x%p\n", printf);
    
  2. Wenn Sie gegen statische libc kompilieren printf wird in Ihre Binärdatei eingebunden

  3. wenn kompiliert mit

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

    Ausgänge:

    address of printf is 0x0x7f40acb522a0
    

    was drin ist

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

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

  • Leider kann ich fPIC nicht verwenden, dies ist ein riesiges Projekt bei der Arbeit, bei dem ich die Build-Prozedur nicht ändern kann.

    – Pusch

    5. Oktober 2018 um 16:41 Uhr

Benutzeravatar von Basile Starynkevitch
Basile Starynkevitch

Angenommen, ich möchte zur Laufzeit herausfinden, wo eine Funktion “printf” definiert ist.

Allgemein und absolut gesehen können Sie das wahrscheinlich nicht (zumindest nicht einfach). Eine gegebene Funktion könnte in definiert werden mehrere Bibliotheken (z printf, das ist unwahrscheinlich; da es sich in der C-Standardbibliothek befindet).

Wenn Sie Ihr Linux-System bauen von Grund auf neukönnten Sie davon träumen, dass etwas jede Bibliothek zur Erstellungszeit verarbeitet (wenn Sie beispielsweise jede gemeinsam genutzte Bibliothek erstellen, könnten Sie alle ihre öffentlichen Namen mit erhalten nm(1) und sie in eine Datenbank einfügen). Dies ist heute noch nicht wirklich geschehen, aber einige Forschungsprojekte gehen in diese Richtung (insbesondere Softwareerbeund andere im Jahr 2019).

Übrigens, hättest du haben können mehrere Bibliotheken definieren printf. Wenn Sie beispielsweise beide GNU glibc und musl-libc auf Ihrem Computer (oder wahrscheinlicher, falls vorhanden mehrere Varianten von glibc). Es ist unwahrscheinlich, dass ein bestimmtes Programm beide verwendet (könnte aber theoretisch dlopen beide).

Vielleicht möchten Sie die Linux-spezifischen dladdr(3) Funktion. Von einer gegebenen Adresse teilt es Ihnen mit, welches gemeinsame Objekt es hat.

Die Funktion ist in meinem eigenen Programm definiert

Ja. Lesen Sie mehr darüber dynamische Verlinkung. Lesen Sie insbesondere Drepper’s So schreiben Sie gemeinsam genutzte Bibliotheken Papier. Verstehen Sie, was der Zweck der Prozedurverknüpfungstabelle ist.

Analysieren Sie die elf-Datei für die benötigten dynamisch verknüpften Bibliotheken. Dann können Sie sie analysieren und nach dem erforderlichen Symbol suchen

Benutzeravatar von KevinZ
KevinZ

Sie können dies statisch ableiten. Keine Ausführung erforderlich:

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

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

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy