Clang не «меняет» вашу сборку. Он делает именно то, что вы его просите, а именно: он выбирает регистр (это то, что означает ограничение r
) и заменяет его %0
. Обратите внимание, что этот регистр также может быть rax
, и в этом случае ваш код не будет работать. Если вы хотите, чтобы clang выбирал операнд памяти, используйте вместо этого ограничение m
. См. руководство по g cc для получения подробной информации о встроенной сборке в стиле g cc. Вот пример:
extern unsigned long long jumpAddrAbsolute;
__attribute__((naked)) void XxInternalOperation()
{
asm volatile("mov %%rcx, %%rax\n\t"
"mov %0, %%r11\n\t"
"jmpq *%%r11"
:
: "m" (jumpAddrAbsolute)
: "r11", "rax");
}
Это компилируется в:
movq %rcx, %rax
movq jumpAddrAbsolute(%rip), %r11
jmpq *%r11
, что кажется тем, что вам нужно.
Обратите внимание, что я пометил rax
как затертый, так как вы используйте его в первой инструкции. Обратите внимание, что нет никакой гарантии, что rcx
будет содержать какое-либо конкретное значение при выполнении этого встроенного оператора сборки. Компилятор может установить любое значение, которое ему нравится.
Обратите внимание также, что компилятор может решить встроить XxInternalOperation
, и в этом случае ваш косвенный переход будет иметь непреднамеренный побочный эффект выполнения хвостового вызова в звонящий. Чтобы избежать этого сценария, подумайте о том, чтобы пометить функцию как noinline
.
Однако в целом выполнение переходов или вызовов во встроенной сборке часто является признаком неправильного подхода и обычно приводит к разного рода проблемам. Если у вас есть некоторые подробности о проблеме, которую вы решили решить с помощью этой встроенной сборки, я мог бы предложить вам лучшее решение.