Skip to main content

rucc_debug/
lib.rs

1//! DWARF 5 generation.
2//!
3//! Design: `spec/11-asm-objects-debug.md`. Layer rank 10, see `spec/18-package-layout.md`.
4//!
5//! # Status
6//!
7//! The line table, the types, the functions, the variables a unit defines at file scope, the locals
8//! of those functions wherever they are kept, and the scopes the locals were declared in.
9//! [`write()`] takes one unit's worth of addresses, the places in the source they came from, the
10//! types the unit names, what each of its functions takes and gives back, what each of its
11//! file-scope variables is and where each of its locals is at each address, and gives back the
12//! sections that say so. That is enough for `addr2line` to answer a program counter with a file and
13//! a line, enough for a debugger to produce a backtrace with argument types in it, enough for one to
14//! print a global, and enough for one to print a local whether it is in the frame or in a register.
15//!
16//! A local in the frame is one `DW_OP_fbreg` for the whole of its function. A local the register
17//! allocator placed is a list instead, since one of those has to be said where it is at the program
18//! counter that is asking, and a `.debug_loclists` saying so comes out. Which of the two a local
19//! gets is decided by what lowering did with it rather than by the optimization level.
20//!
21//! What is still missing is marking a variable unavailable at a program counter where it is dead
22//! rather than leaving the address uncovered, a `.debug_frame` for a build that writes no unwind
23//! table, and the debugger differential that would say all of this is right against another
24//! compiler on the same program. That is the rest of M8 and of tamnd/rucc#9.
25//!
26//! The reason that part came first is tamnd/rucc#1558. A report from the safety monitor carries a
27//! program counter and deliberately carries no source location, because
28//! `spec/safe-memory/06-instrumentation.md` section 6.5 would rather have one line table than two
29//! that can disagree, and that only works once there is one. Until there was, every report out of a
30//! corpus run had to be read backwards out of a disassembly.
31//!
32//! Every crate in the workspace is published, and publishing implies a promise. This one is
33//! tier 3: its Rust API is explicitly unstable and will change without a major version bump.
34//! Depend on the `rucc` binary's behaviour, not on this.
35//!
36//! # Two things still waiting
37//!
38//! `Options::prefix_map` in `rucc-session` holds a `debug` list, which is what
39//! `-fdebug-prefix-map=` and `-ffile-prefix-map=` put there. Every path that reaches [`Unit`] has
40//! already been through `PrefixMap::apply`, because that is the whole reason a distribution passes
41//! those flags and because a build is only reproducible if all of the paths in it are rewritten
42//! rather than most. The rewriting is the driver's rather than this crate's for one reason: the
43//! driver is where a path is still a path, and by the time one arrives here it is a string in a
44//! table that nothing is allowed to reinterpret.
45//!
46//! `Options::compress` is the other, and it is what `-gz` put there. Every debug section this
47//! crate hands to the object writer has to be compressed the way that field says, which for
48//! `Compress::ZlibGnu` also means the section is named `.zdebug_info` rather than `.debug_info`
49//! and carries a `ZLIB` tag and a length instead of an `Elf64_Chdr`. Compressing the sections is
50//! worth more than anything else a distribution shipping debug symbols passes, so a build that
51//! asked for it and got a file twice the size it expected has a real complaint. Nothing reads it
52//! yet, and on a line table alone the saving is smaller than it will be.
53
54#![doc(html_root_url = "https://docs.rs/rucc-debug/0.10.76")]
55
56mod line;
57mod shape;
58mod tree;
59
60pub use crate::line::{Error, Function, Row, Unit, write};
61pub use crate::shape::{
62    Bits, Constant, Encoding, Global, Held, Local, Member, Param, Place, Qualifier, Reach, Scope,
63    Shape, Sig, Span, Spot,
64};
65
66/// The milestone in `spec/17-milestones.md` that fills this crate in.
67pub const MILESTONE: &str = "M8";
68
69#[cfg(test)]
70mod tests {
71    #[test]
72    fn milestone_is_recorded() {
73        assert!(super::MILESTONE.starts_with('M'));
74    }
75}