Linux系统调用和错误

时间:2016-05-11 15:29:14

标签: c linux system-calls

上下文:我正在尝试编写一个带有内联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 */
    );

resultmsglen定义如下:

long result = 0;
char* msg = "Hello World\n";
long len = 12;

4 个答案:

答案 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)

在x86-64 ABI中的 IIRC,在进位设置的情况下从系统调用发送错误。然后eax包含errno代码。

我建议研究某些libc库的源代码的较低层,如musl-libc