Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/64.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
在内联C程序集中执行系统调用会导致segfault_C_Ubuntu_Assembly_System Calls - Fatal编程技术网

在内联C程序集中执行系统调用会导致segfault

在内联C程序集中执行系统调用会导致segfault,c,ubuntu,assembly,system-calls,C,Ubuntu,Assembly,System Calls,我最近涉猎了低级编程,想做一个接受(CType-rax、CType-rbx、CType-rcx、CType-rdx)的函数somesyscall。struct CType看起来像: /* TYPES: 0 int 1 string 2 bool */ typedef struct { void* val; int typev; } CType; 函数有点混乱,但理论上应该可以工作: #include <errno.h

我最近涉猎了低级编程,想做一个接受
(CType-rax、CType-rbx、CType-rcx、CType-rdx)的函数
somesyscall
。struct CType看起来像:

/*
    TYPES:
        0 int
        1 string
        2 bool
*/
typedef struct {
    void* val;
    int typev;
} CType;
函数有点混乱,但理论上应该可以工作:

#include <errno.h>
#include <stdbool.h>
#include "ctypes.h"

//define functions to set registers
#define seteax(val) asm("mov %0, %%rax" :: "g" (val) : "%rax")
#define setebx(val) asm("mov %0, %%rbx" :: "g" (val) : "%rbx")
#define setecx(val) asm("mov %0, %%rcx" :: "g" (val) : "%rcx")
#define setedx(val) asm("mov %0, %%rdx" :: "g" (val) : "%rdx")
///////////////////////////////////

#define setregister(value, register)       \
switch (value.typev) {                     \
    case 0: {                              \
        register(*((double*)value.val));   \
        break;                             \
    }                                      \
    case 1: {                              \
        register(*((char**)value.val));    \
        break;                             \
    }                                      \
    case 2: {                              \
        register(*((bool*)value.val));     \
        break;                             \
    }                                      \
}

static inline long int somesyscall(CType a0, CType a1, CType a2, CType a3) {

    //set the registers
    setregister(a0, seteax);
    setregister(a1, setebx);
    setregister(a2, setecx);
    setregister(a3, setedx);
    ///////////////////

    asm("int $0x80"); //interrupt

    //fetch back the rax
    long int raxret;
    asm("mov %%rax, %0" : "=r" (raxret));

    return raxret;
}
并使用


我犯了一个错误。然而,一切看起来都是正确的。我在这里做错了什么吗?

在宏扩展之后,您将看到类似

long int raxret;

asm("mov %0, %%rax" :: "g" (a0) : "%rax");
asm("mov %0, %%rbx" :: "g" (a1) : "%rbx");
asm("mov %0, %%rcx" :: "g" (a2) : "%rcx");
asm("mov %0, %%rdx" :: "g" (a3) : "%rdx");
asm("int $0x80");
asm("mov %%rax, %0" : "=r" (raxret));
这不起作用,因为您没有告诉编译器,在
asm
语句序列期间,不允许将
rax
rbx
rcx
rdx
用于其他内容。例如,寄存器分配器可能会决定将
a2
从堆栈复制到
rax
,然后使用
rax
作为
mov%0、%%rcx
指令的输入操作数——对放入
rax
的值进行压缩

(没有输出的asm语句是无效的,因此前5个语句不能相对彼此重新排序,但最后一个语句可以移动到任何位置。例如,在以后的代码之后,可以移动到编译器可以方便地在其选择的寄存器中生成
raxret
。此时RAX可能不再具有系统调用返回值-您需要告诉编译器输出来自实际生成它的asm语句,而不假设asm语句之间存在任何寄存器。)

有两种不同的方法告诉编译器不要这样做:

  • 仅将
    int
    指令放在asm中,并用约束字母表示在哪个寄存器中的所有要求:

    asm volatile ("int $0x80" 
        : "=a" (raxret)                              // outputs
        : "a" (a0), "b" (a1), "c" (a2), "d" (a3)     // pure inputs
        : "memory", "r8", "r9", "r10", "r11"         // clobbers
         // 32-bit int 0x80 system calls in 64-bit code zero R8..R11
         // for native "syscall", clobber "rcx", "r11".
     );
    
    对于这个简单的示例,这是可能的,但一般来说并不总是可能的,因为每个寄存器都没有约束字母,特别是在除x86之外的CPU上

         // use the native 64-bit syscall ABI
         // remove the r8..r11 clobbers for 32-bit mode
    
  • 仅将
    int
    指令放在asm中,并用以下内容表达进入寄存器的要求:

    无论您需要使用哪个寄存器,这都会起作用。它可能与您试图用来擦除类型的宏更兼容

  • 顺便说一句,如果这是64位x86/Linux,那么参数属于ABI标准传入参数寄存器(按该顺序为rdi、rsi、rdx、rcx、r8、r9),而不是rbx、rcx、rdx等。不过,系统调用号仍然是rax。(如果您为特定的系统调用编写包装,请使用
    #include中的调用号码告诉GCC哪些内存可能被读取或写入,而不是
    “内存”
    clobber)

    一些无指针的情况包括
    getpid
    ,在这种情况下,避免往返内核模式会快得多,就像glibc对适当的系统调用所做的那样。这也适用于使用指针的
    clock\u gettime



    顺便说一句,请注意实际的内核接口与C库包装器提供的接口不匹配。这通常记录在手册页的“注释”部分中,例如,对于和

    ,在宏扩展之后,您将有如下内容

    long int raxret;
    
    asm("mov %0, %%rax" :: "g" (a0) : "%rax");
    asm("mov %0, %%rbx" :: "g" (a1) : "%rbx");
    asm("mov %0, %%rcx" :: "g" (a2) : "%rcx");
    asm("mov %0, %%rdx" :: "g" (a3) : "%rdx");
    asm("int $0x80");
    asm("mov %%rax, %0" : "=r" (raxret));
    
    这不起作用,因为您没有告诉编译器,在
    asm
    语句序列期间,不允许将
    rax
    rbx
    rcx
    rdx
    用于其他内容。例如,寄存器分配器可能决定将
    a2
    从堆栈复制到
    rax
    ,并且然后使用
    rax
    作为
    mov%0、%%rcx
    指令的输入操作数--删除输入
    rax
    的值

    (没有输出的asm语句是无效的,因此前5个语句不能相对彼此重新排序,但最后一个语句可以移动到任何位置。例如,在以后的代码之后,可以移动到编译器可以方便地在其选择的寄存器中生成
    raxret
    。此时RAX可能不再具有系统调用返回值-您需要告诉编译器输出来自实际生成它的asm语句,而不假设asm语句之间存在任何寄存器。)

    有两种不同的方法告诉编译器不要这样做:

  • 仅将
    int
    指令放在asm中,并用约束字母表示在哪个寄存器中的所有要求:

    asm volatile ("int $0x80" 
        : "=a" (raxret)                              // outputs
        : "a" (a0), "b" (a1), "c" (a2), "d" (a3)     // pure inputs
        : "memory", "r8", "r9", "r10", "r11"         // clobbers
         // 32-bit int 0x80 system calls in 64-bit code zero R8..R11
         // for native "syscall", clobber "rcx", "r11".
     );
    
    对于这个简单的示例,这是可能的,但一般来说并不总是可能的,因为每个寄存器都没有约束字母,特别是在除x86之外的CPU上

         // use the native 64-bit syscall ABI
         // remove the r8..r11 clobbers for 32-bit mode
    
  • 仅将
    int
    指令放在asm中,并用以下内容表达进入寄存器的要求:

    无论您需要使用哪个寄存器,这都会起作用。它可能与您试图用来擦除类型的宏更兼容

  • 顺便说一句,如果这是64位x86/Linux,那么参数属于ABI标准传入参数寄存器(按该顺序为rdi、rsi、rdx、rcx、r8、r9),而不是rbx、rcx、rdx等。不过,系统调用号仍然是rax。(如果您为特定的系统调用编写包装,请使用
    #include中的调用号码告诉GCC哪些内存可能被读取或写入,而不是
    “内存”
    clobber)

    一些无指针的情况包括
    getpid
    ,在这种情况下,避免往返内核模式会快得多,就像glibc对适当的系统调用所做的那样。这也适用于使用指针的
    clock\u gettime



    顺便说一句,请注意实际的内核接口与C库包装器提供的接口不匹配。这通常记录在手册页的注释部分,例如for和

    请注意,32位
    int$0x80
    API(实际上是64位代码)使用了它的参数