Android 中的 SYSCALL_INLINE

Phi*_*lip 3 assembly android multiplatform system-calls android-ndk

我需要在 Android NDK 内部使用 syscall 来防止挂钩包装函数。在 Linux 中有像 SYSCALL_INLINE 这样的宏,它允许在没有包装函数的情况下使用系统调用。因此宏将系统调用汇编代码直接嵌入到项目中。

我在 Android NDK 中找不到类似的宏。

也许我可以像这样编写自己的函数;https://git.busybox.net/uClibc/tree/libc/sysdeps/linux/arm/syscall.c

但我需要有相同功能的 arm、arm_64、x86 和 x86_64 版本。

你能帮助我吗?我怎样才能找到解决办法?

Pet*_*des 5

Android 的 Linux 内核仍然使用与常规 Linux 相同的系统调用号和 ABI,不是吗?(那么如何从用户空间访问系统调用?)所以你应该能够使用普通方法,从<asm/unistd.h>.

您可以在arch/x86_64/syscall_arch.h. 对于每个不同数量的参数,它都有不同的参数,而不是一个大参数。


MUSL有版本syscall_arch.hARM,AArch64,I386,和x86-64,以及它支持其他架构。它是根据MIT 许可获得许可的,因此您只需复制这些标头即可。

例如,他们的 ARM 版本有

static inline long __syscall3(long n, long a, long b, long c)
{
    register long r7 __ASM____R7__ = n;  // macro trickery for not clobbering r7 in thumb mode (where it may be the frame pointer)
    register long r0 __asm__("r0") = a;
    register long r1 __asm__("r1") = b;
    register long r2 __asm__("r2") = c;
    __asm_syscall(R7_OPERAND, "0"(r0), "r"(r1), "r"(r2));
 // FIXME: add a "memory" clobber because pointed-to memory can be an input or output
}
Run Code Online (Sandbox Code Playgroud)

不幸的是,这不是严格安全的:这不会告诉编译器指针操作数被取消引用,因此它可能会将之前的write()存储视为死存储并将它们优化掉!

这很容易解决:添加一个"memory"clobber。

IDK 如果那是 glibc 删除其类似系统调用宏并仅提供非内联系统调用函数的动机的一部分。或者,也许他们不想鼓励人们将系统调用 ABI 嵌入到他们的程序中,这样理论上它可以在未来变得更高效。

你会像这样使用它

#include <asm/unistd.h>   // for __NR_write
#include <stdlib.h>       // for ssize_t
#include "syscall_arch.h"

// doesn't set errno or force all error returns to -1
// return values from -1 to -4095 are errors, e.g. -EBADF or -EFAULT

__attribte__((noinline))  // hack for inline asm unsafety
ssize_t my_write(int fd, const void *buf, size_t count) {
    return __syscall3(__NR_write, fd, (long)buf, count);
}
Run Code Online (Sandbox Code Playgroud)

我把它放在 Godbolt 编译器资源管理器上,并syscall_arch.h复制了足够的 ARM来进行编译。Godbolt 的一些 ARM gcc 安装缺少<asm/unistd.h>,但 gcc5.4 有一个可以工作的。ARM模式下的结果是:

my_write:
    str     r7, [sp, #-4]!
    mov     r7, #4
@ system-calling convention mostly matches function-calling convention
@ so args are in the right registers already
    svc 0
    ldr     r7, [sp], #4
    bx      lr
Run Code Online (Sandbox Code Playgroud)

当然,这个函数可以内联到调用者中,因此r7整个函数的保存/恢复发生一次。

(编辑):如果内联到死商店可以优化掉的调用者,这将是不安全的。 更好的蛮力选项是内联 asm 语句上的内存破坏,或者更多的工作是为读取或写入用户空间内存的系统调用添加虚拟内存操作数(请参阅at&t asm inline c++ 问题)。 或者munmap确保没有存储到被释放的页面中,并在内存未映射后发生。