Инструкция пэда, так что конец выровнен - PullRequest
1 голос
/ 03 декабря 2009

Я работаю с ассемблером GNU на i386, как правило, под 32-битным Linux (я также нацеливаюсь на решение под Cygwin).

У меня есть функция "заглушки":

    .align 4
stub:
    call *trampoline
    .align 4
stub2:

trampoline:
    ...

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

Теперь stub компилируется в:

ff 15 44 00 00 00      call *0x44
66 90                  xchg %ax,%ax

Это вызов по абсолютному адресу, который хорош, потому что адрес call неизвестен. Заполнение было превращено в то, что, как я полагаю, является операцией бездействия, и это нормально, и в любом случае она никогда не будет выполнена, поскольку trampoline перезапишет стек до перехода к указателю функции.

Проблема заключается в том, что адрес возврата, выдаваемый этим вызовом, будет указывать на невыровненную инструкцию xchg, а не на выровненные данные сразу после нее. Это означает, что trampoline необходимо исправить выравнивание, чтобы найти данные. Это не серьезная проблема, но было бы немного предпочтительнее создать что-то вроде:

66 90                  xchg %ax,%ax
ff 15 44 00 00 00      call *0x44
# Data will be placed starting here

Так что обратный адрес указывает непосредственно на данные. Тогда возникает вопрос: как я могу дополнить инструкцию так, чтобы конец ее выровнялся?

Редактировать Небольшой фон (для тех, кто еще не догадался). Я пытаюсь реализовать замыкания. На языке

(int -> int) make_curried_adder(int x)
{
    return int lambda (int y) { return x + y; };
}

(int -> int) plus7;
plus7 = make_curried_adder(7);
print("7 + 5 = ", plus7(5));

{ return x + y } переводится в обычную, но анонимную функцию с двумя параметрами. Блок памяти выделяется и заполняется инструкциями-заглушками, адресом функции и значением 7. Это возвращается make_curried_adder и при вызове помещает дополнительный аргумент 7 в стек, а затем переходит к анонимной функции.

Обновление

Я принял ответ Паскаля, который заключается в том, что ассемблеры, как правило, пишутся для запуска за один проход. Я думаю, что некоторые ассемблеры имеют более одного прохода, чтобы иметь дело с кодом типа "call x; ...; x: ...", который имеет прямую ссылку. (На самом деле я написал один давным-давно - он вернется и заполнит правильный адрес, как только он достигнет x.) Или, возможно, все такие дыры оставлены для закрытия компоновщика. Другая проблема с заполнением конца состоит в том, что вам нужен синтаксис, чтобы сказать «вставить заполнение здесь , чтобы там было выровнено». Я могу придумать алгоритм, который бы работал для таких простых случаев, как этот, но он может быть настолько неясным, что его не стоит реализовывать. Более сложные случаи с вложенным заполнением могут иметь противоречивые результаты ...

Ответы [ 3 ]

1 голос
/ 03 декабря 2009

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

Я видел руководство по сложному многопроходному ассемблеру, которое позволяет вычесть адреса двух меток, чтобы получить длину последовательности команд, и позволит вам вставить директиву для вставки последовательности NOP, скажем, (4 - эта длина по модулю 4) в месте по вашему выбору (при условии, что остается возможность сходиться на определенной позиции для каждой инструкции). Я не могу вспомнить, какой это был ассемблер. Определенно не gas, что, насколько я знаю, является однопроходным. Возможно, это был почтенный А386.

1 голос
/ 04 декабря 2009

Рассматривали ли вы ввод данных перед кодом?

Таким образом, это только вычитание (длины кода заглушки плюс некоторое постоянное смещение), чтобы добраться до адреса данных, так что это одна инструкция вместо двух, как вы были готовы принять. И я считаю, что gas даст вам длину кода заглушки (как разность двух меток) без проблем, поскольку метки используются после того, как они были определены в этом случае.

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

1 голос
/ 03 декабря 2009

Есть ли проблема с добавлением вашей собственной инструкции xchg до call? Поскольку у вас есть выравнивание непосредственно перед заглушкой, выравнивание должно быть последовательным.

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