Minimal lauffähiges Beispiel
Wenn ein Konzept nicht klar ist, gibt es ein einfacheres Beispiel, das Sie nicht gesehen haben und das es erklärt.
In diesem Fall ist dieses Beispiel die freistehende (keine libc) Hallo-Welt der Linux x86_64-Assembly:
hallo.S
.text
.global _start
_start:
/* write */
mov $1, %rax /* syscall number */
mov $1, %rdi /* stdout */
mov $msg, %rsi /* buffer */
mov $len, %rdx /* buffer len */
syscall
/* exit */
mov $60, %rax /* exit status */
mov $0, %rdi /* syscall number */
syscall
msg:
.ascii "hello\n"
len = . - msg
GitHub stromaufwärts .
Zusammenbauen und ausführen:
as -o hello.o hello.S
ld -o hello.out hello.o
./hello.out
Gibt das erwartete aus:
hello
Verwenden wir nun Strace für dieses Beispiel:
env -i ASDF=qwer strace -o strace.log -s999 -v ./hello.out arg0 arg1
cat strace.log
Wir gebrauchen:
strace.log enthält jetzt:
execve("./hello.out", ["./hello.out", "arg0", "arg1"], ["ASDF=qwer"]) = 0
write(1, "hello\n", 6) = 6
exit(0) = ?
+++ exited with 0 +++
Bei einem so minimalen Beispiel ist jedes einzelne Zeichen der Ausgabe selbstverständlich:
execveZeile: Zeigt an, wie sie straceausgeführt wird hello.out, einschließlich der CLI-Argumente und der Umgebung, wie unter dokumentiertman execve
writeZeile: Zeigt den Schreibsystemaufruf an, den wir durchgeführt haben. 6ist die Länge der Zeichenfolge "hello\n".
= 6ist der Rückgabewert des Systemaufrufs, der, wie in dokumentiert, man 2 writedie Anzahl der geschriebenen Bytes ist.
exitZeile: Zeigt den von uns getätigten Aufruf des Exit-Systems an. Es gibt keinen Rückgabewert, da das Programm beendet wurde!
Komplexere Beispiele
Die Anwendung von strace dient natürlich dazu, festzustellen, welche Systemaufrufe komplexe Programme tatsächlich ausführen, um das Debuggen / Optimieren Ihres Programms zu unterstützen.
Insbesondere haben die meisten Systemaufrufe, die unter Linux wahrscheinlich auftreten, Glibc-Wrapper, viele davon von POSIX .
Intern verwenden die glibc-Wrapper die Inline-Assembly mehr oder weniger folgendermaßen: Wie rufe ich einen Systemaufruf über sysenter in der Inline-Assembly auf?
Das nächste Beispiel, das Sie studieren sollten, ist eine POSIX- writeHallo-Welt:
Haupt c
#define _XOPEN_SOURCE 700
#include <unistd.h>
int main(void) {
char *msg = "hello\n";
write(1, msg, 6);
return 0;
}
Kompilieren und ausführen:
gcc -std=c99 -Wall -Wextra -pedantic -o main.out main.c
./main.out
Dieses Mal werden Sie sehen, dass eine Reihe von Systemaufrufen von glibc ausgeführt werden, bevor maineine schöne Umgebung für main eingerichtet wird.
Dies liegt daran, dass wir jetzt kein freistehendes Programm verwenden, sondern ein allgemeineres glibc-Programm, das libc-Funktionen ermöglicht.
Dann strace.logenthält an jedem Ende :
write(1, "hello\n", 6) = 6
exit_group(0) = ?
+++ exited with 0 +++
Wir schließen daraus, dass die writePOSIX-Funktion überraschenderweise den Linux write-Systemaufruf verwendet.
Wir beobachten auch, dass dies return 0zu einem exit_groupAnruf statt führt exit. Ha, ich wusste nichts davon! Deshalb straceist es so cool. man exit_groupdann erklärt:
Dieser Systemaufruf entspricht exit (2), außer dass nicht nur der aufrufende Thread, sondern alle Threads in der Thread-Gruppe des aufrufenden Prozesses beendet werden.
Und hier ist ein weiteres Beispiel, in dem ich untersucht habe, welche Systemaufrufe dlopenverwendet werden: /unix/226524/what-system-call-is-used-to-load-libraries-in-linux/462710#462710
Getestet in Ubuntu 16.04, GCC 6.4.0, Linux Kernel 4.4.0.