执行 "Hello world!" 时导致段错误的简单内联汇编
Simple inline assembly resulting in segfault when executing "Hello world!"
我正在尝试让一些简单的 C 内联汇编在 here 上工作。但是每次我尝试执行它时都会收到段错误。我正在使用 OS X,所以我不得不更改系统调用编号。使用 gdb 我想我确定了罪魁祸首:执行退出系统调用后,它尝试执行存储 "Hello world!" 字符串的部分:
void main() {
__asm__ (
// print Hello World
"movl , %eax;\n" /* 4 is the syscall number for write on osx */
"movl , %ebx;\n" /* 1 is stdout and is the first argument */
// "movl $message, %esi;\n" /* load the address of string into the second argument*/
// instead use this to load the address of the string
// as 16 bytes from the current instruction
"leal 16(%eip), %ecx;\n"
"movl , %edx;\n" /* third argument is the length of the string to print*/
"syscall;\n"
// call exit (1 on osx) (so it doesn't try to run the string Hello World
"movl ,%eax;\n"
"xorl %ebx,%ebx; \n"
"syscall;\n"
//"jmp ex;\n" here I tried to jump over the message, which results in no string being printed
// Store the Hello World inside the main function, results in segfault
"message: .ascii \"Hello World!\n\";"
"ex:" //jump over message
);
}
如您所见,我还尝试完全跳过消息,结果没有产生任何输出。
如果这确实是导致段错误的原因,我该如何阻止消息执行?
在它之前添加一个.data
语句。此外,您应该告诉 gcc 您正在烧录 eax
、ebx
和 ecx
regs,以便之后的任何代码都可以工作(例如 gcc 不会在 ecx
before asm 块并期望它仍然存在 after.
这样做:
asm (
"your stuff"
" .data\n"
" .ascii ..."
:
:
: "a", "b", "c");
更新: 不同架构的系统调用编号不同。例如,对于 i386,退出系统调用号是 1,但对于 64 位,它是 60。相应地进行调整。考虑包括 /usr/include/syscall.h 并在那里使用 __NR_* 符号(它包括 /usr/include/asm/unistd*.h,它具有实际的符号)。
此代码基于可以找到的简单教程 here。我现在使用 64 位寄存器,64 位 SYSCALL 值添加了 0x2000000,我使用 64 位等效的 LEA获取message
的地址
int main() {
__asm__ (
/* print Hello World */
"mov [=10=]x2000004, %rax;\n" /* 0x2000004 is the syscall number for 64-bit osx */
"mov , %rbx;\n" /* 1 is stdout and is the first argument */
"lea message(%rip), %rsi\n" /* load the address of string into the second argument*/
"mov , %rdx;\n" /* third argument is the length of the string to print*/
"syscall;\n"
/* call exit (0x2000001 on osx) so it doesn't try to run the string Hello World */
"mov [=10=]x2000001,%rax;\n"
"xor %rbx,%rbx; \n"
"syscall;\n"
/* Store the Hello World inside the main function, results in segfault */
"message: .ascii \"Hello World!\n\";"
);
}
如果您要在代码中使用 asm 块并且它们被其他 C 代码包围,那么您应该使用 input/output 约束和破坏列表。最好的信息在 GCC Extended ASM documentation 中。我们销毁了一些寄存器(rax、rbx、rdx、rsi,以及 rcx,r11 被 syscall
破坏),我们应该告诉 GCC。使用扩展汇编器语法时,您还必须在所有寄存器名称前加上 %%
前缀(而不仅仅是 %
)。生成的 asm 块看起来像:
int main() {
__asm__ (
/* print Hello World */
"mov [=11=]x2000004, %%rax;\n" /* 0x2000004 is the syscall number for 64-bit osx */
"mov , %%rbx;\n" /* 1 is stdout and is the first argument */
"lea message(%%rip), %%rsi\n" /* load the address of string into the second argument*/
"mov , %%rdx;\n" /* third argument is the length of the string to print*/
"syscall;\n"
/* call exit (1 on osx) (so it doesn't try to run the string Hello World */
"mov [=11=]x2000001,%%rax;\n"
"xor %%rbx,%%rbx; \n"
"syscall;\n"
"message: .ascii \"Hello World!\n\";"
: /* No output constraints */
: /* No input constraints */
: "rax", "rbx", "rdx","rsi", "rcx", "r11"); /* list of clobbered registers */
}
我正在尝试让一些简单的 C 内联汇编在 here 上工作。但是每次我尝试执行它时都会收到段错误。我正在使用 OS X,所以我不得不更改系统调用编号。使用 gdb 我想我确定了罪魁祸首:执行退出系统调用后,它尝试执行存储 "Hello world!" 字符串的部分:
void main() {
__asm__ (
// print Hello World
"movl , %eax;\n" /* 4 is the syscall number for write on osx */
"movl , %ebx;\n" /* 1 is stdout and is the first argument */
// "movl $message, %esi;\n" /* load the address of string into the second argument*/
// instead use this to load the address of the string
// as 16 bytes from the current instruction
"leal 16(%eip), %ecx;\n"
"movl , %edx;\n" /* third argument is the length of the string to print*/
"syscall;\n"
// call exit (1 on osx) (so it doesn't try to run the string Hello World
"movl ,%eax;\n"
"xorl %ebx,%ebx; \n"
"syscall;\n"
//"jmp ex;\n" here I tried to jump over the message, which results in no string being printed
// Store the Hello World inside the main function, results in segfault
"message: .ascii \"Hello World!\n\";"
"ex:" //jump over message
);
}
如您所见,我还尝试完全跳过消息,结果没有产生任何输出。
如果这确实是导致段错误的原因,我该如何阻止消息执行?
在它之前添加一个.data
语句。此外,您应该告诉 gcc 您正在烧录 eax
、ebx
和 ecx
regs,以便之后的任何代码都可以工作(例如 gcc 不会在 ecx
before asm 块并期望它仍然存在 after.
这样做:
asm (
"your stuff"
" .data\n"
" .ascii ..."
:
:
: "a", "b", "c");
更新: 不同架构的系统调用编号不同。例如,对于 i386,退出系统调用号是 1,但对于 64 位,它是 60。相应地进行调整。考虑包括 /usr/include/syscall.h 并在那里使用 __NR_* 符号(它包括 /usr/include/asm/unistd*.h,它具有实际的符号)。
此代码基于可以找到的简单教程 here。我现在使用 64 位寄存器,64 位 SYSCALL 值添加了 0x2000000,我使用 64 位等效的 LEA获取message
的地址int main() {
__asm__ (
/* print Hello World */
"mov [=10=]x2000004, %rax;\n" /* 0x2000004 is the syscall number for 64-bit osx */
"mov , %rbx;\n" /* 1 is stdout and is the first argument */
"lea message(%rip), %rsi\n" /* load the address of string into the second argument*/
"mov , %rdx;\n" /* third argument is the length of the string to print*/
"syscall;\n"
/* call exit (0x2000001 on osx) so it doesn't try to run the string Hello World */
"mov [=10=]x2000001,%rax;\n"
"xor %rbx,%rbx; \n"
"syscall;\n"
/* Store the Hello World inside the main function, results in segfault */
"message: .ascii \"Hello World!\n\";"
);
}
如果您要在代码中使用 asm 块并且它们被其他 C 代码包围,那么您应该使用 input/output 约束和破坏列表。最好的信息在 GCC Extended ASM documentation 中。我们销毁了一些寄存器(rax、rbx、rdx、rsi,以及 rcx,r11 被 syscall
破坏),我们应该告诉 GCC。使用扩展汇编器语法时,您还必须在所有寄存器名称前加上 %%
前缀(而不仅仅是 %
)。生成的 asm 块看起来像:
int main() {
__asm__ (
/* print Hello World */
"mov [=11=]x2000004, %%rax;\n" /* 0x2000004 is the syscall number for 64-bit osx */
"mov , %%rbx;\n" /* 1 is stdout and is the first argument */
"lea message(%%rip), %%rsi\n" /* load the address of string into the second argument*/
"mov , %%rdx;\n" /* third argument is the length of the string to print*/
"syscall;\n"
/* call exit (1 on osx) (so it doesn't try to run the string Hello World */
"mov [=11=]x2000001,%%rax;\n"
"xor %%rbx,%%rbx; \n"
"syscall;\n"
"message: .ascii \"Hello World!\n\";"
: /* No output constraints */
: /* No input constraints */
: "rax", "rbx", "rdx","rsi", "rcx", "r11"); /* list of clobbered registers */
}