Толкни и вставь в нос со стеком
Я делаю учебные уроки и кое-что здесь в коде, я не понимаю, может кто-нибудь может мне помочь? я буду признателен за вашу помощь.
сначала будет вызвана функция sprint, а в функции 'sprint' шаг за шагом будут выталкиваться в стек edx, ecx, ebx, eax, а затем будет вызвана функция 'slen', а 'ebx' будет снова помещенный в стек, я не понимаю этот шаг, ebx уже находится в стеке, так как я знаю, что 'ebx' теперь является вторым последним в стеке после вызова eax после вызова функции sprint. мне интересно, есть ли здесь 2 стека? или нет Может ли кто-нибудь объяснить, пожалуйста, для меня? Я был бы очень благодарен.
С наилучшими пожеланиями
functions.asm
;------------------------------------------
; int slen(String message)
; String length calculation function
slen:
push ebx
mov ebx, eax
nextchar:
cmp byte [eax], 0
jz finished
inc eax
jmp nextchar
finished:
sub eax, ebx
pop ebx
ret
;------------------------------------------
; void sprint(String message)
; String printing function
sprint:
push edx
push ecx
push ebx
push eax
call slen
mov edx, eax
pop eax
mov ecx, eax
mov ebx, 1
mov eax, 4
int 80h
pop ebx
pop ecx
pop edx
ret
;------------------------------------------
; void exit()
; Exit program and restore resources
quit:
mov ebx, 0
mov eax, 1
int 80h
ret '
Цитата helloworld-inc.asm
; Hello World Program (External file include)
; Compile with: nasm -f elf helloworld-inc.asm
; Link with (64 bit systems require elf_i386 option): ld -m elf_i386 helloworld-inc.o -o helloworld-inc
; Run with: ./helloworld-inc
%include 'functions.asm' ; include our external file
SECTION .data
msg1 db 'Hello, brave new world!', 0Ah ; our first message string
msg2 db 'This is how we recycle in NASM.', 0Ah ; our second message string
SECTION .text
global _start
_start:
mov eax, msg1 ; move the address of our first message string into EAX
call sprint ; call our string printing function
mov eax, msg2 ; move the address of our second message string into EAX
call sprint ; call our string printing function
call quit ; call our quit function
1 ответ
Эти две функции независимы (обе предназначены для использования в качестве общедоступных функций API). Это просто совпадение, что sprint
звонки slen
внутренне slen
не могу предположить, что это было вызвано из sprint
и ebx
он уже сохранен, он может быть вызван как непосредственно из кода пользователя, где ebx
может не храниться в стеке.
Таким образом, обе функции следуют соглашению о вызовах (то, которое выбрал автор кода, я не знаю их по голове, и я не хочу догадываться, что это такое, но если вы работаете в linux и собираете elf32, это, вероятно, стандартный linux 32b соглашение о вызовах (неверное предположение, похоже на Irvine32 соглашение о вызовах lib, сохраняющее все регистры, кроме eax
который может вернуть значение, спасибо Peter Cordes за комментарии)). Это означает, что обе функции независимо должны сохранять некоторые регистры, чтобы соответствовать соглашению, и только некоторые регистры могут быть свободно изменены и возвращены в измененном состоянии.
поскольку я знаю, что 'ebx' теперь является вторым последним в стеке после eax после вызова функции sprint
И это тоже не правда. "Стек" - это обычная компьютерная память, такая же, как ваша .data
а также .bss
разделы. Это просто еще одна зарезервированная память для вашего процесса приложения. Что делает его несколько особенным, так это значение в регистре esp
, который указывает на "вершину стека".
Теперь, когда вы делаете push ebx
, инструкция запишет 32-битное значение в регистр ebx
в память компьютера по адресу esp-4
(32 бита = 4 байта, поэтому push
перемещает указатель стека на -4 в режиме 32b), а также обновляет esp
указать на это место (т.е. esp -= 4
).
Одно из ваших заблуждений - "ebx сохранен". Если вы перечитаете это описание выше, вы можете заметить, что в памяти стека нет информации, что значение получено из ebx
, И действительно, если вы будете выполнять в качестве следующей инструкции pop eax
, это значение будет восстановлено в регистр eax
без каких-либо проблем, а esp += 4
, вызывая в целом такой же эффект как mov eax,ebx
, но через стек памяти (намного медленнее, чем прямой mov
).
Еще одно заблуждение - "второй последний после eax". call
сама инструкция помещает адрес возврата в стек, поэтому внутри slen
после push ebx
стек содержит значения: "ebx", адрес возврата в спринт при следующей инструкции после call slen
, "eax", "ebx".
Не стесняйтесь использовать справочник инструкций, чтобы проверить, что именно делает конкретная инструкция, например:
push
pop
call
ret
Даже опытный программист asm часто проверяет любые предположения о конкретной инструкции, особенно когда задействованы флаги, или неявное использование регистра, как с mul/div/lods/stos/xlatb/...
, Не угадывайте, просто по названию инструкции, некоторые из них намного сложнее, чем ожидал бы здравый смысл.
Кроме того, намного, намного проще просто запустить этот код в отладчике, перешагнуть через инструкции и посмотреть, как esp
и содержимое стека памяти развивается (что бы очистить вторую часть этого ответа о том, как именно push/pop
работает).