# gimli reads and writes DWARF without copying the buffer

> gimli is the Rust library for the DWARF debugging format, and its two design bets are zero copy, where every value is a view into the original buffer, and lazy parsing, where you only pay for the DIE trees you iterate over. It also writes DWARF, which is the half most debuggers skip, and it is vendored into the Rust standard library itself.

**gimli-rs/gimli** — A library for reading and writing the DWARF debugging format

- Repository: https://github.com/gimli-rs/gimli
- Website: https://docs.rs/gimli/
- Stars: 1,019 · Forks: 142
- Language: Rust
- License: Apache-2.0
- Published: 2026-10-19 · Updated: 2026-10-19 · Language: en
- Canonical page: https://hysenlabs.com/projects/gimli-rs-gimli

## DWARF is the format under every native debugger

gimli is a library for reading and writing the DWARF debugging format. DWARF is the debugging information format that native toolchains on Unix-like systems and Windows produce, and it is what a debugger reads to turn an address into a file, a line, a variable and a type.

The problem it solves is not the format itself but the shape of the data. A DWARF file is large, structured as a tree of debugging information entries, and full of offsets, forms and abbreviations that have to be resolved against other sections of the object file. Reading it naively means allocating a tree of structures for every entry, most of which nobody will ever ask about, and doing that in the wrong order can mean reading a section several times.

So the audience is a specific kind of developer. People writing debuggers, profilers, stack unwinders, crash reporters, binary analysis tools, symbolication services and compiler tooling. These are the projects where DWARF handling is the product rather than a detail, and where the difference between a fast parser and a slow one shows up in the user experience of the tool.

The library is published on crates.io with documentation on docs.rs, it is maintained in the gimli-rs organisation, and it is dual licensed under Apache 2.0 and MIT at your option, with contributions dual licensed on the same terms unless stated otherwise. The current crate version is 0.34.0, the edition is 2024, and the minimum supported Rust version is 1.88.

Adding it is a single command:

```bash
cargo add gimli
```

## Zero copy means every value is a view

The first of the two design bets is stated simply: everything is just a reference to the original input buffer, and no copies of the input data get made.

That is a bigger constraint than it sounds, because DWARF is full of values that could be stored in a section and referenced by offset, and a convenient API would return owned types. gimli instead hands back values that borrow from the buffer you gave it. A string in a DWARF entry is a range into the input, a type reference is an offset, and a whole section is a slice.

The payoff is allocation behaviour. A tool that walks a large binary's debug information does not allocate once per field, and the working set stays proportional to the buffer you already hold rather than to the size of the parsed structure. For a tool analysing a large binary on a constrained machine, that is the difference between working and not.

The cost is a borrow-driven API. Because the values borrow from the input, the lifetimes are visible in the types, and code that wants to keep a parsed value around has to keep the buffer alive. That is a normal Rust trade, and it is the kind of one that is invisible in a small example and obvious in a large tool. The API is shaped by it: the crate also carries stable_deref_trait as an optional dependency, which is the trait used to let borrowed views be passed where an owned type is expected, and it exists precisely to smooth this pattern.

## Lazy parsing and the sibling shortcut

The second bet is laziness, and it is the one that decides how a debugger feels while it is loading.

You can iterate compilation units without parsing their contents, and gimli parses only as many debugging information entry trees as you iterate over. A DWARF file is organised as a series of compilation units, each with its own tree of entries, and a tool that wants one function's debug information should not have to materialise the whole unit that contains it.

There is a second, subtler optimisation in the same paragraph. gimli uses DW_AT_sibling references to avoid parsing a DIE's children to find its next sibling, when possible. In a tree, the naive way to walk the entries of a unit is to descend into a node, collect its children, and then move to the next child. The DWARF encoding often stores a sibling offset in the entry itself, so a reader can jump straight to the next sibling without descending, and the cost of a lookup becomes proportional to the branch you actually need rather than to the size of the subtree.

Together these two properties define the performance model. Parsing is proportional to the part of the file you look at, and memory is borrowed rather than owned. A stack unwinder that needs one function's line table, or a profiler that samples a few addresses, pays for what it uses.

The trade-off is that the API is iterator-shaped, and iteration has its own cost when it is done badly. A consumer that repeatedly scans for the same entry pays for the scan each time, which is why the crate offers the fallible-iterator feature as an option, letting the lazy parsers be consumed through that trait rather than only through the standard Iterator.

## Cross-platform means you bring the object file loader

The third stated property has a catch attached to it in the same sentence, and the catch is the part that trips people up.

gimli makes no assumptions about what kind of object file you are working with. The flip side is that it is up to you to provide an ELF loader on Linux or a Mach-O loader on macOS. In other words, gimli parses the DWARF sections, and somebody else has to find those sections in the binary in the first place.

That division is deliberate, and the project points at the answer: if you are unsure which object file parser to use, try the cross-platform object crate, which comes from the same organisation. That crate handles the container format and hands gimli the sections it needs.

Doing it yourself is not hard if you need control, and the examples show the shape of it. The repository carries a gimli-examples crate with several programs: a simple .debug_info parser, a simple .debug_line parser, a simple DWARF writer, a simple DWARF converter that reads DWARF sections and writes them back out again, a fuller DWARF converter that modifies the DWARF by removing entries for dead code, and a dwarfdump clone. There is also a separate addr2line clone project in the same organisation.

Those examples are the fastest way to understand the crate, and the dwarfdump clone in particular is the one to read first, because it is the program that has to look at everything and therefore shows how each section is walked.

## Writing DWARF is the half most tools skip

Most DWARF libraries are readers. gimli is described as a library for reading and writing the format, and the writing half is not an afterthought, it is a separate feature in the manifest with its own dependencies.

The write side is enabled by the write feature, which pulls in fnv, hashbrown and indexmap, and those are there for a reason: writing DWARF means emitting a string table and a hash map of the entries you have written, so the writer needs somewhere to intern strings and somewhere to index its own output.

The example programs are what make this concrete. There is a simple DWARF writer for the case where you are producing debug information from scratch, and a simple DWARF converter that reads DWARF sections and writes them back out, which is the round trip test. Then there is a converter that modifies the DWARF by removing entries for dead code, which is the interesting one: rewriting debug information to match a program that has been optimised, so the debugger does not show variables for code that no longer exists.

That capability is what separates gimli from a read-only parser, and it is the reason the write feature exists as a first-class part of the crate rather than as an unfinished corner. For a tool in the business of stripping binaries, or of generating debug information for a linker or a compiler, being able to write the format is not a nice-to-have.

The default feature set turns the writer on, along with the full read stack, so an installation of the crate with no configuration has both halves available.

## The feature menu, and the copy inside libstd

Two structural details in the manifest are worth understanding before choosing how to depend on the crate.

The first is the feature layout. There is read-core, which is the bare minimum for reading without the standard library, a read feature that builds on it, and read-all which adds std and the endian reader. The write feature is separate, std is separate, and fallible-iterator is opt in. The default is read-all plus write, so an ordinary dependency gets the standard library, the endian reader and the writer. A tool that needs to run in a no_std environment, or that only wants to read, can turn features off deliberately, and the read-core split exists because the standard library is not always available.

The second is the rustc-dep-of-std feature, with the comment that it is an internal feature, only used when building as part of libstd, and not part of the stable interface of the crate. Alongside it are the internal dependencies on rustc-std-workspace-core and rustc-std-workspace-alloc, the shim crates the Rust project uses to build a library that can be vendored into the standard library.

That tells you something significant: gimli is used to build the Rust standard library's own DWARF support. The library that parses debugging information is itself part of what produces debugging information for Rust binaries, and a change to it can affect the standard library. It also means the internals are exercised at a scale and in an environment that ordinary crate tests do not reproduce, which is a reasonable thing to know before you rely on an internal detail.

Around the library itself the repository is set up like a parser crate should be. There is a benches/ directory with criterion as the benchmark harness, a fuzz/ directory, a fixtures/ directory, a tests/ directory, a clippy.toml, a rustfmt.toml, a CHANGELOG.md, a releases/ directory, and a crates/ directory holding the examples crate.

## Conclusion

gimli suits a developer writing a debugger, profiler, binary analysis tool or symbolication service who needs precise control over how DWARF data is parsed, and it suits anyone who needs to write DWARF back out rather than only read it. It is the wrong choice if you want a batteries included object file parser, because gimli makes no assumptions about the container format and leaves the ELF or Mach-O loading to you, and a poor match if your tool is not written in Rust, since the crate is a Rust library with a Rust minimum version. Before adopting, decide which feature set you need, since the default bundle turns on the standard library, the endian reader and the writer, and check the object crate if you do not want to write the object file loading yourself.

## FAQ

### How do I add gimli to a Rust project?

Run cargo add gimli. The minimum supported Rust version is 1.88, the crate version is 0.34.0 and the edition is 2024. The default features turn on the full read stack, the standard library, the endian reader and the writer.

### Does gimli parse ELF and Mach-O files?

No. gimli makes no assumptions about the object file format, so you have to provide an ELF loader on Linux or a Mach-O loader on macOS. The project suggests the cross-platform object crate from the same organisation if you do not want to write that yourself.

### Can gimli write DWARF as well as read it?

Yes, the write feature enables it, and the examples include a simple DWARF writer, a converter that reads DWARF sections and writes them back, and a converter that removes entries for dead code.

### What licence is gimli released under?

It is licensed under either the Apache License 2.0 or the MIT licence, at your option, with both licence files in the repository. Contributions are dual licensed on the same terms unless stated otherwise.

## Sources

- [gimli-rs/gimli on GitHub](https://github.com/gimli-rs/gimli)
- [Issues](https://github.com/gimli-rs/gimli/issues)
- [License: Apache-2.0](https://github.com/gimli-rs/gimli/blob/main/LICENSE)
- [Project website](https://docs.rs/gimli/)
- [README](https://github.com/gimli-rs/gimli/blob/main/README.md)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/gimli-rs-gimli
