在C/C++中使用内联汇编

Rod*_*oCR 9 c linux x86 assembly inline-assembly

我正在尝试使用内联汇编...我读过这个页面http://www.codeproject.com/KB/cpp/edujini_inline_asm.aspx但我无法理解传递给我的函数的参数.

我正在写一个C写的例子..这是我的函数头:

write2(char *str, int len){
}
Run Code Online (Sandbox Code Playgroud)

这是我的汇编代码:

global write2
write2:
    push ebp
    mov ebp, esp
    mov eax, 4      ;sys_write
    mov ebx, 1      ;stdout
    mov ecx, [ebp+8]    ;string pointer
    mov edx, [ebp+12]   ;string size
    int 0x80        ;syscall
    leave
    ret
Run Code Online (Sandbox Code Playgroud)

我该怎么做才能将代码传递给C函数...我正在做这样的事情:

write2(char *str, int len){
    asm ( "movl 4, %%eax;"
          "movl 1, %%ebx;"
          "mov %1, %%ecx;"
          //"mov %2, %%edx;"
          "int 0x80;"
           :
           : "a" (str), "b" (len)
    );
}
Run Code Online (Sandbox Code Playgroud)

那是因为我没有输出变量,所以我该如何处理呢?此外,使用此代码:

global main
main:
    mov ebx, 5866       ;PID
    mov ecx, 9      ;SIGKILL
    mov eax, 37     ;sys_kill
    int 0x80        ;interruption
    ret 
Run Code Online (Sandbox Code Playgroud)

如何将代码内联到我的代码中...所以我可以向用户请求pid ..就像这样..这是我的预编码

void killp(int pid){
    asm ( "mov %1, %%ebx;"
          "mov 9, %%ecx;"
          "mov 37, %%eax;"
           :
           : "a" (pid)         /* optional */
    );
}
Run Code Online (Sandbox Code Playgroud)

Chr*_*odd 13

好吧,你没有特别说,但是你的帖子看起来好像你正在使用gcc及其内联asm和约束语法(其他C编译器具有非常不同的内联语法).也就是说,您可能需要使用AT&T汇编语法而不是英特尔,因为这是与gcc一起使用的.

所以用上面说过,让我们来看看你的write2函数.首先,你不想创建一个堆栈框架,因为gcc会创建一个,所以如果你在asm代码中创建一个,你最终会得到两个框架,事情可能会变得很困惑.其次,由于gcc正在布局堆栈框架,你无法访问带有"[ebp + offset]"广告的变量,你不知道它是如何布局的.这就是约束的含义 - 你说你希望gcc在什么样的地方放置值(任何寄存器,内存,特定寄存器)和在asm代码中使用"%X".最后,如果在asm代码中使用显式寄存器,则需要在第3部分中列出它们(在输入约束之后),因此gcc知道您正在使用它们.否则它可能会在其中一个寄存器中放入一些重要的值,你就会破坏这个值.

所以,你的write2函数看起来像:

void write2(char *str, int len) {
    __asm__ volatile (
        "movl $4, %%eax;"      // SYS_write
        "movl $1, %%ebx;"      // file descriptor = stdout_fd
        "movl %0, %%ecx;"
        "movl %1, %%edx;"
        "int $0x80"
        :: "g" (str), "g" (len)       // input values we MOV from
        : "eax", "ebx", "ecx", "edx", // registers we destroy
          "memory"                    // memory has to be in sync so we can read it
     );
}
Run Code Online (Sandbox Code Playgroud)

注意AT&T语法--src,dest而不是dest,src和%寄存器名称之前.

现在这将有效,但效率低,因为它将包含许多额外的mov.一般情况下,你不应该在asm代码中使用mov指令或显式寄存器,因为你最好使用约束来说出你想要的东西,并让编译器确保它们在那里.这样,优化器可能可以摆脱大多数mov,特别是如果它内联函数(如果你指定-O3将会这样做).方便的是,i386机器模型具有特定寄存器的约束,因此您可以改为:

void write2(char *str, int len) {
    __asm__ volatile (
        "movl $4, %%eax;"
        "movl $1, %%ebx;"
        "int $0x80"
        :: "c" (str), /* c constraint tells the compiler to put str in ecx */
           "d" (len)  /* d constraint tells the compiler to put len in edx */
        : "eax", "ebx", "memory");
}
Run Code Online (Sandbox Code Playgroud)

甚至更好

// UNSAFE: destroys EAX (with return value) without telling the compiler
void write2(char *str, int len) {
    __asm__ volatile ("int $0x80"
        :: "a" (4), "b" (1), "c" (str), "d" (len)
        : "memory");
}
Run Code Online (Sandbox Code Playgroud)

还要注意volatile,需要使用它来告诉编译器即使没有使用它的输出(其中没有输出),也不能将其作为死区消除.

编辑

最后要注意的是 - 这个函数正在执行一个写系统调用,它会在eax中返回一个值 - 写入的字节数或错误代码.所以你可以通过输出约束得到它:

int write2(const char *str, int len) {
    __asm__ volatile ("int $0x80" 
     : "=a" (len)
     : "a" (4), "b" (1), "c" (str), "d" (len),
       "m"( *(const char (*)[])str )       // "dummy" input instead of memory clobber
     );
    return len;
}
Run Code Online (Sandbox Code Playgroud)

使用实际输出,您可能会或可能不需要volatile - 如果不使用返回值,则不会让编译器使用死代码来消除写入.但是你总是检查错误的返回值,对吗?

  • 您可以将intel语法与".intel_syntax"指令一起使用. (2认同)