2016-09-18 1 views
-1

Ich habe versucht, Code nicht in der Hauptfunktion zu setzen, sondern direkt in _start:Warum wird von _start segfault zurückgegeben?

segment .text 
    global _start 
_start: 
    push rbp 
    mov rbp, rsp 
    ; ... program logic ... 
    leave 
    ret 

Compile:

yasm -f elf64 main.s 
ld -o main main.o 

Run:

./main 
Segmentation fault(core dumped) 

ich lesen, lassen ist

mov esp,ebp 
pop ebp 

Aber warum ist ein solcher Epilog zum Pop-Stack-Frame und der Zeiger zum Basis-Frame auf die Basis eines vorherigen Frames zu einem Segmentierungsfehler?

In der Tat wird ein Exit-System-Aufruf ordnungsgemäß beendet.

+2

'_start' wird nicht vom Kernel aufgerufen, Sie können nicht von ihm zurückkehren. –

+0

Könnten Sie bitte mehr ausarbeiten? Ich dachte, es sei Routine. Wie sollte man von dort richtig "zurückkommen"? Über den Exit-Systemanruf?Schreibe als Antwort. –

Antwort

3

Per ABI den Stapel am Eingang auf _start ist

Stack at entry on _start

Es gibt keine "Absenderadresse".
Der einzige Weg, um einen Prozess zu beenden, ist durch SYS_EXIT

xorl %edi, %edi ;Error code 
movl $60, %eax ;SYS_EXIT 
syscall 

Abschnitt 3.4.1 Anfängliche Stapel und Register Staat.

+0

Das beantwortet meine Frage. –

3

Die LEAVE-Anweisung ist so definiert, dass keine Ausnahmen verursacht werden, daher kann sie nicht die Quelle Ihrer Fehler sein. Sie sollten GDB verwenden. Debugger sind bei der Lösung dieser Art von Problemen von unschätzbarem Wert.

Dies ist, was passiert:

gdb $ ./main
[...]
Programmsignal SIGSEGV empfangen, Fehlersegmentierung.
0x0000000000000001 in ??()

(GDB) x/gx $ rsp-8
0x7fffffffe650: 0x0000000000000001

also höchstwahrscheinlich Ihr Programm lief bis zur Fertigstellung, aber das erste, was auf dem Stapel, die 0x0000000000000001. RET popped das in das RIP Register, und dann segfaulted, weil diese Adresse nicht zugeordnet ist.

Ich schreibe nicht viel Code unter Linux, aber ich würde wetten, dass _start erforderlich ist, um den Exit-Systemaufruf zu verwenden. Der einzige Weg, wie Sie möglicherweise zu einer nützlichen Adresse zurückkehren können, ist, wenn der Kernel eine Funktion irgendwo hinstellt, die das für Sie tun würde.

+0

Ja, bevor Frage zu stellen habe ich gdb und zeigen '6 leave (GDB) n _start() bei main.s: 7 7 ret (GDB) n 0x0000000000000001 in ??() ' Post nicht, weil ich nicht weiß, was" ?? " meint. –

+0

Ich denke auch über Rip zu nicht zugeordneten Speicherort im Speicher zu setzen, aber es wäre schön, genau zu wissen. –

+0

Es bedeutet, dass die Adresse nicht in einem gültigen Modul ist. Da GDB nicht weiß, zu welchem ​​Modul es gehört, gibt es "??" aus. Der Schlüssel, den Sie hier beachten müssen, ist, dass 0x0000000000000001 keine gültige, ausführbare Adresse ist. Wenn RIP auf eine ungültige oder nicht ausführbare Adresse zeigt, wird es normalerweise entweder von einer RET-Adresse an eine ungültige Adresse oder von einem indirekten JMP oder von CALL an eine ungültige Adresse verursacht. Wenn ein RET, wie es hier der Fall ist, die 8 Bytes unter der Spitze des Stapels RIP übereinstimmen. Bei einem JMP oder CALL stimmt manchmal ein Register mit RIP überein. – icecreamsword

Verwandte Themen