Загрузить 64-битную целочисленную константу через ограничение GNU extended asm?

Я написал этот код на Clang-совместимом "расширенном ассемблере GNU":

namespace foreign {
    extern char magic_pointer[];
}

extern "C" __attribute__((naked)) void get_address_of_x(void)
{
    asm volatile("movq %[magic_pointer], %%rax\n\t"
                 "ret"
                 : : [magic_pointer] "p"(&foreign::magic_pointer));
}

Я ожидал, что он скомпилируется в следующую сборку:

_get_address_of_x:
## InlineAsm Start
    movq    $__ZN7foreign13magic_pointerE, %rax
    ret
## InlineAsm End
    ret  /* useless but I don't think there's any way to get rid of it */

Но вместо этого получаю такую ​​"чушь":

_get_address_of_x:
movq    __ZN7foreign13magic_pointerE@GOTPCREL(%rip), %rax
movq    %rax, -8(%rbp)
## InlineAsm Start
movq -8(%rbp), %rax
    ret
## InlineAsm End
ret

Очевидно Clang присваивает значение &foreign::magic_pointer %rax (что смертельно опасно для naked функции), а затем "проливает" его на фрейм стека, которого даже не существует, чтобы он мог тянуть это снова выключено во встроенном блоке asm.

Итак, как я могу заставить Clang генерировать именно тот код, который мне нужен, не прибегая к ручному изменению имен? Я имею в виду, что я мог бы просто написать

extern "C" __attribute__((naked)) void get_address_of_x(void)
{
    asm volatile("movq  __ZN7foreign13magic_pointerE@GOTPCREL(%rip), %rax\n\t"
                 "ret");
}

но я действительно не хочу этого делать, если есть какой-то способ помочь.

Прежде чем приступить к "p", я попробовал ограничения "i" и "n"; но они, похоже, не работали должным образом с операндами 64-битных указателей. Clang продолжал выдавать мне сообщения об ошибках о невозможности выделить операнд в регистр %flags, что похоже на что-то сумасшедшее, что-то пошло не так.


Для тех, кто заинтересован в решении "проблемы XY" здесь: я действительно пытаюсь написать гораздо более длинную сборочную заглушку, которая вызывает другую функцию foo(void *p, ...), где аргумент p установлен на значение этого магического указателя, а другие аргументы установлены на основе исходные значения регистров ЦП в момент ввода этой заглушки сборки. (Следовательно, naked функция.) Произвольная политика компании запрещает просто записать эту чертову вещь в .S файл для начала; кроме того, я действительно хотел бы написать foreign::magic_pointer вместо __ZN7foreign...etc.... В любом случае, это должно объяснить, почему передача временных результатов в стек или регистры строго запрещена в данном контексте.


Возможно, есть способ написать

asm volatile(".long %[magic_pointer]" : : [magic_pointer] "???"(&foreign::magic_pointer));

чтобы заставить Clang вставить именно то перемещение, которое я хочу?


person Quuxplusone    schedule 11.12.2012    source источник
comment
Вы строите с включенным позиционно-независимым кодом? (-fPIC)   -  person John Bartholomew    schedule 12.12.2012
comment
@JohnBartholomew Он, должно быть, там, ...@GOTPCREL(%rip) нет другой причины быть там   -  person je4d    schedule 12.12.2012
comment
@JohnBartholomew Да, PIC используется по умолчанию на x86-64. Полагаю, это означает, что моя .long %[magic_pointer] идея улетучивается. Но Clang все еще должен иметь возможность кодогенировать простой leaq или movq каким-то образом, без всего этого.   -  person Quuxplusone    schedule 12.12.2012
comment
Возможно, стоит отметить, что использование Extended asm в голых функциях явно запрещено в gcc (не уверен в clang). Из документации: Только базовые операторы asm можно безопасно включать в голые функции. Хотя использование расширенного asm или смеси базового asm и кода C может показаться работоспособным, нельзя полагаться на их надежную работу, и они не поддерживаются.   -  person David Wohlferd    schedule 17.11.2017


Ответы (2)


Думаю, это то, что вам нужно:

namespace foreign {
    extern char magic_pointer[];
}

extern "C" __attribute__((naked)) void get_address_of_x(void)
{
    asm volatile ("ret" : : "a"(&foreign::magic_pointer));
}

В этом контексте «a» - это ограничение, которое указывает, что %rax должен использоваться. Затем Clang загрузит адрес magic_pointer в %rax для подготовки к выполнению вашего встроенного asm, а это все, что вам нужно.

Это немного хитро, потому что он определяет ограничения, на которые нет ссылок в тексте asm, и я не уверен, разрешено ли это технически / четко ли определено, но он работает с последним clang.

На clang 3.0-6ubuntu3 (потому что я ленив и использую gcc.godbolt.org), с -fPIC вы получите следующий asm:

get_address_of_x:                       # @get_address_of_x
    movq    foreign::magic_pointer@GOTPCREL(%rip), %rax
    ret
    ret

И без -fPIC:

get_address_of_x:                       # @get_address_of_x
    movl    foreign::magic_pointer, %eax
    ret
    ret
person je4d    schedule 11.12.2012
comment
Обратите внимание, что на самом деле вам не нужен ret, поскольку для функции в любом случае выдается ret. У меня работает с пустым "" сборочным блоком. Однако у ret есть то преимущество, что он является явным. - person John Bartholomew; 12.12.2012
comment
@JohnBartholomew да, я размышлял, удалять ли его или нет, но решил оставить его там, так как я не могу найти никакой документации, чтобы предположить, что компилятор гарантированно добавит ret свой собственный в конец голой функции, хотя gcc / clang, похоже, делает это на практике. - person je4d; 12.12.2012
comment
Проблема в том, что это приведет к удалению (ПО КРАЙНЕЙ мере) регистра %rax прямо в верхней части функции. Мне нужно сохранить все исходные значения регистров, чтобы передать их foo. - person Quuxplusone; 12.12.2012
comment
@Quuxplusone использует отдельный блок asm перед этим? - person je4d; 12.12.2012
comment
@Quuxplusone подожди секунду ... какое соглашение о вызовах ты используешь? %rax не является регистром передачи параметров в любом соглашении о вызовах x86_64, которое я могу придумать. - person je4d; 12.12.2012
comment
@ je4d В реальном коде он попадает в %rdx (регистр четвертого аргумента). Думаю, я мог бы использовать два блока asm, но это все равно довольно страшно. Помните, в моем первоначальном примере с игрушкой Clang добавлял магазин в -8(%rbp), который просто полностью сломан, поэтому я нахожусь в этом странном психическом состоянии, желая просто полагаться на функции Clang, но в то же время зная, что я не могу доверять их. - person Quuxplusone; 12.12.2012
comment
@Quuxplusone Я понимаю вашу дилемму! Неудивительно, что он глючит, учитывая, насколько это эзотерично. Если одна дополнительная инструкция не является чрезмерно дорогой, как насчет использования одного блока asm, загрузки указателя в% rax, как указано выше (что нормально, поскольку это рабочий регистр), а затем использования mov %rax, %rdx после того, как вы позаботитесь о rcx / r8 / r9? кстати, rdx - 3-й, rcx - 4-й, если вы имеете в виду linux / bsd - person je4d; 12.12.2012

ОП здесь.

В итоге я просто написал вспомогательную функцию extern "C" для возврата магического значения, а затем вызвал эту функцию из моего кода сборки. Я все еще думаю, что Clang должен как-то поддержать мой первоначальный подход, но основная проблема с этим подходом в моем реальном случае заключалась в том, что он не масштабировался до x86-32. В x86-64 загрузка произвольного адреса в %rdx может быть выполнена в одной инструкции с %rip-относительным mov. Но на x86-32 загрузка произвольного адреса с помощью -fPIC превращается всего в тонну кода, .indirect_symbol директив, два доступа к памяти ... Я просто не хотел пытаться Пишу все это от руки. Итак, мой окончательный код сборки выглядит как

asm volatile(
    "...save original register values...;"
    "call  _get_magic_pointer;"
    "movq  %rax, %rdx;"
    "...set up other parameters to foo...;"
    "call  _foo;"
    "...cleanup..."
    );

Проще и чище. :)

person Quuxplusone    schedule 13.12.2012