Прямой вызов функции C с использованием встроенной сборки GCC

Если вы хотите вызвать функцию 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 правильным вызовом .

11
задан Ciro Santilli 新疆改造中心法轮功六四事件 3 July 2019 в 08:32
поделиться

2 ответа

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

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

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

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

11
ответ дан 3 December 2019 в 09:18
поделиться

Хитрость заключается в конкатенации строковых литералов. Прежде чем 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;
}

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

edit Это будет работать только для имен функций C - не C++, поскольку они искажены.

0
ответ дан 3 December 2019 в 09:18
поделиться