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

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

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

call dword ptr[123]

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

ret 0004

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

mov AX, 0004

(вместе с необходимыми NOP) программа вылетает.

4 ответа

Решение

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

RET immediate

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

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

Поэтому, когда вы печатаете:

ret 0004

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

push eax
call dword ptr[123]

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

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

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

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

mov eax, 4
ret

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

Например, в соглашении о вызовах C возвращаемое значение будет в EAX/AX/AL. Ваша одиночная инструкция не имеет таковой: это пустая функция, принимающая около 4 байтов параметров (возможно, один int), которая ничего не делает. Так как в этом соглашении о вызовах обязан вызываемый объект очистить стек, игнорирование этого и замена вызова на "mov ax" не работает.

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

// 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, если перед вызовом процедуры мы поместили аргументы в стек.


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

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

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