Linux syscalls и errno
контекст: я пытаюсь написать небольшую программу C со встроенным asm, которая должна работать под Linux в системе x86_64 и компилироваться с gcc, чтобы лучше понять, как syscalls работают под Linux.
мой вопрос: как номера ошибок возвращаются из syscall (например, write) в этой среде? Я понимаю, что когда я использую библиотеку, такую как glibc, она заботится о сохранении результирующего кода ошибки в global errno
переменной. Но где номер ошибки, сохраненный при вызове syscall непосредственно через встроенный ассемблер? Будет ли он храниться внутри отдельного регистра или будет закодирован в %rax
?
давайте возьмем запись syscall на linux в качестве примера:
при вызове write
затем после возвращения syscall я нахожу, что он хранит 0xfffffffffffffff2
внутри %rax
, мне нужно
как-то извлечь код ошибки из этого?
если у меня есть номер кода ошибки, где я должен искать, чтобы определить фактический ошибки, что происходило? Допустим, я получаю возвращенное число 5, с каким заголовочным файлом мне нужно проконсультироваться, чтобы найти соответствующее символическое имя ошибки.
Я вызываю запись syscall следующим образом:
asm ("mov ,%%rax;"
"mov ,%%rdi;"
"mov %1,%%rsi;"
"mov %2,%%rdx;"
"syscall;"
"mov %%rax,%0;"
: "=r" (result)
: "r" (msg), "r" (len)
: "%rdx", "%rsi", "%rax", "%rdi" /* EDIT: this is needed or else the registers will be overwritten */
);
С result
, msg
и len
определен следующим образом:
long result = 0;
char* msg = "Hello Worldn";
long len = 12;
4 ответов
соглашение linux syscall заключается в том, что они кодируют как возможный код ошибки, так и возвращаемое значение для успешного вызова в возвращаемом значении. Это просто обертки glibc или других библиотек C, которые они установят errno
к коду ошибки, возвращаемому подчеркиванием syscall, и оболочка вернет -1
. Взять write
в качестве примера ядро выполняет обработку ошибок, аналогичную этой:
ssize_t write(int fd, ...) {
if (fd is not valid)
return -EBADF;
return do_write(...);
}
так как вы можете видеть, код ошибки находится только в возвращаемом значении, и в зависимости от семантики всегда есть способ проверить, удалось ли syscall или нет, сравнивая его со значением, невозможным для успешной работы. Для большинства syscalls, как write
один, это означает, что проверьте, если он отрицательный.
Соглашения О Вызове Архитектуры
как вы уже догадались, вы не можете использовать errno
потому что это в glibc специфичных. Информация, которую вы хотите будет в rax
если это x86_64
. Мужская страница man 2 syscall
имеет следующее объяснение:
Architecture calling conventions
Every architecture has its own way of invoking and passing arguments
to the kernel. The details for various architectures are listed in
the two tables below.
The first table lists the instruction used to transition to kernel
mode (which might not be the fastest or best way to transition to the
kernel, so you might have to refer to vdso(7)), the register used to
indicate the system call number, the register used to return the
system call result, and the register used to signal an error.
arch/ABI instruction syscall # retval error Notes
────────────────────────────────────────────────────────────────────
alpha callsys v0 a0 a3 [1]
arc trap0 r8 r0 -
arm/OABI swi NR - a1 - [2]
arm/EABI swi 0x0 r7 r0 -
arm64 svc #0 x8 x0 -
blackfin excpt 0x0 P0 R0 -
i386 int x80 eax eax -
ia64 break 0x100000 r15 r8 r10 [1]
m68k trap #0 d0 d0 -
microblaze brki r14,8 r12 r3 -
mips syscall v0 v0 a3 [1]
nios2 trap r2 r2 r7
parisc ble 0x100(%sr2, %r0) r20 r28 -
powerpc sc r0 r3 r0 [1]
s390 svc 0 r1 r2 - [3]
s390x svc 0 r1 r2 - [3]
superh trap #0x17 r3 r0 - [4]
sparc/32 t 0x10 g1 o0 psr/csr [1]
sparc/64 t 0x6d g1 o0 psr/csr [1]
tile swint1 R10 R00 R01 [1]
x86_64 syscall rax rax - [5]
x32 syscall rax rax - [5]
xtensa syscall a2 a2 -
и :
[5] The x32 ABI uses the same instruction as the x86_64 ABI and
is used on the same processors. To differentiate between
them, the bit mask __X32_SYSCALL_BIT is bitwise-ORed into the
system call number for system calls under the x32 ABI. Both
system call tables are available though, so setting the bit
is not a hard requirement.
(на этой справочной странице приведена таблица, показывающая, как передавать аргументы системным вызовам следует. Интересное чтение.)
как номера ошибок возвращаются из syscall (например, write) в этой среде?:
вы должны проверить ваш rax
Регистрация возвращаемого значения.
в Linux неудачный системный вызов с использованием инструкции сборки syscall вернет значение-errno в регистре rax. Поэтому в вашем случае 0-0xfffffffffffffff2 == 0xE, который равен 14. Итак, ваш errno-14.
Как вы находите, что означает errno 14? Вы должны искать google "таблица кода ошибки Linux" или искать в errno.h и вы найдете ответ.
посмотреть здесь: http://www.virtsync.com/c-error-codes-include-errno
согласно этому таблица 14-EFAULT, что означает "плохой адрес".
IIRC в x86-64 ABI передается ошибка из syscall с набором битов переноса. Тогда eax
содержит errno
код.
Я бы предложил изучить нижние слои исходного кода некоторой библиотеки libc, например musl-библиотеки libc