GCC / X86, Проблемы с относительными скачками - PullRequest
14 голосов
/ 14 апреля 2010

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

Простой пример программы, которую я пытаюсь сделать, таков:

.global main

main:
    jmp 0x4
    ret

Поскольку инструкция jmp имеет длину 4 байта и относительный переход смещен относительно адреса перехода + 1, это должно быть необычным запретом. Однако компиляция и запуск этого кода вызовет ошибку сегментации.

Настоящая загадка для меня заключается в том, что компиляция его на уровне объекта и последующая разборка объектного файла показывают, что похоже, что ассемблер правильно выполняет относительный переход, но после компиляции файла компоновщик меняет его на другой тип. прыжка

Например, если приведенный выше код был в файле asmtest.s:

$gcc -c asmtest.s
$objdump -D asmtest.o

... Some info from objdump
00000000 <main>:
   0:    e9 00 00 00 00           jmp    5 <main+0x5>
   5:    c3                       ret   

Похоже, ассемблер правильно сделал относительный переход, хотя подозрительно, что инструкция jmp заполнена нулями.

Затем я использовал gcc, чтобы связать его, затем разобрал его и получил это:

$gcc -o asmtest asmtest.o
$objdump -d asmtest

...Extra info and other disassembled functions
08048394 <main>:
 8048394:        e9 6b 7c fb f7      jmp   4 <_init-0x8048274>
 8048399:        c3                  ret

Мне кажется, что компоновщик переписал оператор jmp или заменил 5 на другой адрес.

Итак, мой вопрос сводится к тому, что я делаю не так?

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

Ответы [ 3 ]

17 голосов
/ 14 апреля 2010

На самом деле ассемблер подумал, что вы пытаетесь сделать абсолютный прыжок. Однако код операции jmp на уровне металла является относительным. Следовательно, ассемблер не может знать, что писать после байта 0xe9, потому что ассемблер не знает, по какому адресу в итоге окажется ваш код.

Ассемблер не знает, но компоновщик знает. Таким образом, ассемблер записал в заголовки asmtest.o где-то запрос компоновщика, что-то вроде этого: «когда вы знаете, по какому адресу будет загружен код, настройте эти байты сразу после 0xe9, чтобы они соответствовали переход от этой точки (с относительной адресацией) к абсолютному адресу «4» ». Линкер сделал именно это. Он увидел, что 0xe9 был по адресу 0x08048394, а следующий код операции - 0x08048399, и вычислил: чтобы перейти от 0x08048399 к 0x00000004, нужно вычесть 0x08048395, что эквивалентно добавлению (на 32-разрядных компьютерах) 0xf7fb7c6b. Отсюда ваша последовательность «6b 7c fb f7» в полученном двоичном файле.

Вы можете закодировать относительный переход «вручную» следующим образом:

.global main
main:
    .byte 0xe9
    .long 0x4
    ret

Таким образом, ассемблер не заметит, что ваш 0xe9 действительно является jmp, и он не будет пытаться перехитрить вас. В двоичном файле вы получите желаемую последовательность 'e9 04 00 00 00' и отсутствие взаимодействия с компоновщиком.

Обратите внимание, что приведенный выше код может дать сбой, поскольку относительное смещение отсчитывается от адреса сразу после смещения (т. Е. Адрес следующего кода операции, здесь ret). Это прыгнет на ничейной земле через 4 байта после ret, и, вероятно, возникнет ошибка или что-то странное.

15 голосов
/ 14 апреля 2010

Если вы используете GCC на ассемблере GAS, который по умолчанию использует синтаксис AT & T, синтаксис для относительной адресации использует точку ('.') для представления текущего собираемого адреса (очень похоже на $ псевдосимвол используется в синтаксисе сборки Intel / MASM). Вы должны быть в состоянии получить относительный прыжок, используя что-то вроде:

jmp . + 5

Например, следующая функция:

void foo(void)
{
    __asm__ (
        "jmp .  + 5\n\t"
        "nop\n\t"
        "nop\n\t"
        "nop\n\t"
        "nop\n\t"
        "nop\n\t"

    );
}

Собирается в:

  71 0000 55            pushl   %ebp
  72 0001 89E5          movl    %esp, %ebp
  74                LM2:
  75                /APP
  76 0003 EB03          jmp .  + 5
  77 0005 90            nop
  78 0006 90            nop
  79 0007 90            nop
  80 0008 90            nop
  81 0009 90            nop
  82                    
  84                LM3:
  85                /NO_APP
  86 000a 5D            popl    %ebp
  87 000b C3            ret
5 голосов
/ 14 апреля 2010

Я думаю, что ассемблер берет абсолютный адрес и вычисляет смещение адреса для вас. Нули в первом случае, вероятно, присутствуют, потому что это часть таблицы исправлений, а смещение вычисляется в фазе соединения.

Мои навыки ассемблера немного ржавые, но я думаю, вы могли бы просто сделать это:

.global main

main:
    jmp getouttahere
getouttahere:
    ret

Или, если вы действительно хотите, чтобы это выглядело относительно:

.global main

main:
    jmp .+5
    ret

Пожалуйста, будь осторожен, если я не прав; это было давно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...