#assembly #x86 #nasm
#сборка #x86 #nasm
Вопрос:
Итак, я недавно снова перешел на nasm и попытался создать некоторые базовые вещи (putc и puts).
Putc
работает нормально, но проблема в том, что после того, как я вызываю putc
in puts
, ret
in putc
не возвращается к ip
отправленному в стек by puts
, и поэтому больше никаких инструкций не выполняется puts
(отлажена эта часть в gdb).
msg db "Welcome!", 0ah, 0dh, 0h
putc:
push ebp
mov esp, ebp
mov ah, 0ah
; al is set before
mov bh, 0
mov bl, 0
mov cx, 1
int 10h
ret
puts:
push ebp
mov esp, ebp
mov ebx, msg
dec ebx
puts_l:
inc ebx
mov al, [ebx]
call putc
cmp al, 0h
jne puts_l
ret
Это явно не самое лучшее, но я думаю, что у меня где-то недоразумение. Я мог бы представить, что регистр будет перезаписан putc, но это не объясняет, почему ret
in putc не возвращается к puts
Я также должен упомянуть, что я работаю с x86.
Комментарии:
1. Возможно, вы нажимаете на ebp слишком часто. Это может несколько испортить ожидание того,
ret
чтоip
это следующий элемент в стеке2. В этом действительно может быть какой-то смысл. Итак, что мне делать, если я хочу создать фреймы стека? Это все, что я знаю, как это сделать. Я думал, что ret сделает всю работу за меня, чтобы очистить фрейм стека. Если нет, то мне, возможно, придется воспользоваться
leave
.3.
ret
это просто то, как пишется x86pop ip
(илиpop eip
в 32-битном коде). Он волшебным образом не знает, куда возвращаться, [E / R] SP уже должен указывать на обратный адрес.4. Да, но не следует вызывать push текущий ip, а затем изменять ip на новый адрес? Вот что меня немного смущает в вашем комментарии.
5. Да,
call
выталкивает текущийeip
(так называемый обратный адрес ) и переходит к вызываемому адресу, но первое, что на этом адресе вашеpush ebp
, поэтому, когда вы это делаетеret
,eip
загружается из этого pushedebp
вместо ожидаемого обратного адреса над ним.
Ответ №1:
Когда вы это сделаете
push ebp
mov esp, ebp
esp
больше не указывает на отправленный ebp
и обратный адрес, это проблема, которую нельзя решить, используя LEAVE в функции epilogue . BTW LEAVE
должен быть сопряжен с ENTER.
Если вам действительно нужен фрейм стека (например, для определения локальной переменной памяти), скелет может выглядеть следующим образом:
Function:
PUSH EBP
MOV EBP,ESP
SUB ESP,4 ; The local variable is now addressable as [ESP] alias [EBP-4].
; Here is the Function body which can use local variable(s).
MOV ESP,EBP ; Discard local variable, ESP will point to the saved EBP.
POP EBP ; Restore EBP which might be used as parent's frame pointer.
RET
Поскольку ваша программа не использует локальные переменные, это может быть
mov esi, msg ; Address of the ASCIIZ string.
call puts
jmp $ ; Program ends here.
puts: ; Function which displays ASCIIZ string at ESI.
lodsb ; Load AL from [DS:ESI], increment ESI.
cmp al, 0h
je puts_2
call putc ; Display a nonzero character in AL.
jmp puts
puts_2: ret
putc: ; Function which displays a character in AL.
mov ah, 0ah ; WRITE CHARACTER.
mov bh, 0 ; Videopage 0.
mov bl, 0 ; Colour in graphic mode.
mov cx, 1 ; Number of times to write character
int 10h ; Invoke BIOS videofunction.
ret
msg db "Welcome!", 0ah, 0dh, 0h ; The displayed ASCIIZ string.