Search code examples
assemblygdbx86-64nasm

GDB does not load source lines from NASM


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


Solution

  • 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.