Ядро Linux Android все еще использует те же номера системных вызовов и ABI, что и обычный Linux, не так ли?(Итак Как получить доступ к системному вызову из пространства пользователя? ). Таким образом, вы должны иметь возможность использовать обычные методы с номерами вызовов от <asm/unistd.h>
.
. Вы можете использовать MUSL.Встроенные функции системного вызова libc в arch/x86_64/syscall_arch.h
.Он имеет разные значения для каждого различного числа аргументов, вместо одного большого.
MUSL имеет версии syscall_arch.h
для ARM , 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
}
К сожалению, этоне совсем безопасно: это не говорит компилятору, что операнды указателя разыменовываются, поэтому он может обработать хранилища в буфере до write()
как мертвые хранилища и оптимизировать их!
Этоисправить тривиально: добавьте "memory"
clobber.
IDK, если это было частью мотивации glibc по удалению похожих макросов syscall и предоставлению только функции in-syscall ,Или, может быть, они не хотели поощрять людей встраивать системный вызов 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);
}
Я поместил это в проводник компилятора Godbolt с достаточным количеством 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
происходит один раз для всей функции.
(редактировать): это было бы небезопасно, если бы он был встроен в вызывающую программу, где можно было бы оптимизировать мертвые хранилища Лучшей опцией грубой силы будет расширение памяти в встроенном операторе asm или дополнительная работа по добавлению фиктивного операнда памяти для системных вызовов, которые читают или записывают память пользовательского пространства (см. at & t asm inlinec ++ проблема ). Или для munmap
, чтобы убедиться, что никакие сохраненные на странице (страницах) страницы не проходят мимо нее и происходят после того, как память не отображена.