Толкни и вставь в нос со стеком

Я делаю учебные уроки и кое-что здесь в коде, я не понимаю, может кто-нибудь может мне помочь? я буду признателен за вашу помощь.

сначала будет вызвана функция 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 работает).

Другие вопросы по тегам