Я работаю с ассемблером 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.) Или, возможно, все такие дыры оставлены для закрытия компоновщика. Другая проблема с заполнением конца состоит в том, что вам нужен синтаксис, чтобы сказать «вставить заполнение здесь , чтобы там было выровнено». Я могу придумать алгоритм, который бы работал для таких простых случаев, как этот, но он может быть настолько неясным, что его не стоит реализовывать. Более сложные случаи с вложенным заполнением могут иметь противоречивые результаты ...