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