GCC/X86, проблемы с относительными переходами

Я пытаюсь сделать относительный переход в 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 инструкция заполнена 0s.

Я затем использовал 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 пытается удостовериться, что я не делаю опасных вещей в своем коде?

14
задан Ian Kelly 14 April 2010 в 15:34
поделиться

3 ответа

На самом деле, ассемблер подумал, что вы пытаетесь сделать абсолютный переход. Однако опкод 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 00', и никакого взаимодействия с компоновщиком.

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

17
ответ дан 1 December 2019 в 07:19
поделиться

Если вы используете ассемблер GCC GCC, который по умолчанию использует синтаксис 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
15
ответ дан 1 December 2019 в 07:19
поделиться

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

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

.global main

main:
    jmp getouttahere
getouttahere:
    ret

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

.global main

main:
    jmp .+5
    ret

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

5
ответ дан 1 December 2019 в 07:19
поделиться
Другие вопросы по тегам:

Похожие вопросы: