Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/assembly/6.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Assembly 为什么这会给我一个错误_Assembly_X86 64_Gnu Assembler_X86 - Fatal编程技术网

Assembly 为什么这会给我一个错误

Assembly 为什么这会给我一个错误,assembly,x86-64,gnu-assembler,x86,Assembly,X86 64,Gnu Assembler,X86,我正试图写一个hello world程序,但它不起作用-为什么 .data str: .ascii "Hello World\12\0" .text .globl main .extern printf main: pushq %rbp movq %rsp,%rbp movq str,%rdi callq printf movq %rbp,%rsp po

我正试图写一个hello world程序,但它不起作用-为什么

.data
    str:
        .ascii "Hello World\12\0"
.text
    .globl main
    .extern printf
    main:
        pushq %rbp
        movq %rsp,%rbp
        movq str,%rdi
        callq printf
        movq %rbp,%rsp
        popq %rbp
        ret

您违反了呼叫约定。由于这是在Linux上运行的64位代码,因此适用的调用约定是。(更多详细信息请参见标记wiki。)

请注意,对于变量函数,如
printf
,此调用约定要求调用方设置
RAX
寄存器,以指示在向量寄存器中传递的浮点参数的数量。您没有初始化
RAX
寄存器,因此它实际上包含垃圾数据。然后,
printf
函数尝试从向量寄存器读取未定义数量的浮点值,从而导致分段错误

您只需在拨打电话之前将RAX归零即可修复代码。有几种方法可以做到这一点。显而易见的是
movq$0,%rax
,然而,寄存器归零的一种更为理想的方法是使用
XOR
指令,例如:
xorq%rax,%rax
。但你可以做得更好。由于在x86-64上,所有指令都隐式清除目标寄存器的上32位,因此只需执行
xorl%eax,%eax
。这将缩短一个字节,执行速度将略快

从技术上讲,您的
main
函数也应该返回0。调用约定指定函数使用
RAX
寄存器返回一个整数结果,因此在返回之前只需再次将
RAX
归零。现在,
printf
正在
RAX
中返回其结果,并且由于您没有设置
RAX
,因此实际上是从
main
返回
printf
的结果。这是合法的,但可能不是你想要的

因此,您的
main
函数应该如下所示:

  pushq  %rbp
  movq   %rsp, %rbp
  movq   str,  %rdi
  xorl   %eax, %eax
  callq  printf
  xorl   %eax, %eax
  movq   %rbp, %rsp
  popq   %rbp
  ret
但是您可以通过删除
movq%rsp,%rbp
指令来缩短代码。你没有做相对寻址,所以这里不需要。而且,您可以将
movq str、%rdi
优化为
movl str、%edi
。最后的代码就是:

  pushq  %rbp         # reserve 8 bytes of space on the stack
  movl   str,  %edi   # get address of string constant
  xorl   %eax, %eax   # clear RAX register in preparation for calling printf
  callq  printf
  xorl   %eax, %eax   # clear RAX register so we return 0
  popq   %rbp         # clean up the stack
  ret

您应该使用
movabsq$str,%rdi
加载地址。您所做的是加载字符串的前8个字节,然后poor
printf
尝试将其用作地址并出现故障。另外,在调用@Jester
mov$str之前,您需要将
%al
归零,因为ABI将数据和代码放在地址空间的前2G字节内,所以%rdi
就足够了。@fuz:
mov$str,%edi
当然更好,由于GNU
as
不会将7字节符号扩展mov立即数优化为5字节零扩展mov立即数。要格式化代码,请不要使用
,选择代码并按编辑器中的
{}
按钮。请确保最初遵循目标平台+汇编程序语法的课程信息/教程(即在这种情况下,气体+64b clib)。对于人类来说,gas语法比Intel语法更难读/写,因此您必须特别注意所有这些奇怪的符号,例如,
$
是否在数字之前,使其成为完全不同的指令。一旦您稍微熟悉了基础知识并完全了解了指令在CPU,您可以很容易地选择不同的语法/平台示例以获取灵感,但一开始它们会让您感到困惑。
movq str,%rdi
从字符串中加载8个字节,这是导致printf segfaulting的主要原因(除非OP有一个旧的libc,它使用AL进行计算跳转,而不是仅使用0/non-0检查来保存所有XMM regs)。您希望
mov$str,%edi
lea str(%rip),%rdi
传递指向标签的指针。