Как указать отдельный регистр в качестве ограничения в встроенной сборке ARM GCC?
В сборке x86 я могу написать что-то вроде этого:
asm ("cpuid"
: "=a" (_eax),
"=b" (_ebx),
"=c" (_ecx),
"=d" (_edx)
: "a" (op));
поэтому в ограничениях matchin вместо того, чтобы просто написать "=r" и позволить компилятору выбрать регистр, я могу сказать, какой именно регистр я хочу использовать (=a, например, для использования%eax)
Как я могу сделать это для сборки ARM? сборник рецептов сборки ARM GCC http://www.ethernut.de/en/documents/arm-inline-asm.html утверждает, что я могу, например, использовать ограничения "r" для одного из регистров общего назначения R0-R15 "w"для одного из регистров с плавающей запятой VFP S0-S31
но как я могу ограничить операнд, например, точно s1? или в конкретный регистр общего назначения?
2 ответа
Я не думаю, что gcc для ARM позволяет вам использовать ограничения, чтобы точно указать, какой регистр использовать. Однако вы можете использовать явные переменные регистра, чтобы указать регистр для хранения переменной в:
register int my_variable asm("r0");
Явный регистр переменных минимальный исполняемый пример
Вот автономный мир ARMv8 Linux C, представляющий пример /questions/42401578/kak-ukazat-otdelnyij-registr-v-kachestve-ogranicheniya-v-vstroennoj-sborke-arm-gcc/42401590#42401590 с некоторым анализом разборки:
main.c
#include <inttypes.h>
void _start(void) {
uint64_t exit_status;
/* write */
{
char msg[] = "hello syscall v8\n";
uint64_t syscall_return;
register uint64_t x0 __asm__ ("x0") = 1; /* stdout */
register char *x1 __asm__ ("x1") = msg;
register uint64_t x2 __asm__ ("x2") = sizeof(msg);
register uint64_t x8 __asm__ ("x8") = 64; /* syscall number */
__asm__ __volatile__ (
"svc 0;"
: "+r" (x0)
: "r" (x1), "r" (x2), "r" (x8)
: "memory"
);
syscall_return = x0;
exit_status = (syscall_return != sizeof(msg));
}
/* exit */
{
register uint64_t x0 __asm__ ("x0") = exit_status;
register uint64_t x8 __asm__ ("x8") = 93;
__asm__ __volatile__ (
"svc 0;"
: "+r" (x0)
: "r" (x8)
:
);
}
}
Скомпилируйте и запустите:
sudo apt-get install qemu-user gcc-aarch64-linux-gnu
aarch64-linux-gnu-gcc -O3 -std=c99 -ggdb3 -march=armv8-a -pedantic -Wall -Wextra \
-ffreestanding -nostdlib -static -o main.out main.c
qemu-aarch64 main.out
Выход:
hello syscall v8
Разборка:
aarch64-linux-gnu-objdump -S main.out
Выход:
main.out: file format elf64-littleaarch64
Disassembly of section .text:
0000000000400110 <_start>:
void _start(void) {
uint64_t exit_status;
/* write */
{
char msg[] = "hello syscall v8\n";
400110: 90000003 adrp x3, 400000 <_start-0x110>
400114: 91056063 add x3, x3, #0x158
void _start(void) {
400118: d10083ff sub sp, sp, #0x20
uint64_t syscall_return;
register uint64_t x0 __asm__ ("x0") = 1; /* stdout */
40011c: d2800020 mov x0, #0x1 // #1
register char *x1 __asm__ ("x1") = msg;
400120: 910023e1 add x1, sp, #0x8
register uint64_t x2 __asm__ ("x2") = sizeof(msg);
400124: d2800242 mov x2, #0x12 // #18
char msg[] = "hello syscall v8\n";
400128: a9401464 ldp x4, x5, [x3]
register uint64_t x8 __asm__ ("x8") = 64; /* syscall number */
40012c: d2800808 mov x8, #0x40 // #64
char msg[] = "hello syscall v8\n";
400130: 79402063 ldrh w3, [x3, #16]
400134: a90097e4 stp x4, x5, [sp, #8]
400138: 790033e3 strh w3, [sp, #24]
__asm__ __volatile__ (
40013c: d4000001 svc #0x0
: "+r" (x0)
: "r" (x1), "r" (x2), "r" (x8)
: "memory"
);
syscall_return = x0;
exit_status = (syscall_return != sizeof(msg));
400140: eb02001f cmp x0, x2
}
/* exit */
{
register uint64_t x0 __asm__ ("x0") = exit_status;
register uint64_t x8 __asm__ ("x8") = 93;
400144: d2800ba8 mov x8, #0x5d // #93
register uint64_t x0 __asm__ ("x0") = exit_status;
400148: 9a9f07e0 cset x0, ne // ne = any
__asm__ __volatile__ (
40014c: d4000001 svc #0x0
: "+r" (x0)
: "r" (x8)
:
);
}
}
400150: 910083ff add sp, sp, #0x20
400154: d65f03c0 ret
Попытка без явных переменных регистра
В основном ради интереса, я пытался достичь того же результата, не используя переменные регистра, но я не смог этого сделать.
В любом случае код будет более сложным, поэтому вам лучше использовать переменные регистра.
Вот моя лучшая попытка:
main.c
#include <inttypes.h>
void _start(void) {
uint64_t exit_status;
/* write */
{
char msg[] = "hello syscall v8\n";
uint64_t syscall_return;
__asm__ (
"mov x0, 1;" /* stdout */
"mov x1, %[msg];"
"mov x2, %[len];"
"mov x8, 64;" /* syscall number */
"svc 0;"
"mov %[syscall_return], x0;"
: [syscall_return] "=r" (syscall_return)
: [msg] "p" (msg),
[len] "i" (sizeof(msg))
: "x0", "x1", "x2", "x8", "memory"
);
exit_status = (syscall_return != sizeof(msg));
}
/* exit */
__asm__ (
"mov x0, %[exit_status];"
"mov x8, 93;" /* syscall number */
"svc 0;"
:
: [exit_status] "r" (exit_status)
: "x0", "x8"
);
}
Разборка:
main.out: file format elf64-littleaarch64
Disassembly of section .text:
0000000000400110 <_start>:
void _start(void) {
uint64_t exit_status;
/* write */
{
char msg[] = "hello syscall v8\n";
400110: 90000000 adrp x0, 400000 <_start-0x110>
400114: 9105a000 add x0, x0, #0x168
void _start(void) {
400118: d10083ff sub sp, sp, #0x20
char msg[] = "hello syscall v8\n";
40011c: a9400c02 ldp x2, x3, [x0]
400120: a9008fe2 stp x2, x3, [sp, #8]
400124: 79402000 ldrh w0, [x0, #16]
uint64_t syscall_return;
__asm__ (
400128: 910023e3 add x3, sp, #0x8
char msg[] = "hello syscall v8\n";
40012c: 790033e0 strh w0, [sp, #24]
__asm__ (
400130: d2800020 mov x0, #0x1 // #1
400134: aa0303e1 mov x1, x3
400138: d2800242 mov x2, #0x12 // #18
40013c: d2800808 mov x8, #0x40 // #64
400140: d4000001 svc #0x0
400144: aa0003e3 mov x3, x0
: [syscall_return] "=r" (syscall_return)
: [msg] "p" (msg),
[len] "i" (sizeof(msg))
: "x0", "x1", "x2", "x8", "memory"
);
exit_status = (syscall_return != sizeof(msg));
400148: f100487f cmp x3, #0x12
40014c: 9a9f07e1 cset x1, ne // ne = any
}
/* exit */
__asm__ (
400150: aa0103e0 mov x0, x1
400154: d2800ba8 mov x8, #0x5d // #93
400158: d4000001 svc #0x0
"svc 0;"
:
: [exit_status] "r" (exit_status)
: "x0", "x8"
);
}
40015c: 910083ff add sp, sp, #0x20
400160: d65f03c0 ret
Это было менее эффективно по следующим причинам:
write
ограничениеp
нужно использовать промежуточный регистрx3
дляadd
вsp
Я не знаю, как получить статус возврата системного вызова без дополнительной
mov
в выходной регистрexit
статус перемещается один раз черезx1
, С переменными регистра просто рассчитывается непосредственно вx0
,
Протестировано в Ubuntu 18.10, GCC 8.2.0, QEMU 2.12.