在C / C ++中使用内联汇编
我正在尝试使用内联汇编…我读过这个页面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汇编语法而不是英特尔,因为这是与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;" "movl $1, %%ebx;" "movl %0, %%ecx;" "movl %1, %%edx;" "int $0x80" :: "g" (str), "g" (len) : "eax", "ebx", "ecx", "edx"); }
注意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"); }
甚至更好
void write2(char *str, int len) { __asm__ volatile ("int $0x80" :: "a" (4), "b" (1), "c" (str), "d" (len)); }
还要注意使用volatile
来告诉编译器,即使没有使用它的输出(没有输出),也不能将其消除。
编辑
最后一个注意事项 – 此函数正在执行写入系统调用,它会返回eax中的值 – 写入的字节数或错误代码。 所以你可以通过输出约束得到它:
int write2(char *str, int len) { __asm__ ("int $0x80" : "=a" (len) : "a" (4), "b" (1), "c" (str), "d" (len)); return len; }
使用实际输出,您可能会或可能不需要volatile – 如果不使用返回值,则不会让编译器使用死代码来消除写入。 但是你总是检查错误的返回值,对吗?