Во время моего знакомства с языком ассемблера я столкнулся с этим странным поведением, связанным с инструкцией mov
.
Это непреднамеренное поведение состоит в том, что адрес, который я намеревался загрузить, изменяется на эквивалентную инструкцию с другим адресом.
Благодаря трюкам, предложенным другими пользователями StackOverflow ( Вызов функции по ее адресу в памяти в c / c ++ | Использование функции __builtin_extract_return_addr () для поиска RSP значение инструкции ret )
Мне удалось создать простой код (это псевдокод) для выполнения теста загрузки / сравнения адресов:
typedef void function(void);
uint64_t *sp;
asm ("movq %%rsp, %0\n"
: "=r" (sp) : );//:
uint64_t *ret_addr;
ret_addr = __builtin_extract_return_addr((void *) *((long *)sp) + 1);
if (ret_addr == 0x40ac45)
{
printf("WHY:\n");
}
function* test_addr = (function*)0x40ac6b;
asm goto (
"cmp %0, %1\n"
"jne %l[L2]\n"
: // output operands
: "r"(ret_addr), "r"(test_addr) // input operands
:
: L2);
L1: int3();
L2: no_problem();
Подводя итог, я получаю обратный адрес (ret_addr
) инструкции. Затем, если этот адрес равен 0x40ac45, программа выведет «ПОЧЕМУ:», а затем сравнит этот адрес с test_addr
(0x40ac6b). Если эти адреса не равны, тогда функция goto no_problem
, в противном случае я выполню функцию int3
для прерывания.
Как показано здесь более подробно, ошибка в том, что, хотя RET Addr (0x40ac45)! = TEST Addr (0x40ac6b), программа выполняет ловушку трассировки, которая должна происходить только тогда, когда они равны .
Для отладки, Я добавил следующий код для загрузки ret_addr (который должен быть 0x40ac45) в один из пустых регистров (%% r14):
asm volatile (
"mov %0, %%r14\n\t" : : "r"(ret_addr)
);
После запуска GDB я обнаружил, что в %% r14 , вместо предполагаемого адреса 0x40ac45 загружается 0x40ac6b , как вы можете видеть здесь .
Хотя все предыдущие проверки работоспособности были пройдены, чтобы показать, что адрес возврата - 0x40ac45, для по какой-то причине, когда я использую инструкцию mov
, вместо этого загружается 0x40ac6b.
Я провел дополнительный поиск, есть ли у этих двух адресов что-то общее, и при разборке обнаружил следующее:
000000000040ac30 <close_stdout>:
----------------
40ac45: 85 c0 test %eax,%eax
----------------
40ac6b: 85 c0 test %eax,%eax
Это одна и та же инструкция, только что загруженная по другому адресу.
Что в конечном итоге приводит меня к следующим вопросам:
- Что может быть причиной того, что это происходит ? Это связано с тем, что эти два адреса имеют эквивалентные инструкции?
- Подходит ли инструкция
mov
для такого рода использования? Я пробовал использовать команду lea
, но, к сожалению, это не сработало ("lea (%%r14), %0"
), поскольку регистр r14 не получил значение адреса. - Какие еще проверки работоспособности я могу сделать, чтобы убедиться, что я загружаю правильное значение адреса? Кажется, мой код работает для всех остальных инструкций, только эта конкретная доставляет мне проблемы.
- Есть ли способ «принудительно» загрузить абсолютный адрес, не прибегая к жесткому кодированию значения адреса? (например,
function* ret_addr = (function*)0x40ac45;
)
Прошу прощения за пространное объяснение (я постарался сделать его как можно короче) и благодарю вас за любые предложения.
С уважением,