鸿蒙ndk开发学习(6)—— 实现系统调用(SVC)
大约 3 分钟
之前在Android当中,有内联汇编的系统调用方案,然后呢,有了鸿蒙,于是乎就想这里能不能直接内联汇编实现系统调用呢,想到这个想法,那就尝试一下吧。
使用环境
我这里只有9的API,因此这里基于API 9进行测试,对于大于API 9的版本,因为我拿不到对应的sdk,因此无法测试,对于高版本文档,我这也看不了,因此也没办法查阅。
具体环境如上图所示。
实现方案
这里通过.s
文件来实现汇编代码,目录结构如下。
至于为啥我要写三个汇编文件,那肯定不是因为我非常勤快,是因为我没找到只编译特定处理器so的方案,然后呢,如果不提供cpu架构的汇编代码,编译会不过,因此,只能写三个了,好在不麻烦,下面来详细看一下,需要添加那些东西。
修改cmake文件
# 这里开启汇编的支持
set(can_use_assembler TRUE)
enable_language(ASM)
# 根据cpu架构选择不同的汇编代码
if (${CMAKE_SYSTEM_PROCESSOR} STREQUAL "aarch64")
set(syscall asm/syscall64.s)
elseif (${CMAKE_SYSTEM_PROCESSOR} STREQUAL "arm")
set(syscall asm/syscall32.s)
elseif (${CMAKE_SYSTEM_PROCESSOR} STREQUAL "x86_64")
set(syscall asm/syscallx86.s)
endif ()
汇编代码
- arm64
.text
.global raw_syscall
.type raw_syscall,@function
raw_syscall:
MOV X8, X0
MOV X0, X1
MOV X1, X2
MOV X2, X3
MOV X3, X4
MOV X4, X5
MOV X5, X6
SVC 0
RET
- arm
.text
.global raw_syscall
.type raw_syscall,%function
raw_syscall:
MOV R12, SP
STMFD SP!, {R4-R7}
MOV R7, R0
MOV R0, R1
MOV R1, R2
MOV R2, R3
LDMIA R12, {R3-R6}
SVC 0
LDMFD SP!, {R4-R7}
mov pc, lr
- x86_64
.global raw_syscall
.type raw_syscall,@function
raw_syscall:
push %rbp
mov %rsp, %rbp
mov %rdi, %rax
mov %rsi, %rdi
mov %rdx, %rsi
mov %rcx, %rdx
mov %r8, %r10
mov %r9, %r8
mov 8(%rbp), %r9
syscall
mov %rbp, %rsp
pop %rbp
ret
然后呢,有了这些代码,就可以正常调用系统调用了,在之前呢,我们需要先定义一个c的函数。
extern "C" {
__inline__ __attribute__((always_inline)) long raw_syscall(long __number, ...);
}
要注意,这个要按照c导出,否则会找不到符号。
编写测试
有了系统调用,我们简单写个测试的代码,就获取下文件的属性吧。
首先,我们在ets层创建一个文件,至于为啥在ets层创建,那单纯是因为ets创建简单,简简单单写个代码。
let context = getContext();
let pathDir = context.filesDir;
hilog.info(0, "LQ->", "dir: %{public}s", pathDir);
let filePath = pathDir + "/test.txt";
let file = fs.openSync(filePath, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);
fs.write(file.fd, "hello, world").then((writeLen) => {
fs.closeSync(file);
testNapi.readFile(filePath);
}).catch((err) => {
// ... 处理错误
});
然后,定义下native层调用的函数,也就是readFile。
export const readFile: (path: string) => void;
接着来实现以下这个函数吧。
static napi_value ReadFile(napi_env env, napi_callback_info info) {
size_t argc = 1;
napi_value argv[1] = {NULL};
void *data;
// 获取JavaScript传递的参数
napi_get_cb_info(env, info, &argc, argv, nullptr, &data);
// 获取字符串的长度
size_t str_length;
napi_get_value_string_utf8(env, argv[0], nullptr, 0, &str_length);
// 因为这个长度不包括\0, 因此分配缓冲区,加1是为了容纳终止字符 '\0'
char *buffer = new char[str_length + 1];
napi_get_value_string_utf8(env, argv[0], buffer, str_length + 1, &str_length);
LOGD("file path: %{public}s", buffer);
struct stat _stat = {0};
int fd = static_cast<int>(raw_syscall(__NR_openat, AT_FDCWD,
reinterpret_cast<const char *>(buffer),
O_RDONLY | O_CLOEXEC,
0640));
LOGD("fd1 => %{public}x", fd);
if (fd > 0) {
if (raw_syscall(__NR_fstat, fd, &_stat) == 0) {
LOGD("tv_nsec => %{public}ld", _stat.st_atim.tv_nsec);
}
}
napi_value ret;
napi_create_int32(env, 0, &ret);
return ret;
}
简单调用一下,然后发现这个是好用的。
总结
本篇文章呢,利用内联汇编的方案,在鸿蒙当中实现了系统调用,上面的代码仅供参考,因为我没有真机,因此不保证真机能跑,关键是穷,好了,本篇文章到这里就结束了,溜了,溜了~~