Wątek przeniesiony 2020-08-09 02:25 z Edukacja przez cerrato.

segment fault po skoku warunkowym

0

Cześć
OS: Linux debian 4.19.0-9-amd64 #1 SMP Debian x86_64 GNU/Linux
Compiler: NASM version 2.14
nasm: nasm -f elf64 zabawa.asm -o zabawa.o
ld: ld zabawa.o -o zabawa

Dawno nic tu nie pisałem :).

Wydawało mi się, że dobrze zacząłem rozumieć temat wywołań i instrukcji warunkowych, ale czegoś mi brakuje :/.
Czy ktoś może mi po ludzku wyjaśnić dlaczego mam segment fault ?

section .data
msg: db 'Hello world',10
end: db 'Ending program', 10
section .text

global _start

hello_world:

        mov rax, 1
        mov rdi, 1
        mov rsi, msg
        mov rdx , 14

        syscall

        ret

exit:
        mov rax, 1
        mov rdi, 1
        mov rsi, end
        mov rdx, 20

        syscall

       ret
_start:

        mov rax, 10
        mov rdx, 10
        cmp rax, rdx 
        je hello_world
        jne exit

        call hello_world
        mov rax, 60
        xor rdi,rdi
        syscall

        call exit

output:

./zabawa
Hello world
EnSegmentation fault

Googloawem, nawet zapytałem na SO, ale tam nie wytłumaczą mi prosto ..

Problem w tym, że funkcja po dotarciu do instrukcji ret wraca pod zły adres, ale gdy próbowałem dodać to :

        push rbp < - prolog
        mov rbp, rsp <- prolog
        mov rsp,0x21 <-  prolog
       
        leave <- na koniec
       ret <- na koniec

ale efekt ciągle taki sam, chce wykonać najprostszy na świecie skok warunkowy, ale nie mam już pomysłu. Używałem ltrace/ptrace i nic.. jedyne na czym operuje to gdb z gef'em

4

w 36 linijce robisz call hello_world ta linijka powoduje, że adres następnej instrukcji tj mov rax,60 jest wrzucany na stos(najczęściej) i kod wskakuje do hello_world, coś się tam wykonuje i instrukcja ret zbiera sobie adres powrotu ze stosu i wskakuje pod ten adres czyli pod adres instrukcji mov rax,60

Jak ty wkonujesz je hello_world w 33 linijce to wskakujesz po prostu do hello_world wypisujesz 14 czy iles tam znaków czyli to Hello world\nEn ("En z Ending Program)
i instrukcja ret chce zebrać adres ze stosu żeby pod niego wskoczyć ale nie ma tam twojego adresu bo nie użyłeś call tylko jest tam jakiś śmieć i masz segmentation fault

0

Ok, ale jak nie użyje ret w funkcji hello_word to program wykonuje wszystko jak leci. Czy problem ma związek z konwencją wywołań funkcji: callee/caller ? Moze powinienem wrzucić na stos adres z rip, ale z tego co już ustaliłem nie jest to chyba najlepszy pomysł. Ale skoro podczas wywoływania funkcji adres powrotu jest wrzucany na stos, to czemu w tym przypadku tak nie jest ? coś myle, czuje to :D

3

Jak na końcu hello_world i exit masz instrukcję ret to traktuj to jako "funkcję" i wywołuj tylko używając call hello_world.
jmp, je, jne użyj do rozgałęzień w programie czyli do robienia if'ów.
Załóżmy, że chcesz uzyskać taki przebieg:

__start(){
if(rax == rdx) 
   hello_world()

exit(); 

return;
}

to funkcję hello_world() i funkcję exit() wywołujesz używając call a tego ifa tworzysz używając skoków np.

cmp rax,rdx
jne a                 <--- albo zamiast tworzenia etykiety 'a' makro, że skaczesz jedną instrukcję dalej nie wiem jak to sie robi w nasmie
call hello_world
a: call exit
ret

///edit
1* Konwencja wywołań pojawia się wtedy gdy do funkcji przekazujesz jakieś argumenty i musisz się zastanowić kto ma posprzątać po tym, albo gdzie je przekazać ty do swoich funkcji nic nie przekazujesz
2* W tym przypadku adres nie był na stosie bo instrukcja jmp to w skrócie weź adres z argumentu i daj go do rip, a instrukcja call w skrócie to: daj rip na stos czyli adres następnej instrukcji, a potem wykonaj jmp do argumentu

1 użytkowników online, w tym zalogowanych: 0, gości: 1