ARM64上内联汇编实现系统调用
ARM64上内联汇编实现系统调用
系统调用约定
- 系统调用号放在
x8寄存器中 - 参数放在
x0到x5寄存器中 - 系统调用的返回值放在
x0寄存器中
#include <stdio.h>
#include <stdint.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <syscall.h>
int svc_openat(int dir_fd, const char *filename, int flags, mode_t mode)
{
int result;
// ARM64 assembly code to invoke the openat syscall
asm volatile(
"mov x8, %1\n" // syscall number for openat (56 on ARM64)
"mov x0, %2\n" // dirfd (directory file descriptor)
"mov x1, %3\n" // pathname (pointer to the string)
"mov x2, %4\n" // flags
"mov x3, %5\n" // mode
"svc #0\n" // Call the syscall
"mov %0, x0\n" // Store the result in 'result'
: "=r" (result) // 输出操作数列表
: "i" (__NR_openat), "r" (dir_fd), "r" (filename), "r" (flags), "r" (mode) //输入操作数列表,对应占位符顺序
: "x0", "x1", "x2", "x3", "x8" // 告诉编译器在这段汇编代码中会被修改的寄存器, 顺序可以改变
);
return result;
}
int main() {
int dir_fd = AT_FDCWD; // 使用当前工作目录作为目录文件描述符
const char *filename = "example.txt";
int flags = O_RDONLY; // 您可以设置适当的标志
mode_t mode = 0; // 您可以设置适当的文件模式
int result = svc_openat(dir_fd, filename, flags, mode);
printf("result code %d\n", result);
if (result >= 0) {
printf("File opened successfully. File descriptor: %d\n", result);
close(result);
} else {
perror("Error opening file");
}
return 0;
}
在上面的代码中,%1、%2、%3 等是内联汇编中的占位符,用于表示输入或输出操作数。这些占位符与C代码中的变量相对应,用于指定要传递给汇编代码的参数。
具体来说,在以下的代码行中:
%1 表示 __NR_openat,它是 openat 系统调用的系统调用号。"i" (__NR_openat) 中的 "i" 表示立即数(immediate),这里用于表示系统调用号是一个常数值。
%2 表示 dir_fd,它是目录文件描述符,表示在哪个目录中打开文件。在这里,"r" (dir_fd) 中的 "r" 表示寄存器(register),用于表示 dir_fd 是一个寄存器传递的参数。
%3 表示 filename,它是要打开的文件名字符串的指针。在这里,"r" (filename) 中的 "r" 同样表示寄存器传递的参数。
%4 和 %5 分别表示 flags 和 mode,它们是打开文件的标志和权限模式。同样,"r" 表示这些参数是通过寄存器传递的。
最后的 : "=r" (result) 表示将汇编代码中的结果存储到 result 变量中,并告诉编译器使用哪些寄存器作为输入和输出,以及哪些寄存器的值可能会在汇编代码块中被修改。
这种使用占位符的方式使得您可以将C代码中的变量传递给内联汇编,并在汇编代码中使用它们,同时告诉编译器如何处理这些参数。
GDB观测运行状态
查看汇编代码gcc -S svc.c
svc_openat:
.LFB6:
.cfi_startproc
sub sp, sp, #48 // 开辟栈空间
.cfi_def_cfa_offset 48
str w0, [sp, 28] // w0加载到sp+28的地址上
str x1, [sp, 16] // x1加载到sp+16的地址上
str w2, [sp, 24]
str w3, [sp, 12]
ldr w4, [sp, 28]
ldr x5, [sp, 16]
ldr w6, [sp, 24]
ldr w7, [sp, 12]
#APP
// 15 "svc.c" 1
mov x8, 56 // 可以看出__NR_openat系统调用号为56
mov x0, x4
mov x1, x5
mov x2, x6
mov x3, x7
svc #0
mov x4, x0
// 0 "" 2
#NO_APP
str w4, [sp, 44]
ldr w0, [sp, 44]
add sp, sp, 48
.cfi_def_cfa_offset 0
ret
.cfi_endproc
.LFE6:
.size svc_openat, .-svc_openat
理解图
高地址(High Address)
+------------------+
| | <- sp (初始)
+------------------+
| ... |
| |
+------------------+
| w3 | <- sp + 12
+------------------+
| x1 |
+------------------+
| w2 | <- sp + 24
+------------------+
| w0 (-> w4) | <- sp + 28
+------------------+
| |
+------------------+
| | <- sp + 48
+------------------+
低地址(Low Address)
str w0, [sp, 28]:将 w0 的值存储到 sp + 28
ldr w4, [sp, 28]:从 sp + 28 位置加载值到 w4
使用GDB验证上面gcc -g svc.s过程
(gdb) b svc_openat
Breakpoint 1 at 0x7f4: file svc.s, line 10.
(gdb) r
Starting program: /data/workspace/a.out
Breakpoint 1, svc_openat () at svc.s:10
10 sub sp, sp, #48
(gdb) si
12 str w0, [sp, 28]
(gdb) si
13 str x1, [sp, 16]
(gdb)
....
(gdb) si
19 ldr w7, [sp, 12]
(gdb) si // 单步到22,此时mov指令还没执行, 刚执行完上一条ldr
22 mov x8, 56
根据汇编代码mov x1, x5和系统调用约定,我们知道
x0是存放dir_fd
x1是存放filename
以此类推
mov x1, x5因为x1来自于x5,因此我们直接打印x5寄存器的值,应该是"example.txt",同时ldr x5, [sp, 16]表示x5是从sp+16加载到寄存器的,因此sp+16的地址应该就是字符串地址
以此类推,mov x0, x4因为x0来自于x4,且ldr w4, [sp, 28],因此sp+28存放的就是dir_fd的值为-100
(gdb) x/s $x5
0x5555555970: "example.txt"
(gdb) x/8x $sp+16
0x7ffffffa60: 0x70 0x59 0x55 0x55 0x55 0x00 0x00 0x00
(gdb) x/s 0x5555555970
0x5555555970: "example.txt"
(gdb) x/d $sp+28
0x7ffffffa6c: -100
SVC内核调用栈

[ 596.333596] Hardware name: Qualcomm Technologies, Inc. qrb5165 IOT RB5 (DT)
[ 596.333597] Call trace:
[ 596.333604] dump_backtrace+0x0/0x250
[ 596.333607] show_stack+0x20/0x30
[ 596.333611] dump_stack+0xb8/0xf4
[ 596.333614] syscall_comm_entry_handler+0x23c/0x270 [ktracer]
[ 596.333616] faccessat_entry_handler+0x2c/0x40 [ktracer]
[ 596.333619] pre_handler_kretprobe+0x8c/0x160
[ 596.333622] kprobe_breakpoint_handler+0x164/0x210
[ 596.333624] brk_handler+0x44/0x170
[ 596.333625] do_debug_exception+0xac/0x138
[ 596.333627] el1_dbg+0x18/0x78
[ 596.333629] do_faccessat+0x0/0x308
[ 596.333631] el0_svc_common+0xa0/0x188
[ 596.333633] el0_svc_handler+0x6c/0x88
[ 596.333634] el0_svc+0x8/0xc

浙公网安备 33010602011771号