DWARF

Introduction

LIEF Extended can read DWARF functions, variables, types, and source locations, generate C/C++ declarations, and create new debug files.

DWARF debug information can be embedded in a binary or stored in a separate file. To inspect compiler-generated DWARF, build with debug information and preserve it when stripping the binary. Debug files can also be generated from analysis results with the DWARF editor. For an overview of loading and associating debug files, see Debug Information.

Load and inspect DWARF

import lief

elf = lief.ELF.parse("/bin/with_debug")
if debug_info := elf.debug_info:
    assert isinstance(debug_info, lief.dwarf.DebugInfo)
    print(f"DWARF Debug handler: {debug_info}")
Additionally, the function can be used to load a DWARF file, whether it is embedded or standalone:
import lief

dbg: lief.dwarf.DebugInfo | None = lief.dwarf.load("/bin/with_debug")
dbg: lief.dwarf.DebugInfo | None = lief.dwarf.load("external_dwarf")
dbg: lief.dwarf.DebugInfo | None = lief.dwarf.load("debug.dwo")

For a macOS .dSYM bundle, pass the path to the DWARF object inside Contents/Resources/DWARF/. Check the loader’s return value before accessing compilation units or searching for a function or type.

Once loaded, you can use the API to interact with the debug information:
dbg: lief.dwarf.DebugInfo

for compilation_unit in dbg.compilation_units:
    print(compilation_unit.producer)
    for func in compilation_unit.functions:
        print(func.name, func.linkage_name, func.address)

    for var in compilation_unit.variables:
        print(var.name, var.address)

    for ty in compilation_unit.types:
        print(ty.name, ty.size)

dbg.find_function("_ZNSi4peekEv")
dbg.find_function("std::basic_istream<char, std::char_traits<char> >::peek()")
dbg.find_function(0x137A70)

dbg.find_variable("_ZNSt12out_of_rangeC1EPKc")
dbg.find_variable("std::out_of_range::out_of_range(char const*)")
dbg.find_variable(0x2773A0)

dbg.find_type("my_type_t")

Attach an external debug file

Here’s an example:

binary: lief.Binary

dbg = binary.load_debug_info("/home/romain/dev/LIEF/some.dwo")
Use a debug file produced by the same build as the binary. Attaching it updates LIEF’s analysis object. It does not insert DWARF sections into the executable. The function can then resolve functions defined by that debug file while reading their machine code from the binary:
binary: lief.Binary

binary.load_debug_info("/home/romain/dev/LIEF/some.dwo")

# The location (address/size) of `my_function` is defined in some.dwo
for inst in binary.disassemble("my_function"):
    print(inst)

Additionally, you may also want to explore the BinaryNinja and Ghidra DWARF export plugins, which generate debug information based on the analysis performed by these frameworks.

Generating C/C++ Definitions

DWARF functions, variables, types, and compilation units can be rendered as C/C++ declarations using:

The generated output can be configured with a structure (e.g. to prefer C++ syntax or change the indentation):
dbg = lief.dwarf.load("/bin/with_debug")

func = dbg.find_function("main")
print(func.to_decl())

opt = lief.DeclOpt()
opt.is_cpp = True
opt.indentation = 4

for cu in dbg.compilation_units:
    # Emit the definition of the functions of the compilation unit
    print(cu.to_decl(opt))

DWARF Editor

Editing Existing DWARF

LIEF does not currently support modifying an existing DWARF file.

LIEF provides a comprehensive high-level API for programmatically creating DWARF files. This works by using the interface, which can be instantiated using :
pe = lief.PE.parse("demo.exe")
assert isinstance(pe, lief.PE.Binary)

editor = lief.dwarf.Editor.from_binary(pe)
editor: lief.dwarf.Editor

unit = editor.create_compilation_unit()
unit.set_producer("LIEF")

func = unit.create_function("hello")
func.set_address(0x123)

struct_ptr = unit.create_structure("my_struct_t").pointer_to()
assert isinstance(struct_ptr, lief.dwarf.editor.PointerType)

func.set_return_type(struct_ptr)

var = func.create_stack_variable("local_var")
var.set_stack_offset(8)

editor.write("/tmp/out.debug")

BinaryNinja & Ghidra

This feature is provided as a plugin for BinaryNinja and Ghidra.


API

You can find the documentation of the API for the different languages here:

Python API

C++ API

Rust API: lief::dwarf