Я написал этот код в Clang-совместимом «GNU extended asm»:
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
функция), а затем далее «пролить» его на кадр стека, который даже не существовать, все, так что он может снова выполнить его в блоке встроенного ассемблера.
Итак, как я могу заставить 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 вставить именно то перемещение, которое я хочу?
Я думаю, что это то, что вы хотите:
namespace foreign {
extern char magic_pointer[];
}
extern "C" __attribute__((naked)) void get_address_of_x(void)
{
asm volatile ("ret" : : "a"(&foreign::magic_pointer));
}
В этом контексте «а» является ограничением, которое указывает, что %rax
должен быть использован. Затем Clang загрузит адрес magic_pointer
в %rax
в подготовке к выполнению вашего встроенного асма, что все, что вам нужно.
Это немного хитроумно, потому что оно определяет ограничения, на которые нет ссылок в тексте asm, и я не уверен, что это технически разрешено / четко определено — но это работает на последнем кланге.
На 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
ОП здесь.
Я просто написал помощника 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...");
Проще и чище. 🙂