【问题标题】:Seg fault...on hello world段错误...在你好世界
【发布时间】:2016-06-03 10:18:35
【问题描述】:

这段代码非常简单,我的 x86_64 linux 系统出现了段错误。这让我很困扰。刚开始使用 asm,请耐心等待!

使用 NASM 组装 nasm -f elf64 test.asm

ld -o test test.o

SECTION .text
    GLOBAL _start

    _start:
        ; print name
        mov eax,4     ; sys_write
        mov ebx,1     ; stdout
        mov ecx,name  ; start address of name
        mov edx,1     ; length
        int 80H       ; syscall

        ; exit program
        mov eax,1     ; sys_exit
        mov ebx,0     ; success
        int 80H       ; sys_call  

SECTION .data
    name DB 'R'

我的机器:Gentoo x86_64 nomultilib!我在没有 IA32 仿真的情况下编译了自己的内核。我应该说我的系统是 64 位系统。这会归因于我收到的错误吗?

$ uname -a
Linux rcepeda 4.4.1-2-ARCH #1 SMP PREEMPT Wed Feb 3 13:12:33 UTC 2016 x86_64 GNU/Linux

解决方案

使用 64 位寄存器和 64 位 linux 调度器

使用系统调用(不是 int 80H)。

谢谢内特和迈克尔

32 Bit Linux SYSCALL TABLE

64 Bit Linux SYSCALL TABLE

SECTION .text
    GLOBAL _start

    _start:
        ; print name
        mov rax,1     ; sys_write
        mov rdi,1     ; stdout
        mov rsi,name  ; start address of name
        mov rdx,7 ; length
        syscall

        ; exit program
        mov rax,60    ; sys_exit
        mov rdi,0     ; success
        syscall

SECTION .data
    name DB "Rafael",10

.

rafael@rcepeda ~/asm $ ./a.out 
Rafael

【问题讨论】:

  • 为我工作。你的uname -a是什么?
  • @MichaelPetch:它打印R 而不是Hello world,仅此而已。但除此之外,它是真正的代码。
  • @NateEldredge 我知道这是一个完整的例子,但用户可能没有给我们他的确切代码。由于他的标题表明不同的输出可能表明代码段错误可能不是显示的代码。因此,我要求澄清。除非此代码在较低的 4gb 地址空间之外运行,否则即使它是使用 32 位 int 0x80 机制的 64 位应用程序,它也应该可以工作。
  • 我也和@YOU 在一起。我很好奇uname -a 会返回什么。在您的问题中表明这一点会很有帮助
  • 如果您使用 IA32 仿真支持构建内核,您的程序可能会运行。大多数 Linux 发行版都使用其 64 位内核启用 IA32 仿真,因此可以向后兼容旧的 32 位用户代码。

标签: linux assembly x86-64 system-calls


【解决方案1】:

您在 64 位模式下运行,但这是 32 位代码。如果你想要 64 位代码,你必须重写它。

您应该使用 64 位寄存器 rax, rbx 等。在 64 位 Linux 中,系统调用不再使用 int 80h,而是使用新的 syscall 指令。有关示例,请参阅 http://cs.lmu.edu/~ray/notes/linuxsyscalls/(注意这使用 AT&T 汇编语法而不是 Intel)。

或者,您可以使用nasm -f elf32ld -m elf_i386 在32 位模式下将代码保持不变,并将其组装和链接。但是你正在学习(相对)过时的技术。 (编辑:实际上,您的特定系统似乎没有启用 32 位兼容性,所以这对您根本不起作用。)

【讨论】:

  • 这个 32 位代码应该在 64 位 Linux 环境中工作,因为显示的示例很可能不在地址空间的底部 4gb 之外运行。 int 0x80 在 64 位代码中仍然可用,但不是首选机制。强制使用syscall 的一种情况是访问堆栈上的内存。由于 64 位中的堆栈值高于 4gb,因此它们会被截断,并且对于 32 位 int 0x80 调用无用。
  • 我想知道同样的事情,但理论上 32 位寄存器与 64 位寄存器相同,只是 @MichaelPetch 状态的一半大小。虽然我的系统是严格的 64 位,但我的系统上没有 multilib 支持,并且我的内核上没有 IA32 仿真。似乎与我的机器有关,但我不确定它是什么....
  • 感谢 Nate 的链接,这似乎就是我在 64 位机器上所做的事情。我回家后会试试这个。我的系统是严格的 64 位的,但是我不知道我不能使用我的 32 位寄存器呢????
  • @Rafael:一般情况下,您可以在 64 位模式下使用 32 位寄存器。但是例如,在 64 位模式下,write(2) 的第二个参数是一个指针,它应该是 64 位。当您将name 的地址移动到ecx 时,您最终会得到rcx 的低半部分包含地址的低32 位,而高半部分包含0。内核将寻找一个rcx 中的 64 位指针。如果地址不在地址空间的低 2 GB 中,则结果将是错误的。因此,您可以使用 32 位寄存器,但这可能不是正确的做法。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-11-19
  • 1970-01-01
相关资源
最近更新 更多