Home > Software design >  GDB does not load source lines from NASM
GDB does not load source lines from NASM

Time:06-22

I'm assembling an x86-64 program on Ubuntu with NASM:

nasm -f elf64 -g -F dwarf -o foo.o foo.asm
ld -o foo foo.o

Source:

section .text
    global _start
_start:
    mov rax, 60     ;SYS_exit
    mov rdi, 0      ;EXIT_SUCCESS
    syscall

Debugging the program with GDB does not show what file or line number an instruction comes from. For example, break _start shows "Breakpoint 1 at 0x401000" rather than "Breakpoint 1 at 0x400080: file foo.asm, line 4." as shown in this blog post. Switching to layout regs shows "No Source Available" rather than where in the source the current instruction is found. list does show the source, but it switches back to "No Source Available" upon stepping to the next instruction.

objdump -g foo seems to show that the required debug information is there:

foo:     file format elf64-x86-64
...
The File Name Table (offset 0x1c):
  Entry Dir     Time    Size    Name
  1     0       0       0       foo.asm

 Line Number Statements:
  [0x00000028]  Extended opcode 2: set Address to 0x401000
  [0x00000033]  Special opcode 8: advance Address by 0 to 0x401000 and Line by 3 to 4
  [0x00000034]  Special opcode 76: advance Address by 5 to 0x401005 and Line by 1 to 5
  [0x00000035]  Special opcode 76: advance Address by 5 to 0x40100a and Line by 1 to 6
  [0x00000036]  Advance PC by 2 to 0x40100c
  [0x00000038]  Extended opcode 1: End of Sequence

Ubuntu 22.04, NASM 2.15.05, GDB 12.09

CodePudding user response:

I setup an Ubuntu 22.04 VM and found that I could reproduce the issue that you are seeing there, however, on my local machine, I could not reproduce the problem.

I noticed that on my local machine I was using nasm 2.14.02, while on the Ubuntu machine I was using 2.15.05.

If we check the objdump -g output on the two executables, here's part of what I see from the working executable:

Contents of the .debug_info section (loaded from foo):

  Compilation Unit @ offset 0x0:
   Length:        0x45 (32-bit)
   Version:       3
   Abbrev Offset: 0x0
   Pointer Size:  8
 <0><b>: Abbrev Number: 1 (DW_TAG_compile_unit)
    <c>   DW_AT_low_pc      : 0x401000
    <14>   DW_AT_high_pc     : 0x40100c
    <1c>   DW_AT_stmt_list   : 0x0
    <20>   DW_AT_name        : foo.asm
    <28>   DW_AT_producer    : NASM 2.14.02
    <35>   DW_AT_language    : 32769    (MIPS assembler)
 <1><37>: Abbrev Number: 2 (DW_TAG_subprogram)
    <38>   DW_AT_low_pc      : 0x401000
    <40>   DW_AT_frame_base  : 0x0 (location list)
 <1><44>: Abbrev Number: 0

And here's the same part from the broken executable:

Contents of the .debug_info section (loaded from foo):

  Compilation Unit @ offset 0x0:
   Length:        0x45 (32-bit)
   Version:       3
   Abbrev Offset: 0x0
   Pointer Size:  8
 <0><b>: Abbrev Number: 1 (DW_TAG_compile_unit)
    <c>   DW_AT_low_pc      : 0x401000
    <14>   DW_AT_high_pc     : 0x401000
    <1c>   DW_AT_stmt_list   : 0x0
    <20>   DW_AT_name        : foo.asm
    <28>   DW_AT_producer    : NASM 2.15.05
    <35>   DW_AT_language    : 32769    (MIPS assembler)
 <1><37>: Abbrev Number: 2 (DW_TAG_subprogram)
    <38>   DW_AT_low_pc      : 0x401000
    <40>   DW_AT_frame_base  : 0x0 (location list)
 <1><44>: Abbrev Number: 0

The critical difference is the DW_AT_high_pc, this appears to be wrong with the 2.15.05 nasm. I manually went in and edited this value, and suddenly, I can debug the previously broken executable just fine.

This appears to be a regression in 2.15.05 of nasm, you should consider downgrading nasm (I think 2.15.05 is the current latest release), or maybe file a nasm bug.

CodePudding user response:

IIRC, GDB works fine NASM's default STABS output, so let it use that instead of DWARF. Jester says GNU tools use a newer DWARF format that NASM doesn't know how to make. But they still support the STABS format that DWARF replaced. STABS is more than fine for the simple task for mapping source lines to addresses, which is all that's needed for a simple source language like NASM.

Just use nasm -g without a -F option.

Or leave out -g as well, you still get symbol names, and I've never had much use for debug info in hand-written asm beyond basic label names. (And of course section headers, which are also not needed in an executable, just the program headers. As you'll find out if you try to use GDB on an executable created directly by FASM.)


I always use layout n after layout reg, to switch to registers disassembly. That used to be what layout reg was; I'm not sure if the current behaviour is a GDB bug or what. (In some previous version, both layout next / prev were required, like it thought the config setting was out of sync with what the UI was actually displaying).

Anyway, I basically never want GDB displaying the .asm source, I want it to show canonical disassembly of whatever I'm running, in case I wrote something that assembled differently from how it looks.

Using meaningful label names in your asm source will lead to GDB showing execution in sieve.crossout or whatever. Although it's less nice with lots of conditional branching, since every branch target looks like a full label for GDB.

  • Related