From f689471c9072ddbade9260e70596f64dd2f25ff3 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 8 Oct 2026 11:54:08 +0100 Subject: [PATCH] Embed the gdb pretty printers in the `.debug_gdb_scripts` section of ELF binaries built with `-debug`, and move the script next to the runtime it reads --- {misc => base/runtime}/odin_debugger.py | 13 +++++++--- src/llvm_backend.cpp | 34 +++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 3 deletions(-) rename {misc => base/runtime}/odin_debugger.py (96%) diff --git a/misc/odin_debugger.py b/base/runtime/odin_debugger.py similarity index 96% rename from misc/odin_debugger.py rename to base/runtime/odin_debugger.py index 5fd38658f..934c34f12 100644 --- a/misc/odin_debugger.py +++ b/base/runtime/odin_debugger.py @@ -1,7 +1,10 @@ # Pretty printers for Odin types in gdb and lldb # -# gdb: source /misc/odin_debugger.py -# lldb: command script import /misc/odin_debugger.py +# gdb: source /base/runtime/odin_debugger.py +# lldb: command script import /base/runtime/odin_debugger.py +# +# gdb also loads it by itself from the `.debug_gdb_scripts` section of an ELF binary built with `-debug`, +# once that binary's directory is trusted, with `add-auto-load-safe-path ` # # Shown: `string`, `string16`, slices, dynamic arrays (and fixed capacity ones), maps as their entries, # and unions as the variant they hold, or `nil`. @@ -243,7 +246,11 @@ if gdb is not None: return _GdbArray(val, kind) return None - gdb.pretty_printers.append(_gdb_lookup) + # NOTE: loaded from a binary's `.debug_gdb_scripts` section, the printers belong to that binary + if gdb.current_objfile() is not None: + gdb.current_objfile().pretty_printers.append(_gdb_lookup) + else: + gdb.pretty_printers.append(_gdb_lookup) ################################################################################ diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index e49eac8b5..78743c943 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -3864,6 +3864,40 @@ gb_internal bool lb_generate_code(lbGenerator *gen) { } if (build_context.ODIN_DEBUG) { + // NOTE(bill): gdb runs the pretty printers of `base/runtime/odin_debugger.py` from the `.debug_gdb_scripts` section of an ELF binary, + // once the user trusts its directory; lldb reads no such section. + // The section is written in assembly, as a global cannot be made a section that is never loaded into memory, + // which is what keeps the linker from dropping it, and lets it merge the copies of several objects. + if (build_context.metrics.os != TargetOs_windows && build_context.metrics.os != TargetOs_darwin && !is_arch_wasm()) { + String path = concatenate_strings(temporary_allocator(), odin_root_dir(), str_lit("base/runtime/odin_debugger.py")); + gbFileContents fc = gb_file_read_contents(heap_allocator(), false, alloc_cstring(temporary_allocator(), path)); + if (fc.data != nullptr) { + // an entry of kind 4 is the script's name, a newline, then its text + gbString s = gb_string_make(heap_allocator(), ".pushsection \".debug_gdb_scripts\", \"MS\", %progbits, 1\n.byte 4\n.ascii \"odin_debugger.py\\n\"\n.ascii \""); + defer (gb_string_free(s)); + for (isize i = 0; i < fc.size; i++) { + u8 c = (cast(u8 *)fc.data)[i]; + switch (c) { + case '\r': break; + case '\n': s = gb_string_appendc(s, "\\n\"\n.ascii \""); break; + case '\t': s = gb_string_appendc(s, "\\t"); break; + case '\\': s = gb_string_appendc(s, "\\\\"); break; + case '"': s = gb_string_appendc(s, "\\\""); break; + default: + if (c < 0x20 || c >= 0x7f) { + s = gb_string_append_fmt(s, "\\%03o", c); + } else { + s = gb_string_append_length(s, &c, 1); + } + break; + } + } + s = gb_string_appendc(s, "\"\n.byte 0\n.popsection\n"); + gb_file_free_contents(&fc); + LLVMAppendModuleInlineAsm(default_module->mod, s, gb_string_length(s)); + } + } + // Custom `.raddbg` section for its debugger if (build_context.metrics.os == TargetOs_windows) { lbModule *m = default_module;