2016-01-31 16 views
6

ich die syscall Aufruf im letzten libc seziert:Linux syscall, libc, VDSO und Implementierung Dissektion

git clone git://sourceware.org/git/glibc.git 

Und ich habe diesen Code in sysdeps/Unix/sysv/linux/i386/sysdep.h:

# define INTERNAL_SYSCALL_MAIN_INLINE(name, err, nr, args...) \ 
LOADREGS_##nr(args)       \ 
asm volatile (       \ 
"call *%%gs:%P2"       \ 
: "=a" (resultvar)       \ 
: "a" (__NR_##name), "i" (offsetof (tcbhead_t, sysinfo))  \ 
    ASMARGS_##nr(args) : "memory", "cc") 

Wenn ich auch diesen Code zu verstehen, die LOADREGS _ ## nr (args) Makro lädt das Argument in der EBX-Register, ECX, EDX, esi, EDX und EBP.

sysdeps/Unix/sysv/linux/i386/sysdep.h

# define LOADREGS_0() 
# define ASMARGS_0() 
# define LOADREGS_1(arg1) \ 
    LOADREGS_0() 
# define ASMARGS_1(arg1) \ 
    ASMARGS_0(), "b" ((unsigned int) (arg1)) 
# define LOADREGS_2(arg1, arg2) \ 
    LOADREGS_1 (arg1) 
# define ASMARGS_2(arg1, arg2) \ 
    ASMARGS_1 (arg1), "c" ((unsigned int) (arg2)) 
# define LOADREGS_3(arg1, arg2, arg3) \ 
    LOADREGS_2 (arg1, arg2) 
# define ASMARGS_3(arg1, arg2, arg3) \ 
    ASMARGS_2 (arg1, arg2), "d" ((unsigned int) (arg3)) 
# define LOADREGS_4(arg1, arg2, arg3, arg4) \ 
    LOADREGS_3 (arg1, arg2, arg3) 
# define ASMARGS_4(arg1, arg2, arg3, arg4) \ 
    ASMARGS_3 (arg1, arg2, arg3), "S" ((unsigned int) (arg4)) 
# define LOADREGS_5(arg1, arg2, arg3, arg4, arg5) \ 
    LOADREGS_4 (arg1, arg2, arg3, arg4) 
# define ASMARGS_5(arg1, arg2, arg3, arg4, arg5) \ 
    ASMARGS_4 (arg1, arg2, arg3, arg4), "D" ((unsigned int) (arg5)) 
# define LOADREGS_6(arg1, arg2, arg3, arg4, arg5, arg6) \ 
    register unsigned int _a6 asm ("ebp") = (unsigned int) (arg6); \ 
    LOADREGS_5 (arg1, arg2, arg3, arg4, arg5) 
# define ASMARGS_6(arg1, arg2, arg3, arg4, arg5, arg6) \ 
    ASMARGS_5 (arg1, arg2, arg3, arg4, arg5), "r" (_a6) 
#endif /* GCC 5 */ 
    enter code here 

Wo ist der Code, der das Argument in den Registern ebx laden, ECX, EDX, esi, EDX und EBP? es ist dieser Code oben? Ich verstehe die Implementierung nicht. der folgende Code lädt das 6. Argument im ebx-Register?

register unsigned int _a6 asm ("ebp") = (unsigned int) (arg6); 

Was diesen Code tut:

ASMARGS_0(), "b" ((unsigned int) (arg1)) 

Es lädt das erste Argument in der EBX registrieren?

Dann springt der "Aufruf * %% gs:% P2" zum VDSO-Code? dieser Code entspricht "call * gs: 0x10"?

so, das folgende Diagramm für den Schreib syscall, ist es gut ?:

write(1, "A", 1) -----> LIBC -----> VDSO -----> KERNEL 
          load reg   ? 
         jump to vdso 
|---------------------------------------------------|--------------| 
     user land          kernel land 

ich nicht das VDSO Dienstprogramm nicht verstehe! vdso wählt die Methode syscall (sysenter oder int 0x80).

Vielen Dank im Voraus für Ihre Hilfe. Und tut mir leid, mein Englisch ist sehr schlecht.

+0

Die Glibc ist aufgrund ihrer verschachtelten Abstraktionsschicht extrem kompliziert. Ich empfehle Ihnen, zuerst eine einfachere libc zu betrachten. – fuz

+0

ist sehr einfach zu verstehen, eine einfache libc, die syscall Argumente sind in den Registern gespeichert und die Int 0x80 oder sysenter Anweisung wird ausgeführt, um im Kernel-Modus zu drehen. – tutuen

+0

@tuutuen suchst du jemanden, der das VDSO oder die Methode erklärt, mit der glibc damit interagiert? Wenn Sie nur eine Erklärung des VDSO wünschen, wäre das einfacher bereitzustellen. –

Antwort

2

Die Makros, die an den syscalls von glibc beteiligt sind, werden für das Beispiel des exit syscall wie folgt erweitert.

LOADREGS_1(args) 
asm volatile (
"call *%%gs:%P2" 
: "=a" (resultvar) 
: "a" (__NR_exit), "i" (offsetof (tcbhead_t, sysinfo)) 
    ASMARGS_1(args) : "memory", "cc") 

LOADREGS_1(args) wird LOADREGS_0(), erweitern die zu nichts erweitern - LOADREGS_*(...) brauchen nur Register einzustellen, wenn mehrere Parameter zur Verfügung gestellt werden.

ASMARGS_1(args) wird erweitert auf ASMARGS_0(), "b" ((unsigned int) (arg1)), die auf , "b" ((unsigned int) (arg1) erweitern wird.

__NR_exit ist 1 auf x86.

Als solches wird der Code so etwas wie erweitern:

asm volatile (
"call *%%gs:%P2" 
: "=a" (resultvar) 
: "a" (1), "i" (offsetof (tcbhead_t, sysinfo)) 
, "b" ((unsigned int) (arg1) : "memory", "cc") 

ASMARGS_* nicht tatsächlich Code ausführen per se - sie sind Anweisungen gcc, um sicherzustellen, dass bestimmte Werte (wie (unsigned int) (arg1)) sind in bestimmten Registern (wie b, auch bekannt als ebx10). Die Kombination von Parametern zu asm volatile (die natürlich keine Funktion ist, aber nur ein GCC-Built-in), geben einfach an, wie gcc auf den Systemaufruf vorbereiten sollte und wie es fortgesetzt werden soll, nachdem der Syscall abgeschlossen ist.

Nun wird die generierte Assembly wie folgt aussehen:

; set up other registers... 
movl $1, %eax 
call *%gs:0x10 
; tear down 

%gs ist ein Segment, dass Verweise lokalen Thread-Speicherregister - Insbesondere wird glibc einen gespeicherten Wert verweisen, die dem VDSO Punkte, die es dort gespeichert, als es zuerst die ELF-Header analysierte, die ihm mitteilten, wo das VDSO war.

Sobald der Code in das VDSO eingegeben wird, wissen wir nicht genau, was passiert - es hängt von der Kernel-Version ab - aber wir wissen, dass es den effizientesten verfügbaren Mechanismus verwendet, um einen Syscall auszuführen, wie die sysenter Anweisung oder die int 0x80 Anweisung.

Also, ja, ist das Diagramm genau:

write(1, "A", 1) -----> LIBC -----> VDSO -----> KERNEL 
          load reg   ? 
         jump to vdso 
|---------------------------------------------------|--------------| 
     user land          kernel land 

hier ein einfacheres Beispiel von Code in das VDSO zu nennen, die speziell für Ein-Parameter syscalls, aus einer Bibliothek, die ich libsyscall genannt maintain:

Dies verschiebt einfach Parameter aus dem Stapel in Register, ruft über einen aus dem Speicher geladenen Zeiger in das VDSO auf, stellt die anderen Register in ihren vorherigen Zustand zurück und gibt das Ergebnis des Systemaufrufs zurück.