как работают вызовы процедур на ассемблере?

Я только начал возиться с ASM, и я не уверен, правильно ли я понимаю вызовы процедур.

скажем, в какой-то момент в коде есть вызов процедуры

call dword ptr[123]

и процедура состоит всего из одной команды, ret:

ret 0004

каков будет эффект вызова этой процедуры и где будет храниться возвращаемое значение? Я где-то читал, что в AX будет храниться возвращаемое значение в 2 байта, но когда я заменяю вызов процедуры на

mov AX, 0004

(вместе с нужными NOP'ами) программа вылетает.


person int3    schedule 09.08.2009    source источник


Ответы (4)


в ассемблере x86 параметр инструкции ret означает:

RET immediate

Вернитесь к вызывающей процедуре и извлеките немедленные байты из стека.

(цитата из Руководства разработчика программного обеспечения для архитектур Intel® 64 и IA-32 Том 2B)

Итак, когда вы вводите:

ret 0004

Вы говорите процессору вернуться к инструкции сразу после call и удалить 4 байта из стека. Это замечательно, если вы поместили 4 байта в стек перед вызовом.

push eax
call dword ptr[123]

Обратите внимание, что это не имеет ничего общего с возвращаемым значением. На самом деле процедура в ассемблере не имеет возможности указать, что значение является возвращаемым значением. Все это делается по соглашению. Большинство компиляторов, о которых я знаю, будут использовать EAX для хранения возвращаемого значения, но это верно только потому, что вызывающая функция будет ожидать результат там.

Таким образом, ваш код вызова будет:

call dword ptr [123]
mov dword ptr [result], eax

и ваша функция, которая возвращает значение 4, будет:

mov eax, 4
ret
person Nathan Fellman    schedule 09.08.2009
comment
Благодарность! большинство руководств, которые я искал в Google, просто использовали ret и не объясняли операцию ret ‹i›immediate‹/i›. Я еще больше запутался, когда ассемблер LLVM, по-видимому, имел команду ret ‹type› ‹value›. - person int3; 09.08.2009
comment
есть ли какая-то причина, по которой RET может вернуться в какое-то другое место, кроме призвания? Я только что создал вопрос и нашел его в связанных вопросах - person lisovaccaro; 30.06.2013
comment
да, если вы манипулировали адресом возврата, который CALL поместил в стек. - person Nathan Fellman; 30.06.2013

Все зависит от используемого соглашения о вызовах. Я не буду здесь повторять статью из Википедии, просто прочитаю определение.

Например, в соглашении о вызовах языка C возвращаемое значение будет в формате EAX/AX. /АЛ. В вашей одиночной инструкции ее нет: это функция void, принимающая около 4 байтов параметров (возможно, один int), которая ничего не делает. Поскольку в этом соглашении о вызовах обязанностью вызываемого является очистка стека, игнорирование этого и замена вызова на «mov axe» не работает.

Также я подозреваю, что вы можете возиться с 32-битной сборкой, читая 16-битный документ. Это не большая проблема, но вы должны знать о различиях.

person aib    schedule 09.08.2009

// possibly there are arguments pushed here
...
call dword ptr[123] // push next OP code offset in the stack and jump to procedure

// procedure
...
ret 0004 // pop offset, set EIP to that offset and decrease ESP by 4

мы дополнительно уменьшаем ESP, если перед вызовом процедуры мы помещали аргументы в стек.


Если есть переданные аргументы, ваша программа аварийно завершает работу, потому что вы их не извлекаете. Смещение возврата для текущей процедуры будет неправильным, поскольку в качестве смещения она получит значение из одного из переданных аргументов.

person Nick Dandoulakis    schedule 09.08.2009

Я не думаю, что возвращаемое значение хранится в регистре AX

person A.Rashad    schedule 09.08.2009
comment
@int3: это зависит от соглашения о вызовах. Здесь нет ничего автоматического. Вызывающая функция и вызываемая функция должны договориться о том, где находится возвращаемое значение. - person Nathan Fellman; 30.06.2013