ARM inline asm: выход из системного вызова со значением, считанным из памяти
проблема
Я хочу выполнить системный вызов выхода в ARM, используя встроенную сборку на устройстве Linux Android, и хочу, чтобы значение выхода считывалось из места в памяти.
пример
Без предоставления этого дополнительного аргумента макрос для вызова выглядит так:
#define ASM_EXIT() __asm__("mov %r0, #1\n\t" \
"mov %r7, #1\n\t" \
"swi #0")
Это хорошо работает. Чтобы принять аргумент, я настраиваю его на:
#define ASM_EXIT(var) __asm__("mov %r0, %0\n\t" \
"mov %r7, #1\n\t" \
"swi #0" \
: \
: "r"(var))
и я называю это используя:
#define GET_STATUS() (*(int*)(some_address)) //gets an integer from an address
ASM_EXIT(GET_STATUS());
ошибка
неверный asm: номер операнда вне диапазона
Я не могу объяснить, почему я получаю эту ошибку, так как я использую одну входную переменную в приведенном выше фрагменте (%0/var). Кроме того, я попытался с обычной переменной, и все еще получил ту же ошибку.
1 ответ
Синтаксис Extended-ASM требует записи %%
чтобы получить один %
в выводе asm. например для x86:
asm("inc %eax") // bad: undeclared clobber
asm("inc %%eax" ::: "eax"); // safe but still useless :P
%r7
лечит r7
в качестве номера операнда. Как отметили комментаторы, просто опустите %
s, потому что они вам не нужны для ARM, даже с GNU as
,
К сожалению, кажется, что нет способа запрашивать входные операнды в определенных регистрах на ARM, как это можно сделать для x86. (например "a"
средства ограничения eax
в частности).
Ты можешь использовать register int var asm ("r7")
чтобы заставить переменную использовать определенный регистр, а затем использовать "r"
ограничение и предположим, что он будет в этом регистре. Я не уверен, что это всегда безопасно или хорошая идея, но, похоже, это работает даже после встраивания. @ Джереми комментирует, что этот метод был рекомендован командой GCC.
Я получил сгенерированный эффективный код, который позволяет не тратить впустую инструкцию на перемещение reg-reg:
Посмотрите это в проводнике компилятора Godbolt:
__attribute__((noreturn)) static inline void ASM_EXIT(int status)
{
register int status_r0 asm ("r0") = status;
register int callno_r7 asm ("r7") = 1;
asm volatile("swi #0\n"
:
: "r" (status_r0), "r" (callno_r7)
);
}
#define GET_STATUS() (*(int*)(some_address)) //gets an integer from an address
void foo(void) { ASM_EXIT(12); }
push {r7} @ # gcc is still saving r7 before use, even though it sees the "noreturn" and doesn't generate a return
movs r0, #12 @ stat_r0,
movs r7, #1 @ callno,
swi #0
# yes, it literally ends here, after the inlined noreturn
void bar(int status) { ASM_EXIT(status); }
push {r7} @
movs r7, #1 @ callno,
swi #0 # doesn't touch r0: already there as bar()'s first arg.
Поскольку вы всегда хотите, чтобы значение считывалось из памяти, вы можете использовать "m"
ограничение и включают в себя ldr
в вашем встроенном ассм. Тогда вам не понадобится register int var asm("r0")
хитрость, чтобы избежать потраченных впустую mov
для этого операнда.
mov r7, #1
не всегда может быть необходимо, поэтому я использовал register asm()
синтаксис для него тоже. Если GCC хочет 1
константа в регистре где-то еще в функции, это может сделать это в r7
так что это уже там для ASM_EXIT.
Каждый раз, когда первая или последняя инструкция встроенного asm-оператора GNU C mov
инструкции, вероятно, есть способ удалить их с лучшими ограничениями.