栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 面试经验 > 面试问答

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

面试问答 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

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

好吧,您没有特别说明,但是从您的帖子看来,您似乎正在使用带有约束语法的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), "d" (len)          : "eax", "ebx", "memory");}

甚至更好

// UNSAFE: destroys EAX (with return value) without telling the compilervoid 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
。否则,允许编译器假定其存在只是为了计算其返回值,并且可以消除具有相同输入的重复调用,而不必编写多行。(或者,如果不检查返回值,则将其完全删除。)



转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/408611.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号