skip to Main Content

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

4

Answers


  1. There was a longstanding NASM bug in debug-info generation, fixed in 2.16. From the release notes in the NASM docs:

    • Fix a code range generation bug in the DWARF debug format (incorrect information in the DW_AT_high_pc field) for the ELF output formats. This bug happened to cancel out with a bug in older versions of the GNU binutils linker, but breaks with other linkers and updated or other linkers that expect the spec to be followed.

    Old answer: Debug the machine code, forget the source. Or use STABS

    IIRC, GDB works fine with 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.

    Login or Signup to reply.
  2. 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.

    Login or Signup to reply.
  3. As pointed by Andrew this is indeed a bug in nasm (https://bugzilla.nasm.us/show_bug.cgi?id=3392798). It will not be fixed by downgrading as the bug is pretty old from what I understand.

    The bug is aparent right now because older version of GNU ld had an issue that complemented this one (making it imperceptible). AFAIK users of lld and gold were experiencing this issue long ago.

    The nice part is that I’ve sent a really simple and small patch (and that I feel comfortable thinking is correct after reading through the ld-nasm interaction and why it wasn’t discovered before).

    If you are inclined to read the patch (maybe even leave helpful comments if you find an issue) it’s here: https://github.com/netwide-assembler/nasm/pull/35

    Also, if you use Ubuntu 22.04 I’ve built a patched .deb that should be relatively easy to install https://github.com/iglosiggio/nasm/releases/tag/nasm-2.15.05-2.

    Login or Signup to reply.
  4. I have the same situation ‘Single stepping until exit from function main, which has no line number information.’ I checked the gcc and gdb versions but needed to remember the nasm.
    I have this problem in version 2.15.05. So I upgrade the nasm to version 2.16.01, and it’s okay.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search