显式寄存器变量最小可运行示例
这是一个ARMv8 Linux C独立运行的hello world示例,演示了https://dev59.com/uFHTa4cB1Zd3GeqPOyCQ#3936064的一些反汇编分析:
main.c
Original Answer翻译成"最初的回答"
#include <inttypes.h>
void _start(void) {
uint64_t exit_status;
{
char msg[] = "hello syscall v8\n";
uint64_t syscall_return;
register uint64_t x0 __asm__ ("x0") = 1;
register char *x1 __asm__ ("x1") = msg;
register uint64_t x2 __asm__ ("x2") = sizeof(msg);
register uint64_t x8 __asm__ ("x8") = 64;
__asm__ __volatile__ (
"svc 0;"
: "+r" (x0)
: "r" (x1), "r" (x2), "r" (x8)
: "memory"
);
syscall_return = x0;
exit_status = (syscall_return != sizeof(msg));
}
{
register uint64_t x0 __asm__ ("x0") = exit_status;
register uint64_t x8 __asm__ ("x8") = 93;
__asm__ __volatile__ (
"svc 0;"
: "+r" (x0)
: "r" (x8)
:
);
}
}
最初的回答:
GitHub上游。
编译并运行:
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
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
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;
{
char msg[] = "hello syscall v8\n";
uint64_t syscall_return;
__asm__ (
"mov x0, 1;"
"mov x1, %[msg];"
"mov x2, %[len];"
"mov x8, 64;"
"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));
}
__asm__ (
"mov x0, %[exit_status];"
"mov x8, 93;"
"svc 0;"
:
: [exit_status] "r" (exit_status)
: "x0", "x8"
);
}
最初的回答:
GitHub上游。
反汇编:
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
到输出寄存器的情况下获取syscall返回状态
exit
状态通过x1
多移动了一次。与寄存器变量相比,可以直接计算到x0
中。
在Ubuntu 18.10, GCC 8.2.0和QEMU 2.12中进行了测试。