问题
I need to use syscall internally in Android NDK to prevent hooking of the wrapper functions. In Linux there are macros like SYSCALL_INLINE which allows using syscall without wrapper function. Thus the macro embeds the syscall assembly code into the project directly.
I could not find similar macro in Android NDK.
Maybe I can write my own functions like this one; https://git.busybox.net/uClibc/tree/libc/sysdeps/linux/arm/syscall.c
But I need to have arm, arm_64, x86 and x86_64 versions of the same function.
Can you help me? How can I find a solution?
回答1:
Android's Linux kernel still uses the same system-call numbers and ABI as regular Linux, doesn't it? (So How to access the system call from user-space?) So you should be able to use the normal methods, with call numbers from <asm/unistd.h>
.
You could use the MUSL libc syscall inline functions in arch/x86_64/syscall_arch.h. It has different ones for each different number of args, instead of one big one.
MUSL has versions of syscall_arch.h
for ARM, AArch64, i386, and x86-64, as well as other architectures it supports. It's licensed under a permissive MIT license, so you can just copy those headers.
For example, their ARM version has
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
}
Unfortunately this is not strictly safe: this doesn't tell the compiler that pointer operands are dereferenced, so it might treat stores into a buffer before write()
as dead stores and optimize them away!
This is trivial to fix: add a "memory"
clobber.
IDK if that was part of glibc's motivation for removing its similar syscall macros and only providing a non-inline syscall function. Or maybe they didn't want to encourage people to embed the system-call ABI into their program so it could in theory change to become more efficient in the future.
You'd use it like
#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);
}
I put this on the Godbolt compiler explorer with enough of ARM syscall_arch.h
copied in to make this compile. Some of Godbolt's ARM gcc installs have missing <asm/unistd.h>
, but gcc5.4 has a working one. The result in ARM mode is:
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
And of course this function can inline into a caller so the save/restore of r7
happens once for the whole function.
(edit): this would be unsafe if inlined into a caller where dead stores could optimize away. A better brute-force option would be a memory clobber on the inline asm statement, or more work would be to add a dummy memory operand for system calls that read or write user-space memory (see at&t asm inline c++ problem). Or for munmap
to make sure no stores into the page(s) being freed sink past it and happen after the memory is unmapped.
来源:https://stackoverflow.com/questions/50576767/syscall-inline-in-android