如何调试 NASM 在点击 _start 之前产生段错误的程序集

How to debug NASM Assembly that produces segfault before hitting _start

我正在编写以下汇编 (NASM) 代码,将鼠标指针设置为位置 100,100:

section .text
    global _start:

_start:
    mov eax, 4
    mov ecx, 100
    mov edx, 100
    int 33h

    mov eax, 1
    int 0x80

我用下面的 make 文件编译它:

DEPS = Mouse.asm

Mouse: $(DEPS)
    nasm -f elf $(DEPS)
    ld -m elf_i386 -s -o Mouse Mouse.o

然后我使用 $sudo gdb 鼠标在 GDB 中打开它。然后我输入"break _start"然后按"r"到运行。当我这样做时,我得到以下输出:
"启动程序:/home/tyler/ASM/Mouse/Mouse

程序接收到信号 SIGSEGV,分段错误。 0x0804806f 在 ?? () “

在我看来,它甚至从未在 _start 处遇到断点,所以我不知道我可能做错了什么。我该如何调试呢?

更新:现代 GDB 有一个 starti 命令使这个 hack 过时了。 Stopping at the first machine code instruction in GDB

TL:DR: b _start 找不到设置断点的符号的原因是您在链接时删除了符号。 (所以这就是为什么它在加载动态库时处于“等待”状态,但在这种情况下没有任何动态库。)


为了回答您提出的问题(而不是您实际遇到的问题),请使用@Ped7g 的建议“滥用”GDB 的错误处理,使其在不知道正确地址的情况下在第一条指令之前停止:

  • b *0 在地址零处设置断点(这是不可能的)。
  • r 运行s 程序,此时 GDB 尝试实际设置断点。它失败了,在执行任何指令之前停止
  • d 1删除无效断点
  • si(或stepi)将从那里单步执行。

这是调试没有符号的程序的有用技巧,即使它是使用 ASLR (gcc -pie) 动态链接的,所以您无法使用 [=20 获取真正的 ELF 入口点地址=].


你做了什么:

Then I input "break _start" and press "r" to run.

当我使用与您使用的相同命令构建后执行此操作时,我得到

Reading symbols from ./Mouse...(no debugging symbols found)...done.
(gdb) b _start
No symbol table is loaded.  Use the "file" command.
Make breakpoint pending on future shared library load? (y or [n]) y
Breakpoint 1 (_start) pending.

我必须输入 yn 才能输入 r 到 运行 程序。

所以我实际上并没有在 asm 源代码中标记为 _start: 的指令处设置断点。我只有一个 pending 断点,如果我给 GDB 一个符号 table,它将被设置在某处。 (可以将调试符号剥离到一个单独的文件中,因此这种 gdb 行为是有道理的。但不幸的是,在这种情况下它让你感到困惑。:/)

您的二进制文件已被删除,因为您是使用 ld -s 构建的。这与您想要调试的相反。使用 nasm -g -Fdwarf 以使用现代 dwarf 调试信息格式而不是 STAS 以获得更好的符号信息。


(gdb) r
Starting program: /home/peter/src/SO/Mouse

Program received signal SIGSEGV, Segmentation fault.
0x0804806f in ?? ()

0x0804806f 处的指令是您的 int 33h

它在 _start 之前没有出现段错误,它只是没有停止,因为您从未 实际上 设置断点。在静态链接的二进制文件中,user-space 中 运行 的第一条指令是您的 ELF 入口点处的指令。 (在动态链接的二进制文件中,ELF 解释器 运行 首先跳转到您的 _start,或者您称之为入口点的任何内容。)

使用layout reg显示寄存器和反汇编。使用 set disassembly-flavor intel 获得 GNU 的 MASM-like 语法,当您知道它应该是什么时,它足够接近 NASM 来阅读。将这些放入您的 ~/.gdbinit。有关 gdbstrace.

的更多调试提示,另请参阅 标签 wiki 的底部

正如评论者所指出的,您的程序无法在 Linux 下原生地 运行。 Linux 本身不支持 BIOS int 33h ABI,仅支持其自身的系统调用 ABI。 What are the calling conventions for UNIX & Linux system calls (and user-space functions) on i386 and x86-64。这就是 int 33h 段错误的原因。

如果你想编写 MS-DOS 或 PC-BIOS 代码,请使用像 BOCHS 这样的模拟器(它有一个内置的调试器,可以让你单步执行任何东西,甚至是引导加载程序)。