我正在尝试使用内联汇编... 我读了这页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 */
);
}
答案 0 :(得分:13)
嗯,你没有特别说,但是你的帖子看起来好像你正在使用gcc及其内联asm和约束语法(其他C编译器有非常不同的内联语法)。也就是说,您可能需要使用AT& T汇编语法而不是英特尔,因为这是与gcc一起使用的。
所以用上面说过,让我们来看看你的write2函数。首先,你不想创建一个堆栈框架,因为gcc会创建一个,所以如果你在asm代码中创建一个,你最终会得到两个框架,事情可能会变得很困惑。其次,由于gcc正在布置堆栈帧,因此您无法访问带有“[ebp + offset]”的变量,因为您不知道它是如何布局的。
这就是约束的含义 - 你说你希望gcc在什么样的地方放置值(任何寄存器,内存,特定寄存器)和在asm代码中使用“%X”。最后,如果在asm代码中使用显式寄存器,则需要在第3部分中列出它们(在输入约束之后),因此gcc知道您正在使用它们。否则它可能会在其中一个寄存器中放入一些重要的值,你就会破坏这个值。
您还需要告诉编译器内联asm将或可能读取或写入由输入操作数指向的内存;那是不暗示。
所以,你的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和%
。
现在这将有效,但效率低,因为它将包含许多额外的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");
}
甚至更好
// 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
,但是当真正的目的不是为了计算某些东西时,使其显式不会受到伤害;它是像系统调用那样的副作用。)< / p>
修改强>
最后一点注意事项 - 此函数正在执行写入系统调用,它会返回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示例,如果您将其删除则会中断; this answer以获取有关虚拟内存输入/输出的更多详细信息。
使用此输出操作数,您需要显式volatile
- 每次write
语句“运行”在源中时,只需要一个asm
系统调用。否则,允许编译器假定它仅存在以计算其返回值,并且可以消除具有相同输入的重复调用而不是写入多行。 (如果没有检查返回值,则完全删除它。)