当前位置: 首页 > 面试题库 >

在GNU C内联汇编中编写Linux int 80h系统调用包装器

聂炜
2023-03-14
问题内容

我正在尝试使用内联汇编…我已阅读此页面http://www.codeproject.com/KB/cpp/edujini_inline_asm.aspx,但是我无法理解传递给函数的参数。

我正在写一个C编写示例。这是我的函数标头:

write2(char *str, int len){
}

这是我的汇编代码:

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

我该怎么做才能将该代码传递给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)
    );
}

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

global main
main:
    mov ebx, 5866       ;PID
    mov ecx, 9      ;SIGKILL
    mov eax, 37     ;sys_kill
    int 0x80        ;interruption
    ret

如何将代码内联到我的代码中..这样我就可以向用户请求pid。

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

问题答案:

好吧,您没有特别说明,但是从您的帖子看来,您似乎正在使用带有约束语法的gcc及其内联asm(其他C编译器具有非常不同的内联语法)。就是说,您可能需要使用AT&T汇编语法而不是Intel,因为这就是gcc使用的语法。

因此,根据以上所述,让我们看一下您的write2函数。首先,您不想创建一个堆栈框架,因为gcc会创建一个堆栈框架,因此,如果在asm代码中创建一个堆栈框架,最终将得到两个框架,事情可能会变得非常混乱。其次,由于gcc正在布置堆栈框架,因此您无法使用“
[ebp + offset]”访问var,因为您不知道它的布置方式。

这就是约束的目的-
您说您希望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
     );
}

请注意AT&T语法-src,dest而不是dest,src,并且%在寄存器名称之前。

现在这可以工作,但是效率低下,因为它将包含许多额外的动作。通常,您永远不要在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");
}

甚至更好

// 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");
}

还要注意volatile,需要使用它来告诉编译器,即使没有使用其输出(没有输出),也不能将其消除为无效。(asm没有输出操作数已经是隐式的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;
}

所有系统调用均以EAX返回。从-4095到的值-1(包括)是负errno代码,其他值是无错误。(这全局适用于所有Linux系统调用)。

如果要编写通用的系统调用包装器,则可能需要使用"memory"Clobber,因为不同的系统调用具有不同的指针操作数,并且可能是输入或输出。请参见https://godbolt.org/z/GOXBue,以获取一个示例,该示例在您将其遗漏的情况下会中断,并且此答案提供了有关虚拟内存输入/输出的更多详细信息。

使用此输出操作数,每次在源中执行“语句”语句时,就需要显式的volatile-恰好一个write系统调用asm。否则,允许编译器假定其存在只是为了计算其返回值,并且可以消除具有相同输入的重复调用,而不必编写多行。(或者,如果不检查返回值,则将其完全删除。)



 类似资料:
  • 问题内容: 我们如何直接在x86 Linux中使用sysenter / syscall来实现系统调用?有人可以提供帮助吗?如果您还可以显示amd64平台的代码,那就更好了。 我知道在x86中,我们可以使用 间接路由到sysenter。 但是,我们如何直接使用sysenter / syscall进行编码以发出系统调用? 我找到了一些资料http://damocles.blogbus.com/tag/

  • 问题内容: 说,我想在gcc的内联汇编中调用具有以下签名的函数。我怎样才能做到这一点? 问题答案: 通常,您会想要做类似的事情 也就是说,您根本不需要在嵌入式asm中进行函数调用。这样,您不必担心调用约定或堆栈框架管理的细节。

  • 问题内容: 我想知道是否可以在Swift中编写内联汇编。 我知道在Objective-C中,您可以使用如下代码: 但是在Swift中似乎无法使用 。 有谁知道如何使用,如果可能的话。我没有找到任何关于它的信息,所以我认为这是一个很好的问题。 问题答案: 要扩展Robert Levy所说的内容,您可以只使用Swift / Obj-C互操作功能,并编写一个可以处理ASM内容的Obj- C类,然后可以从

  • 为了极端底层操作和性能要求,你可能希望直接控制 CPU。Rust 通过asm!宏来支持使用内联汇编。 asm!(assembly template : output operands : input operands : clobbers : options ); 任何asm的使用需要功能通道(需要在包装箱上加上#![feature(asm)]来允许使用)并且当然也

  • 为了增强对语言的细粒度的控制,特别是在写通用库时,可以在一个语言中交错使用Solidity的语句来接近其中一个虚拟机。但由于EVM是基于栈执行的,所以有时很难定位到正确的栈槽位,从而提供正确的的参数或操作码。Solidit的内联汇编尝试解决这个问题,但也引入了其它的问题,当你通过下述特性进行手动的汇编时: 函数式的操作码:mul(1, add(2, 3))代替push1 3 push1 2 add

  • 5. C内联汇编 用C写程序比直接用汇编写程序更简洁,可读性更好,但效率可能不如汇编程序,因为C程序毕竟要经由编译器生成汇编代码,尽管现代编译器的优化已经做得很好了,但还是不如手写的汇编代码。另外,有些平台相关的指令必须手写,在C语言中没有等价的语法,因为C语言的语法和概念是对各种平台的抽象,而各种平台特有的一些东西就不会在C语言中出现了,例如x86是端口I/O,而C语言就没有这个概念,所以in/