SYSCALL_INLINE 在 Android

SYSCALL_INLINE in Android

我需要在 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 版本。

你能帮帮我吗?我怎样才能找到解决方案?

Android 的 Linux 内核仍然使用与常规 Linux 相同的 system-call 数字和 ABI,不是吗? (所以 How to access the system call from user-space?)所以你应该能够使用正常的方法,调用号码来自 <asm/unistd.h>.

您可以在 arch/x86_64/syscall_arch.h 中使用 MUSL libc 系统调用内联函数。每个不同数量的参数都有不同的参数,而不是一个大参数。


MUSL 有 ARM, AArch64, i386, and x86-64, as well as other architectures it supports. It's licensed under a permissive MIT licensesyscall_arch.h 版本,因此您只需复制 headers.

比如他们的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
}

不幸的是,这并不严格安全:这不会告诉编译器指针操作数已被取消引用,因此它可能会将 write() 之前的缓冲区中的存储视为死存储并对其进行优化离开!

这很容易修复:添加一个 "memory" 破坏。

IDK 如果那是 glibc 删除其类似系统调用宏并仅提供 non-inline 系统调用 函数 的动机的一部分。或者他们可能不想鼓励人们将 system-call 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);
}

我把这个 on the Godbolt compiler explorer 和足够的 ARM syscall_arch.h 复制进去进行编译。 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

当然,这个函数可以内联到调用者中,因此 r7 的 save/restore 对整个函数只发生一次。

(edit):如果内联到死存储可以优化掉的调用者中,这将是不安全的。 更好的 brute-force 选项是内联 asm 语句上的内存破坏,或者更多的工作是为读取或写入 user-space 内存的系统调用添加虚拟内存操作数(请参阅at&t asm inline c++ problem). 或者 munmap 以确保没有存储到正在释放的页面中的存储通过它并在内存未映射后发生。