Если вы хотите вызвать функцию C / C ++ из встроенной сборки, вы можете сделать что-то вроде этого:
void callee() {}
void caller()
{
asm("call *%0" : : "r"(callee));
}
Затем GCC будет выдавать код, который выглядит следующим образом:
movl $callee, %eax
call *%eax
Это может быть проблематично, поскольку косвенный вызов разрушит конвейер на старых процессорах.
Поскольку адрес вызываемого абонента
в конечном итоге является константой, можно представить, что можно будет использовать я
ограничение. Цитирование из GCC онлайн docs :
`i '
Разрешается непосредственный целочисленный операнд (один с постоянным значением). Эта включает в себя символические константы, чьи значения будут известны только при сборке время или позже.
Если я пытаюсь использовать его следующим образом:
asm("call %0" : : "i"(callee));
Я получаю следующую ошибку от ассемблера:
Ошибка: суффикс или операнды недопустимы для `call '
Это происходит потому, что GCC испускает код
call $callee
Вместо
call callee
Поэтому мой вопрос заключается в том, можно ли сделать вывод GCC правильным вызовом
.
Я получил ответ из списка рассылки GCC:
asm("call %P0" : : "i"(callee));
Теперь мне просто нужно узнать, что на самом деле означает % P0
, потому что это кажется быть недокументированной функцией ...
Правка : После просмотра исходного кода GCC не совсем понятно, что означает код P
перед ограничением. Но, помимо прочего, он не позволяет GCC помещать $
перед постоянными значениями. Как раз то, что мне нужно в данном случае.
Хитрость заключается в конкатенации строковых литералов. Прежде чем GCC начнет пытаться извлечь из вашего кода какой-либо реальный смысл, он объединит соседние строковые литералы, поэтому, даже если строки ассемблера не такие же, как другие строки, которые вы используете в своей программе, они должны быть объединены, если вы сделаете:
#define ASM_CALL(X) asm("\t call " X "\n")
int main(void) {
ASM_CALL( "my_function" );
return 0;
}
Поскольку вы используете GCC, вы также можете сделать
#define ASM_CALL(X) asm("\t call " #X "\n")
int main(void) {
ASM_CALL(my_function);
return 0;
}
Если вы еще не знаете, вы должны знать, что вызов вещей из встроенного ассемблера очень сложен. Когда компилятор генерирует свои собственные вызовы других функций, он включает код для установки и восстановления вещей до и после вызова. Однако он не знает, что должен делать все это для вашего вызова. Вам придется либо включить это самостоятельно (очень сложно сделать это правильно и может нарушиться при обновлении компилятора или флагов компиляции), либо убедиться, что ваша функция написана таким образом, что она не изменяет регистры или состояние стека (или переменной на нем).