Непредвиденное поведение инструкции "mov" (загружает неправильный адрес) - PullRequest
0 голосов
/ 20 июня 2020

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

Это одна и та же инструкция, только что загруженная по другому адресу.

Что в конечном итоге приводит меня к следующим вопросам:

  1. Что может быть причиной того, что это происходит ? Это связано с тем, что эти два адреса имеют эквивалентные инструкции?
  2. Подходит ли инструкция mov для такого рода использования? Я пробовал использовать команду lea, но, к сожалению, это не сработало ("lea (%%r14), %0"), поскольку регистр r14 не получил значение адреса.
  3. Какие еще проверки работоспособности я могу сделать, чтобы убедиться, что я загружаю правильное значение адреса? Кажется, мой код работает для всех остальных инструкций, только эта конкретная доставляет мне проблемы.
  4. Есть ли способ «принудительно» загрузить абсолютный адрес, не прибегая к жесткому кодированию значения адреса? (например, function* ret_addr = (function*)0x40ac45;)

Прошу прощения за пространное объяснение (я постарался сделать его как можно короче) и благодарю вас за любые предложения.

С уважением,

...