上下文:我正在尝试编写一个带有内联asm的小型C程序,该程序应该在x86_64系统上的Linux下运行并使用gcc编译,以便更好地理解系统调用在Linux下的工作方式。 / p>
我的问题是:在这种环境中,如何从系统调用(例如写入)返回错误号码?我知道当我使用像glibc这样的库时,它会在全局errno
变量中保存生成的错误代码。但是当我直接通过内联汇编程序调用系统调用时,存储的错误号在哪里?它是存储在单独的寄存器中,还是以%rax
编码?
让我们以linux上的write syscall为例:
当调用write
然后在系统调用返回后我发现它将0xfffffffffffffff2
存储在%rax
内,我是否需要
以某种方式从中提取错误代码?
如果我有错误代码编号,我应该在哪里找出发生的实际错误?可以说我得到了返回的数字5,我需要咨询哪个头文件才能找到相应的符号错误名称。
我正在调用这样的写系统调用:
asm ("mov $1,%%rax;"
"mov $1,%%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 World\n";
long len = 12;
答案 0 :(得分:7)
linux系统调用的惯例是它们在返回值中编码可能的错误代码和成功调用的返回值。它只是glibc或其他C库的包装器,它们将errno
设置为由下划线系统调用返回的错误代码,并且包装器将返回-1
。以write
为例,内核执行与此类似的错误处理:
ssize_t write(int fd, ...) {
if (fd is not valid)
return -EBADF;
return do_write(...);
}
正如您所看到的,错误代码只在返回值中,并且根据语义,始终有一种方法可以通过将系统调用与成功操作不可能的值进行比较来检查系统调用是否成功。对于大多数系统调用,例如write
调用,这意味着检查它是否为负数。
答案 1 :(得分:5)
正如您已经猜到的那样,您无法使用errno
,因为它的 GLibC specific 。如果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 $0x80 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]
:
[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.
(在该手册页中,显示了如何将参数传递给系统调用的表格。这是一个有趣的阅读。)
如何在此环境中从系统调用(例如写入)返回错误编号?:
您必须检查rax
寄存器的返回值。
答案 2 :(得分:3)
在Linux上,使用syscall汇编指令的失败系统调用将在rax寄存器中返回值-errno。所以在你的情况下0-0xfffffffffffffff2 == 0xE这是14.所以你的错误是14。
你如何找到errno 14的含义?你应该谷歌搜索“Linux错误代码表”或查看errno.h,你会找到答案。
看看这里: http://www.virtsync.com/c-error-codes-include-errno
根据该表,14是EFAULT,意思是“坏地址”。
答案 3 :(得分:1)
eax
包含errno
代码。
我建议研究某些libc库的源代码的较低层,如musl-libc