Direct C Функция Call с использованием встроенной сборки GCC

StackOverflow https://stackoverflow.com/questions/3467180

Вопрос

Если вы хотите вызвать функцию C / C ++ из встроенной сборки, вы можете сделать что-то подобное:

void callee() {}
void caller()
{
    asm("call *%0" : : "r"(callee));
}

GCC затем выделяет код, который выглядит так:

movl $callee, %eax
call *%eax

Это может быть проблематично, так как косвенный вызов уничтожит трубопровод на более старых процессорах.

Поскольку адрес callee в конечном итоге постоянна, можно представить, что можно было бы использовать i ограничение. Цитата из GCC онлайн документы:

`Я '

Допускается немедленный целочисленный операнд (одно с постоянным значением). Это включает в себя символические константы, значения которых будут известны только при времени сборки или позже.

Если я попытаюсь использовать это так:

asm("call %0" : : "i"(callee));

Я получаю следующую ошибку из ассемблера:

Ошибка: суффикс или операнды недействительны для `call '

Это потому, что GCC издает код

call $callee

Вместо

call callee

Итак, мой вопрос в том, можно ли сделать GCC выводить правильный call.

Это было полезно?

Решение

Я получил отвечать Из списка рассылки GCC:

asm("call %P0" : : "i"(callee));

Теперь мне просто нужно выяснить, что %P0 на самом деле означает, что, кажется, недокументирована ...

Редактировать: После взгляда на исходный код GCC, не совсем понятно, какой код P перед средством ограничения. Но, помимо прочего, это мешает GCC откладывать $ перед постоянными значениями. Что именно то, что мне нужно в этом случае.

Другие советы

Может быть, я что-то упускаю здесь, но

extern "C" void callee(void) 
{

}

void caller(void)
{
  asm("call callee\n");
}

должен работать нормально. Вам нужно extern "C", чтобы имя не было украшено на основе правил Callgling C ++.

Хитрость - это строковое буквальное конкатенация. До того, как 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;
}

Если вы еще не знаете, что вы должны знать, что вызов вещей от встроенной сборки очень сложно. Когда компилятор генерирует свои собственные вызовы на другие функции, он включает в себя код для настройки и восстановления вещей до и после вызова. Это не знает, что это должно делать что-нибудь из этого для вашего звонка, хотя. Вам придется либо включать, что сами (очень сложно получить правильно и может сломаться с обновлением компилятора или компиляции) или убедиться, что ваша функция написана таким образом, чтобы она не изменила никаких регистров или условий стек (или переменная на нем).

Редактировать это будет работать только на имена функций C - не C ++, так как они будут работать.

Если вы генерируете 32-битный код (например, опция -M32 GCC), следующие встроенные ASM излучают прямой вызов:

asm ("call %0" :: "m" (callee));
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top