简单的内联汇编导致执行时的段错误#34; Hello world!"

时间:2015-10-28 20:04:39

标签: c assembly inline-assembly

我正在尝试从here开始使用一些简单的内联汇编。但每次我尝试执行它时,我都会收到一个段错误。我使用OS X所以我不得不更改系统调用号码。使用gdb我认为我确定了罪魁祸首:在执行退出系统调用之后,它尝试执行" Hello world!"字符串存储:

void main() {
__asm__ (
    // print Hello World
    "movl $4, %eax;\n"  /* 4 is the syscall number for write on osx */
    "movl $1, %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 $13, %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 $1,%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
);
}

正如你所看到的,我也试图完全跳过这条消息,而这又没有输出。

那么,如果这确实是导致段错误的原因,我怎么能阻止该消息的执行?

2 个答案:

答案 0 :(得分:2)

此代码基于可以找到的简单教程here。我现在使用64位寄存器,64位 SYSCALL 值添加了0x2000000,我使用64位等效的 LEA 来获取地址消息

int main() {
__asm__ (
    /* print Hello World */
    "mov $0x2000004, %rax;\n"  /* 0x2000004 is the syscall number for 64-bit osx */
    "mov $1, %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 $13, %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 $0x2000001,%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 代码包围,那么您应该使用输入/输出约束和clobber列表。最好的信息在 GCC Extended ASM documentation中。我们销毁了许多寄存器( rax rbx rdx rsi ),我们应该告诉GCC 。使用扩展汇编语法时,您还必须使用%%(而不仅仅是%)为所有寄存器名称添加前缀。生成的 asm 块看起来像:

int main() {
__asm__ (
    /* print Hello World */
    "mov $0x2000004, %%rax;\n"  /* 0x2000004 is the syscall number for 64-bit osx */
    "mov $1, %%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 $13, %%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 $0x2000001,%%rax;\n"
    "xor %%rbx,%%rbx; \n"
    "syscall;\n"
    "message: .ascii \"Hello World!\\n\";"
    : /* No output constraints */
    : /* No input constraints */
    : "rax", "rbx", "rdx","rsi"); /* list of clobbered registers */
}

答案 1 :(得分:0)

在其前面添加.data语句。另外,您应该告诉gcc您正在刻录eaxebxecx个注册表,以便之后的任何代码都能正常工作(例如gcc赢了在 asm块之前ecx 中的某些内容,并希望它在之后仍然存在

这样做:

asm (
"your stuff"
"    .data\n"
"    .ascii ..."
:
:
: "a", "b", "c");

更新:不同拱门的系统调用号码不同。例如,对于i386,退出系统调用编号为1,但对于64位,则为60.相应地进行调整。考虑包括/usr/include/syscall.h并在那里使用__NR_ *符号(它包括/usr/include/asm/unistd*.h,它们具有实际符号)。