鸿蒙ndk开发学习(5)—— 调用系统调用函数
大约 3 分钟
在上一篇文章当中,研究了在鸿蒙当中如何采用内联汇编的方案来执行系统调用,这种方案呢,可以直接在so当中搜索到相关的指令,那么有没有什么方案可以避免这一点呢,那么我就想到了mmap
这个函数。
mmap函数
mmap
是一个Unix和类Unix操作系统(如Linux)中的一个系统调用,它允许程序员将一个文件或其他对象映射进内存。通过这种方式,文件或设备的内容可以像访问普通内存数组那样直接访问,而无需使用传统的文件读写操作。
有了,这个知识,那么,我们就可以用代码来实现这个功能了。
配置CMake
因为这里,鸿蒙我没有发现如何编译指定架构的so的方案,因此呢,这里还是要出3个文件,因此这里还是需要再CMakeLists.txt里面给配置一下。
if (${CMAKE_SYSTEM_PROCESSOR} STREQUAL "aarch64")
target_compile_definitions(entry PRIVATE AARCH64=1)
elseif (${CMAKE_SYSTEM_PROCESSOR} STREQUAL "arm")
target_compile_definitions(entry PRIVATE ARM=1)
elseif (${CMAKE_SYSTEM_PROCESSOR} STREQUAL "x86_64")
target_compile_definitions(entry PRIVATE X86_64=1)
endif ()
后续,就可以通过判断宏是否定义,来分别处理不同架构的代码了。
核心代码
这里,我们只需要从之前编译好的里面,然后直接扣出来对应的指令代码就可以了,非常的方便,这里给出指令代码。
#ifdef X86_64
unsigned char code[0x100] = {0x55, 0x48, 0x89, 0xE5, 0x48, 0x89, 0xF8, 0x48, 0x89, 0xF7, 0x48, 0x89,
0xD6, 0x48, 0x89, 0xCA, 0x4D, 0x89, 0xC2, 0x4D, 0x89, 0xC8, 0x4C, 0x8B,
0x4D, 0x08, 0x0F, 0x05, 0x48, 0x89, 0xEC, 0x5D, 0xC3, 0x0F, 0x1F, 0x00,
0x66, 0x66, 0x66, 0x2E, 0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00};
#elif AARCH64
unsigned char code[32] = {0xE8, 0x03, 0x00, 0xAA, 0xE0, 0x03, 0x01, 0xAA, 0xE1, 0x03, 0x02,
0xAA, 0xE2, 0x03, 0x03, 0xAA, 0xE3, 0x03, 0x04, 0xAA, 0xE4, 0x03,
0x05, 0xAA, 0xE5, 0x03, 0x06, 0xAA, 0x01, 0x00, 0x00};
#else ARM
unsigned char code[0x100] = {
0x0D, 0xC0, 0xA0, 0xE1, 0xF0, 0x00, 0x2D, 0xE9, 0x00, 0x70, 0xA0, 0xE1, 0x01, 0x00, 0xA0, 0xE1,
0x02, 0x10, 0xA0, 0xE1, 0x03, 0x20, 0xA0, 0xE1, 0x78, 0x00, 0x9C, 0xE8, 0x00, 0x00, 0x00,
};
#endif
然后呢,我们便可以通过mmap函数来直接分配一块可读、可写、可执行的代码,完成系统调用的功能。
long pagesize = sysconf(_SC_PAGESIZE);
// 分配一页内存,权限为可读可写可执行
void *mem = mmap(NULL, pagesize, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
if (mem == MAP_FAILED) {
// 内存映射失败
}
memcpy(mem, code, sizeof(code));
// 将内存位置转换为函数指针
func_t raw_syscall = (func_t)mem;
这样,我们就完成了,对于系统调用的函数了,接下来,还是简单测试一下,测试代码和上次一样。
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);
}
}
发现,效果非常的nice,这就成功了,好了,本篇文章到这里就结束了。