执行 "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 您正在烧录 eaxebxecx 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 中。我们销毁了一些寄存器(raxrbxrdxrsi,以及 rcxr11syscall 破坏),我们应该告诉 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 */
}