Ich studiere die ELF-Spezifikation ( http://www.skyfree.org/linux/references/ELF_Format.pdf ) und ein Punkt, der mir über den Programmladeprozess nicht klar ist, ist, wie und was der Stapel initialisiert wird Die anfängliche Seitengröße ist. Hier ist der Test (unter Ubuntu x86-64):
$ cat test.s
.text
.global _start
_start:
mov $0x3c,%eax
mov $0,%edi
syscall
$ as test.s -o test.o && ld test.o
$ gdb a.out -q
Reading symbols from a.out...(no debugging symbols found)...done.
(gdb) b _start
Breakpoint 1 at 0x400078
(gdb) run
Starting program: ~/a.out
Breakpoint 1, 0x0000000000400078 in _start ()
(gdb) print $sp
$1 = (void *) 0x7fffffffdf00
(gdb) info proc map
process 20062
Mapped address spaces:
Start Addr End Addr Size Offset objfile
0x400000 0x401000 0x1000 0x0 ~/a.out
0x7ffff7ffa000 0x7ffff7ffd000 0x3000 0x0 [vvar]
0x7ffff7ffd000 0x7ffff7fff000 0x2000 0x0 [vdso]
0x7ffffffde000 0x7ffffffff000 0x21000 0x0 [stack]
0xffffffffff600000 0xffffffffff601000 0x1000 0x0 [vsyscall]
Die ELF-Spezifikation hat sehr wenig darüber zu sagen, wie oder warum diese Stapelseite überhaupt existiert, aber ich kann Referenzen finden, die besagen, dass der Stapel mit SP initialisiert werden sollte, das auf argc zeigt, mit argv, envp und dem Hilfsvektor direkt darüber das, und ich habe dies bestätigt. Aber wie viel Platz ist unter SP verfügbar? Auf meinem System sind 0x1FF00
Bytes unterhalb von SP zugeordnet, aber vermutlich zählt dies von der Oberseite des Stapels bei herunter 0x7ffffffff000
, und es gibt 0x21000
Bytes in der vollständigen Zuordnung. Was beeinflusst diese Zahl?
Mir ist bewusst, dass die Seite direkt unter dem Stapel eine "Schutzseite" ist, die automatisch beschreibbar wird und "den Stapel verkleinert", wenn ich darauf schreibe (vermutlich, damit die naive Stapelbehandlung "nur funktioniert"), aber wenn ich eine zuweise Ein riesiger Stapelrahmen, dann könnte ich die Schutzseite und den Segfault überschreiten. Daher möchte ich feststellen, wie viel Speicherplatz mir bereits zu Beginn des Prozesses ordnungsgemäß zugewiesen wurde.
EDIT : Einige weitere Daten machen mich noch unsicherer, was los ist. Der Test ist der folgende:
.text
.global _start
_start:
subq $0x7fe000,%rsp
movq $1,(%rsp)
mov $0x3c,%eax
mov $0,%edi
syscall
Ich habe hier mit verschiedenen Werten der Konstante gespielt 0x7fe000
, um zu sehen, was passiert, und für diesen Wert ist es nicht deterministisch, ob ich einen Segfault bekomme oder nicht. Laut GDB wird die subq
Anweisung allein die Größe der mmap erweitern, was für mich mysteriös ist (woher weiß Linux, was sich in meinem Register befindet?), Aber dieses Programm stürzt GDB normalerweise beim Beenden aus irgendeinem Grund ab. Es kann nicht ASLR sein, das den Nichtdeterminismus verursacht, weil ich keinen GOT- oder PLT-Abschnitt verwende. Die ausführbare Datei wird jedes Mal an denselben Stellen im virtuellen Speicher geladen. Ist dies also eine Zufälligkeit der PID oder des physischen Gedächtnisses? Alles in allem bin ich sehr verwirrt darüber, wie viel Stapel tatsächlich legal für den wahlfreien Zugriff verfügbar ist und wie viel beim Ändern des RSP oder beim Schreiben in Bereiche "nur außerhalb des Bereichs" angefordert wird.
readelf
und objdump
waren zu diesem Zweck sehr nützlich. Dies ist nur ein letztes Stück unterbestimmtes Verhalten, das ich hoffentlich regeln kann. (Anders ausgedrückt, mein ultimatives Ziel ist nicht "Ich frage mich, was unter der Haube vor sich geht", sondern "Ich möchte eine genaue Beschreibung, welche Bytes bei der folgenden Eingabe wohin gehen")