Why is the stack trace being outputted differently when `dwarf_format` is not null?

I’m new to zig and low-level programming, I’ve been debugging in LLDB, and for LLDB to let me watch the variables in my code I found that I need to set the dwarf_format to either @"64" or @"32", however when I do that, the stack trace that zig prints on @panic becomes a lot less understandable.

???:?:?: 0x7ff79d7816a2 in ??? (my-exe.exe)
???:?:?: 0x7ff79d7811ae in ??? (my-exe.exe)
???:?:?: 0x7ff79d781120 in ??? (my-exe.exe)
C:\Program Files\Zig\zig-x86_64-windows-0.16.0\lib\libc\mingw\crt\crtexe.c:259: 0x7ff79d8b6d6b in __tmainCRTStartup (crt2.obj)
    mainret = _tmain (argc, argv, envp);

C:\Program Files\Zig\zig-x86_64-windows-0.16.0\lib\libc\mingw\crt\crtexe.c:179: 0x7ff79d8b6dcb in mainCRTStartup (crt2.obj)
  ret = __tmainCRTStartup ();

???:?:?: 0x7ff9af55e956 in ??? (KERNEL32.DLL)
???:?:?: 0x7ff9b0b07c1b in ??? (ntdll.dll)

As a workaround, I’ve set a build option that sets the dwarf_format to be not null just when I’m doing debugging with LLDB, so this isn’t a big deal. But I would greatly appreciate if someone could tell me how I can make both the printed stack trace and LLDB work at the same time or just explain why this happens. I’m on windows, targeting x86_64 windows gnu, in case that’s relevant, and I am using CodeLLDB which is a vs code plugin.

are you forcing the use of LLVM when building? The self hosted backend generates DWARF version 5 output, which LLDB does not support.

You can set it in build.zig by setting use_llvm=true in you module.

Also, you most likely should be using DWARF format 32. Confusingly, the dwarf format has nothing to do with the target architecture, but with how big the binary size is. Unless your binary is very large, dwarf format 32 will be all you need.

2 Likes

I tried both use_llvm=true and just leaving it null. I am assuming that since I’m targetting windows zig would always use llvm anyways, so there isnt a point to not leaving it null. Is that not the case?
I was also assuming the 64 and 32 were related to target architecture, so thanks for letting me know that is not the case.
You said LLDB does not support the generated DWARF but LLDB seems to work for me fine when I have dwarf_format not null, but when I do that, the stack trace gets printed out differently.
Here is a build script that I’m testing this issue with:

pub fn build(b: *std.Build) void 
{
    const root_exe = b.createModule(.{
            .root_source_file = b.path("src/main.zig"),
            .target = b.standardTargetOptions(.{}),
            .dwarf_format = .@"32",
            .optimize = .Debug,
    });

    const exe = b.addExecutable(.{
        .name = "my-exe",
        .root_module = root_exe,
    });

    b.installArtifact(exe);

    const run_exe = b.addRunArtifact(exe);
    const run_step = b.step("run", "Run the exe");
    run_step.dependOn(&run_exe.step);
}
pub fn main() void 
{
    @panic("panics");
}
PS C:\Users\User\Documents\Zig\first-project> zig build run
thread 21484 panic: panics
???:?:?: 0x7ff72084d4b2 in ??? (my-exe.exe)
???:?:?: 0x7ff72084d457 in ??? (my-exe.exe)
???:?:?: 0x7ffbb6487373 in ??? (KERNEL32.DLL)
???:?:?: 0x7ffbb7e1cc90 in ??? (ntdll.dll)

I forgot that this was for windows, So i’m guessing you are right that it is using the llvm backend (which generates dwarf version 4 debug info). I’m not sure why setting the dwarf format explicitly is causing the issues with traceback, I’m not experienced with mingw (which I’m assuming you are using, as it’s in the quoted paths). What happens when you objdump the resulting binary (assuming objdump works on mingw, again, i’m not experienced with it). objecump --dwarf=info my-exe.exe

Actually, that .exe is interesting, is the output file an ELF file? (Linux Executable format)? Dwarf is a Linux format, and I’m wondering if the executable is using windows PDB format for debug info.

My exe’s format is PE (Portable Executable), which is the windows format for executables. And yes the executable is outputted along a PDB file.

As I understand it, DWARF is being put directly in the exe rather than outside it in another file like PDB. And the reason I’m using DWARF even though it already outputs PDB is that I couldn’t get LLDB to work properly with PDB, it would let me step through code but I could not watch any variables, but with DWARF it worked fine.

I did some testing and my guess is that the stack trace back is printed using the PDB file by default, but if I ouput DWARF the PDB is no longer valid. If build without DWARF and run it, the stack trace works fine, if I then delete the PDB, the stack trace breaks. If I build with DWARF and run it, the stack trace does not work, but if I delete the PDB (that is still generated) then the stack trace works.

If my guess is correct I have two options. Either make LLDB work with PDB, or tell zig to not ouput or delete the PDB when I output DWARF.