diff options
Diffstat (limited to 'vendor/gimli/src')
42 files changed, 47171 insertions, 0 deletions
diff --git a/vendor/gimli/src/arch.rs b/vendor/gimli/src/arch.rs new file mode 100644 index 0000000..af5d4ba --- /dev/null +++ b/vendor/gimli/src/arch.rs @@ -0,0 +1,839 @@ +use crate::common::Register; + +macro_rules! registers { + ($struct_name:ident, { $($name:ident = ($val:expr, $disp:expr)),+ $(,)? } + $(, aliases { $($alias_name:ident = ($alias_val:expr, $alias_disp:expr)),+ $(,)? })?) => { + #[allow(missing_docs)] + impl $struct_name { + $( + pub const $name: Register = Register($val); + )+ + $( + $(pub const $alias_name: Register = Register($alias_val);)+ + )* + } + + impl $struct_name { + /// The name of a register, or `None` if the register number is unknown. + /// + /// Only returns the primary name for registers that alias with others. + pub fn register_name(register: Register) -> Option<&'static str> { + match register { + $( + Self::$name => Some($disp), + )+ + _ => return None, + } + } + + /// Converts a register name into a register number. + pub fn name_to_register(value: &str) -> Option<Register> { + match value { + $( + $disp => Some(Self::$name), + )+ + $( + $($alias_disp => Some(Self::$alias_name),)+ + )* + _ => return None, + } + } + } + }; +} + +/// ARM architecture specific definitions. +/// +/// See [DWARF for the ARM Architecture]( +/// https://github.com/ARM-software/abi-aa/blob/main/aadwarf32/aadwarf32.rst). +#[derive(Debug, Clone, Copy)] +pub struct Arm; + +registers!(Arm, { + R0 = (0, "R0"), + R1 = (1, "R1"), + R2 = (2, "R2"), + R3 = (3, "R3"), + R4 = (4, "R4"), + R5 = (5, "R5"), + R6 = (6, "R6"), + R7 = (7, "R7"), + R8 = (8, "R8"), + R9 = (9, "R9"), + R10 = (10, "R10"), + R11 = (11, "R11"), + R12 = (12, "R12"), + R13 = (13, "R13"), + R14 = (14, "R14"), + R15 = (15, "R15"), + + WCGR0 = (104, "wCGR0"), + WCGR1 = (105, "wCGR1"), + WCGR2 = (106, "wCGR2"), + WCGR3 = (107, "wCGR3"), + WCGR4 = (108, "wCGR4"), + WCGR5 = (109, "wCGR5"), + WCGR6 = (110, "wCGR6"), + WCGR7 = (111, "wCGR7"), + + WR0 = (112, "wR0"), + WR1 = (113, "wR1"), + WR2 = (114, "wR2"), + WR3 = (115, "wR3"), + WR4 = (116, "wR4"), + WR5 = (117, "wR5"), + WR6 = (118, "wR6"), + WR7 = (119, "wR7"), + WR8 = (120, "wR8"), + WR9 = (121, "wR9"), + WR10 = (122, "wR10"), + WR11 = (123, "wR11"), + WR12 = (124, "wR12"), + WR13 = (125, "wR13"), + WR14 = (126, "wR14"), + WR15 = (127, "wR15"), + + SPSR = (128, "SPSR"), + SPSR_FIQ = (129, "SPSR_FIQ"), + SPSR_IRQ = (130, "SPSR_IRQ"), + SPSR_ABT = (131, "SPSR_ABT"), + SPSR_UND = (132, "SPSR_UND"), + SPSR_SVC = (133, "SPSR_SVC"), + + RA_AUTH_CODE = (143, "RA_AUTH_CODE"), + + R8_USR = (144, "R8_USR"), + R9_USR = (145, "R9_USR"), + R10_USR = (146, "R10_USR"), + R11_USR = (147, "R11_USR"), + R12_USR = (148, "R12_USR"), + R13_USR = (149, "R13_USR"), + R14_USR = (150, "R14_USR"), + + R8_FIQ = (151, "R8_FIQ"), + R9_FIQ = (152, "R9_FIQ"), + R10_FIQ = (153, "R10_FIQ"), + R11_FIQ = (154, "R11_FIQ"), + R12_FIQ = (155, "R12_FIQ"), + R13_FIQ = (156, "R13_FIQ"), + R14_FIQ = (157, "R14_FIQ"), + + R13_IRQ = (158, "R13_IRQ"), + R14_IRQ = (159, "R14_IRQ"), + + R13_ABT = (160, "R13_ABT"), + R14_ABT = (161, "R14_ABT"), + + R13_UND = (162, "R13_UND"), + R14_UND = (163, "R14_UND"), + + R13_SVC = (164, "R13_SVC"), + R14_SVC = (165, "R14_SVC"), + + WC0 = (192, "wC0"), + WC1 = (193, "wC1"), + WC2 = (194, "wC2"), + WC3 = (195, "wC3"), + WC4 = (196, "wC4"), + WC5 = (197, "wC5"), + WC6 = (198, "wC6"), + WC7 = (199, "wC7"), + + D0 = (256, "D0"), + D1 = (257, "D1"), + D2 = (258, "D2"), + D3 = (259, "D3"), + D4 = (260, "D4"), + D5 = (261, "D5"), + D6 = (262, "D6"), + D7 = (263, "D7"), + D8 = (264, "D8"), + D9 = (265, "D9"), + D10 = (266, "D10"), + D11 = (267, "D11"), + D12 = (268, "D12"), + D13 = (269, "D13"), + D14 = (270, "D14"), + D15 = (271, "D15"), + D16 = (272, "D16"), + D17 = (273, "D17"), + D18 = (274, "D18"), + D19 = (275, "D19"), + D20 = (276, "D20"), + D21 = (277, "D21"), + D22 = (278, "D22"), + D23 = (279, "D23"), + D24 = (280, "D24"), + D25 = (281, "D25"), + D26 = (282, "D26"), + D27 = (283, "D27"), + D28 = (284, "D28"), + D29 = (285, "D29"), + D30 = (286, "D30"), + D31 = (287, "D31"), + + TPIDRURO = (320, "TPIDRURO"), + TPIDRURW = (321, "TPIDRURW"), + TPIDPR = (322, "TPIDPR"), + HTPIDPR = (323, "HTPIDPR"), +}, +aliases { + SP = (13, "SP"), + LR = (14, "LR"), + PC = (15, "PC"), + + ACC0 = (104, "ACC0"), + ACC1 = (105, "ACC1"), + ACC2 = (106, "ACC2"), + ACC3 = (107, "ACC3"), + ACC4 = (108, "ACC4"), + ACC5 = (109, "ACC5"), + ACC6 = (110, "ACC6"), + ACC7 = (111, "ACC7"), + + S0 = (256, "S0"), + S1 = (256, "S1"), + S2 = (257, "S2"), + S3 = (257, "S3"), + S4 = (258, "S4"), + S5 = (258, "S5"), + S6 = (259, "S6"), + S7 = (259, "S7"), + S8 = (260, "S8"), + S9 = (260, "S9"), + S10 = (261, "S10"), + S11 = (261, "S11"), + S12 = (262, "S12"), + S13 = (262, "S13"), + S14 = (263, "S14"), + S15 = (263, "S15"), + S16 = (264, "S16"), + S17 = (264, "S17"), + S18 = (265, "S18"), + S19 = (265, "S19"), + S20 = (266, "S20"), + S21 = (266, "S21"), + S22 = (267, "S22"), + S23 = (267, "S23"), + S24 = (268, "S24"), + S25 = (268, "S25"), + S26 = (269, "S26"), + S27 = (269, "S27"), + S28 = (270, "S28"), + S29 = (270, "S29"), + S30 = (271, "S30"), + S31 = (271, "S31"), +}); + +/// ARM 64-bit (AArch64) architecture specific definitions. +/// +/// See [DWARF for the ARM 64-bit Architecture]( +/// https://github.com/ARM-software/abi-aa/blob/main/aadwarf64/aadwarf64.rst). +#[derive(Debug, Clone, Copy)] +pub struct AArch64; + +registers!(AArch64, { + X0 = (0, "X0"), + X1 = (1, "X1"), + X2 = (2, "X2"), + X3 = (3, "X3"), + X4 = (4, "X4"), + X5 = (5, "X5"), + X6 = (6, "X6"), + X7 = (7, "X7"), + X8 = (8, "X8"), + X9 = (9, "X9"), + X10 = (10, "X10"), + X11 = (11, "X11"), + X12 = (12, "X12"), + X13 = (13, "X13"), + X14 = (14, "X14"), + X15 = (15, "X15"), + X16 = (16, "X16"), + X17 = (17, "X17"), + X18 = (18, "X18"), + X19 = (19, "X19"), + X20 = (20, "X20"), + X21 = (21, "X21"), + X22 = (22, "X22"), + X23 = (23, "X23"), + X24 = (24, "X24"), + X25 = (25, "X25"), + X26 = (26, "X26"), + X27 = (27, "X27"), + X28 = (28, "X28"), + X29 = (29, "X29"), + X30 = (30, "X30"), + SP = (31, "SP"), + PC = (32, "PC"), + ELR_MODE = (33, "ELR_mode"), + RA_SIGN_STATE = (34, "RA_SIGN_STATE"), + TPIDRRO_EL0 = (35, "TPIDRRO_EL0"), + TPIDR_EL0 = (36, "TPIDR_EL0"), + TPIDR_EL1 = (37, "TPIDR_EL1"), + TPIDR_EL2 = (38, "TPIDR_EL2"), + TPIDR_EL3 = (39, "TPIDR_EL3"), + + VG = (46, "VG"), + FFR = (47, "FFR"), + + P0 = (48, "P0"), + P1 = (49, "P1"), + P2 = (50, "P2"), + P3 = (51, "P3"), + P4 = (52, "P4"), + P5 = (53, "P5"), + P6 = (54, "P6"), + P7 = (55, "P7"), + P8 = (56, "P8"), + P9 = (57, "P9"), + P10 = (58, "P10"), + P11 = (59, "P11"), + P12 = (60, "P12"), + P13 = (61, "P13"), + P14 = (62, "P14"), + P15 = (63, "P15"), + + V0 = (64, "V0"), + V1 = (65, "V1"), + V2 = (66, "V2"), + V3 = (67, "V3"), + V4 = (68, "V4"), + V5 = (69, "V5"), + V6 = (70, "V6"), + V7 = (71, "V7"), + V8 = (72, "V8"), + V9 = (73, "V9"), + V10 = (74, "V10"), + V11 = (75, "V11"), + V12 = (76, "V12"), + V13 = (77, "V13"), + V14 = (78, "V14"), + V15 = (79, "V15"), + V16 = (80, "V16"), + V17 = (81, "V17"), + V18 = (82, "V18"), + V19 = (83, "V19"), + V20 = (84, "V20"), + V21 = (85, "V21"), + V22 = (86, "V22"), + V23 = (87, "V23"), + V24 = (88, "V24"), + V25 = (89, "V25"), + V26 = (90, "V26"), + V27 = (91, "V27"), + V28 = (92, "V28"), + V29 = (93, "V29"), + V30 = (94, "V30"), + V31 = (95, "V31"), + + Z0 = (96, "Z0"), + Z1 = (97, "Z1"), + Z2 = (98, "Z2"), + Z3 = (99, "Z3"), + Z4 = (100, "Z4"), + Z5 = (101, "Z5"), + Z6 = (102, "Z6"), + Z7 = (103, "Z7"), + Z8 = (104, "Z8"), + Z9 = (105, "Z9"), + Z10 = (106, "Z10"), + Z11 = (107, "Z11"), + Z12 = (108, "Z12"), + Z13 = (109, "Z13"), + Z14 = (110, "Z14"), + Z15 = (111, "Z15"), + Z16 = (112, "Z16"), + Z17 = (113, "Z17"), + Z18 = (114, "Z18"), + Z19 = (115, "Z19"), + Z20 = (116, "Z20"), + Z21 = (117, "Z21"), + Z22 = (118, "Z22"), + Z23 = (119, "Z23"), + Z24 = (120, "Z24"), + Z25 = (121, "Z25"), + Z26 = (122, "Z26"), + Z27 = (123, "Z27"), + Z28 = (124, "Z28"), + Z29 = (125, "Z29"), + Z30 = (126, "Z30"), + Z31 = (127, "Z31"), +}); + +/// LoongArch architecture specific definitions. +/// +/// See [LoongArch ELF psABI specification](https://loongson.github.io/LoongArch-Documentation/LoongArch-ELF-ABI-EN.html). +#[derive(Debug, Clone, Copy)] +pub struct LoongArch; + +registers!(LoongArch, { + R0 = (0, "$r0"), + R1 = (1, "$r1"), + R2 = (2, "$r2"), + R3 = (3, "$r3"), + R4 = (4, "$r4"), + R5 = (5, "$r5"), + R6 = (6, "$r6"), + R7 = (7, "$r7"), + R8 = (8, "$r8"), + R9 = (9, "$r9"), + R10 = (10, "$r10"), + R11 = (11, "$r11"), + R12 = (12, "$r12"), + R13 = (13, "$r13"), + R14 = (14, "$r14"), + R15 = (15, "$r15"), + R16 = (16, "$r16"), + R17 = (17, "$r17"), + R18 = (18, "$r18"), + R19 = (19, "$r19"), + R20 = (20, "$r20"), + R21 = (21, "$r21"), + R22 = (22, "$r22"), + R23 = (23, "$r23"), + R24 = (24, "$r24"), + R25 = (25, "$r25"), + R26 = (26, "$r26"), + R27 = (27, "$r27"), + R28 = (28, "$r28"), + R29 = (29, "$r29"), + R30 = (30, "$r30"), + R31 = (31, "$r31"), + + F0 = (32, "$f0"), + F1 = (33, "$f1"), + F2 = (34, "$f2"), + F3 = (35, "$f3"), + F4 = (36, "$f4"), + F5 = (37, "$f5"), + F6 = (38, "$f6"), + F7 = (39, "$f7"), + F8 = (40, "$f8"), + F9 = (41, "$f9"), + F10 = (42, "$f10"), + F11 = (43, "$f11"), + F12 = (44, "$f12"), + F13 = (45, "$f13"), + F14 = (46, "$f14"), + F15 = (47, "$f15"), + F16 = (48, "$f16"), + F17 = (49, "$f17"), + F18 = (50, "$f18"), + F19 = (51, "$f19"), + F20 = (52, "$f20"), + F21 = (53, "$f21"), + F22 = (54, "$f22"), + F23 = (55, "$f23"), + F24 = (56, "$f24"), + F25 = (57, "$f25"), + F26 = (58, "$f26"), + F27 = (59, "$f27"), + F28 = (60, "$f28"), + F29 = (61, "$f29"), + F30 = (62, "$f30"), + F31 = (63, "$f31"), + FCC0 = (64, "$fcc0"), + FCC1 = (65, "$fcc1"), + FCC2 = (66, "$fcc2"), + FCC3 = (67, "$fcc3"), + FCC4 = (68, "$fcc4"), + FCC5 = (69, "$fcc5"), + FCC6 = (70, "$fcc6"), + FCC7 = (71, "$fcc7"), +}, +aliases { + ZERO = (0, "$zero"), + RA = (1, "$ra"), + TP = (2, "$tp"), + SP = (3, "$sp"), + A0 = (4, "$a0"), + A1 = (5, "$a1"), + A2 = (6, "$a2"), + A3 = (7, "$a3"), + A4 = (8, "$a4"), + A5 = (9, "$a5"), + A6 = (10, "$a6"), + A7 = (11, "$a7"), + T0 = (12, "$t0"), + T1 = (13, "$t1"), + T2 = (14, "$t2"), + T3 = (15, "$t3"), + T4 = (16, "$t4"), + T5 = (17, "$t5"), + T6 = (18, "$t6"), + T7 = (19, "$t7"), + T8 = (20, "$t8"), + FP = (22, "$fp"), + S0 = (23, "$s0"), + S1 = (24, "$s1"), + S2 = (25, "$s2"), + S3 = (26, "$s3"), + S4 = (27, "$s4"), + S5 = (28, "$s5"), + S6 = (29, "$s6"), + S7 = (30, "$s7"), + S8 = (31, "$s8"), + + FA0 = (32, "$fa0"), + FA1 = (33, "$fa1"), + FA2 = (34, "$fa2"), + FA3 = (35, "$fa3"), + FA4 = (36, "$fa4"), + FA5 = (37, "$fa5"), + FA6 = (38, "$fa6"), + FA7 = (39, "$fa7"), + FT0 = (40, "$ft0"), + FT1 = (41, "$ft1"), + FT2 = (42, "$ft2"), + FT3 = (43, "$ft3"), + FT4 = (44, "$ft4"), + FT5 = (45, "$ft5"), + FT6 = (46, "$ft6"), + FT7 = (47, "$ft7"), + FT8 = (48, "$ft8"), + FT9 = (49, "$ft9"), + FT10 = (50, "$ft10"), + FT11 = (51, "$ft11"), + FT12 = (52, "$ft12"), + FT13 = (53, "$ft13"), + FT14 = (54, "$ft14"), + FT15 = (55, "$ft15"), + FS0 = (56, "$fs0"), + FS1 = (57, "$fs1"), + FS2 = (58, "$fs2"), + FS3 = (59, "$fs3"), + FS4 = (60, "$fs4"), + FS5 = (61, "$fs5"), + FS6 = (62, "$fs6"), + FS7 = (63, "$fs7"), +}); + +/// RISC-V architecture specific definitions. +/// +/// See [RISC-V ELF psABI specification](https://github.com/riscv/riscv-elf-psabi-doc). +#[derive(Debug, Clone, Copy)] +pub struct RiscV; + +registers!(RiscV, { + X0 = (0, "x0"), + X1 = (1, "x1"), + X2 = (2, "x2"), + X3 = (3, "x3"), + X4 = (4, "x4"), + X5 = (5, "x5"), + X6 = (6, "x6"), + X7 = (7, "x7"), + X8 = (8, "x8"), + X9 = (9, "x9"), + X10 = (10, "x10"), + X11 = (11, "x11"), + X12 = (12, "x12"), + X13 = (13, "x13"), + X14 = (14, "x14"), + X15 = (15, "x15"), + X16 = (16, "x16"), + X17 = (17, "x17"), + X18 = (18, "x18"), + X19 = (19, "x19"), + X20 = (20, "x20"), + X21 = (21, "x21"), + X22 = (22, "x22"), + X23 = (23, "x23"), + X24 = (24, "x24"), + X25 = (25, "x25"), + X26 = (26, "x26"), + X27 = (27, "x27"), + X28 = (28, "x28"), + X29 = (29, "x29"), + X30 = (30, "x30"), + X31 = (31, "x31"), + + F0 = (32, "f0"), + F1 = (33, "f1"), + F2 = (34, "f2"), + F3 = (35, "f3"), + F4 = (36, "f4"), + F5 = (37, "f5"), + F6 = (38, "f6"), + F7 = (39, "f7"), + F8 = (40, "f8"), + F9 = (41, "f9"), + F10 = (42, "f10"), + F11 = (43, "f11"), + F12 = (44, "f12"), + F13 = (45, "f13"), + F14 = (46, "f14"), + F15 = (47, "f15"), + F16 = (48, "f16"), + F17 = (49, "f17"), + F18 = (50, "f18"), + F19 = (51, "f19"), + F20 = (52, "f20"), + F21 = (53, "f21"), + F22 = (54, "f22"), + F23 = (55, "f23"), + F24 = (56, "f24"), + F25 = (57, "f25"), + F26 = (58, "f26"), + F27 = (59, "f27"), + F28 = (60, "f28"), + F29 = (61, "f29"), + F30 = (62, "f30"), + F31 = (63, "f31"), +}, +aliases { + ZERO = (0, "zero"), + RA = (1, "ra"), + SP = (2, "sp"), + GP = (3, "gp"), + TP = (4, "tp"), + T0 = (5, "t0"), + T1 = (6, "t1"), + T2 = (7, "t2"), + S0 = (8, "s0"), + S1 = (9, "s1"), + A0 = (10, "a0"), + A1 = (11, "a1"), + A2 = (12, "a2"), + A3 = (13, "a3"), + A4 = (14, "a4"), + A5 = (15, "a5"), + A6 = (16, "a6"), + A7 = (17, "a7"), + S2 = (18, "s2"), + S3 = (19, "s3"), + S4 = (20, "s4"), + S5 = (21, "s5"), + S6 = (22, "s6"), + S7 = (23, "s7"), + S8 = (24, "s8"), + S9 = (25, "s9"), + S10 = (26, "s10"), + S11 = (27, "s11"), + T3 = (28, "t3"), + T4 = (29, "t4"), + T5 = (30, "t5"), + T6 = (31, "t6"), + + FT0 = (32, "ft0"), + FT1 = (33, "ft1"), + FT2 = (34, "ft2"), + FT3 = (35, "ft3"), + FT4 = (36, "ft4"), + FT5 = (37, "ft5"), + FT6 = (38, "ft6"), + FT7 = (39, "ft7"), + FS0 = (40, "fs0"), + FS1 = (41, "fs1"), + FA0 = (42, "fa0"), + FA1 = (43, "fa1"), + FA2 = (44, "fa2"), + FA3 = (45, "fa3"), + FA4 = (46, "fa4"), + FA5 = (47, "fa5"), + FA6 = (48, "fa6"), + FA7 = (49, "fa7"), + FS2 = (50, "fs2"), + FS3 = (51, "fs3"), + FS4 = (52, "fs4"), + FS5 = (53, "fs5"), + FS6 = (54, "fs6"), + FS7 = (55, "fs7"), + FS8 = (56, "fs8"), + FS9 = (57, "fs9"), + FS10 = (58, "fs10"), + FS11 = (59, "fs11"), + FT8 = (60, "ft8"), + FT9 = (61, "ft9"), + FT10 = (62, "ft10"), + FT11 = (63, "ft11"), +}); + +/// Intel i386 architecture specific definitions. +/// +/// See Intel386 psABi version 1.1 at the [X86 psABI wiki](https://github.com/hjl-tools/x86-psABI/wiki/X86-psABI). +#[derive(Debug, Clone, Copy)] +pub struct X86; + +registers!(X86, { + EAX = (0, "eax"), + ECX = (1, "ecx"), + EDX = (2, "edx"), + EBX = (3, "ebx"), + ESP = (4, "esp"), + EBP = (5, "ebp"), + ESI = (6, "esi"), + EDI = (7, "edi"), + + // Return Address register. This is stored in `0(%esp, "")` and is not a physical register. + RA = (8, "RA"), + + ST0 = (11, "st0"), + ST1 = (12, "st1"), + ST2 = (13, "st2"), + ST3 = (14, "st3"), + ST4 = (15, "st4"), + ST5 = (16, "st5"), + ST6 = (17, "st6"), + ST7 = (18, "st7"), + + XMM0 = (21, "xmm0"), + XMM1 = (22, "xmm1"), + XMM2 = (23, "xmm2"), + XMM3 = (24, "xmm3"), + XMM4 = (25, "xmm4"), + XMM5 = (26, "xmm5"), + XMM6 = (27, "xmm6"), + XMM7 = (28, "xmm7"), + + MM0 = (29, "mm0"), + MM1 = (30, "mm1"), + MM2 = (31, "mm2"), + MM3 = (32, "mm3"), + MM4 = (33, "mm4"), + MM5 = (34, "mm5"), + MM6 = (35, "mm6"), + MM7 = (36, "mm7"), + + MXCSR = (39, "mxcsr"), + + ES = (40, "es"), + CS = (41, "cs"), + SS = (42, "ss"), + DS = (43, "ds"), + FS = (44, "fs"), + GS = (45, "gs"), + + TR = (48, "tr"), + LDTR = (49, "ldtr"), + + FS_BASE = (93, "fs.base"), + GS_BASE = (94, "gs.base"), +}); + +/// AMD64 architecture specific definitions. +/// +/// See x86-64 psABI version 1.0 at the [X86 psABI wiki](https://github.com/hjl-tools/x86-psABI/wiki/X86-psABI). +#[derive(Debug, Clone, Copy)] +pub struct X86_64; + +registers!(X86_64, { + RAX = (0, "rax"), + RDX = (1, "rdx"), + RCX = (2, "rcx"), + RBX = (3, "rbx"), + RSI = (4, "rsi"), + RDI = (5, "rdi"), + RBP = (6, "rbp"), + RSP = (7, "rsp"), + + R8 = (8, "r8"), + R9 = (9, "r9"), + R10 = (10, "r10"), + R11 = (11, "r11"), + R12 = (12, "r12"), + R13 = (13, "r13"), + R14 = (14, "r14"), + R15 = (15, "r15"), + + // Return Address register. This is stored in `0(%rsp, "")` and is not a physical register. + RA = (16, "RA"), + + XMM0 = (17, "xmm0"), + XMM1 = (18, "xmm1"), + XMM2 = (19, "xmm2"), + XMM3 = (20, "xmm3"), + XMM4 = (21, "xmm4"), + XMM5 = (22, "xmm5"), + XMM6 = (23, "xmm6"), + XMM7 = (24, "xmm7"), + + XMM8 = (25, "xmm8"), + XMM9 = (26, "xmm9"), + XMM10 = (27, "xmm10"), + XMM11 = (28, "xmm11"), + XMM12 = (29, "xmm12"), + XMM13 = (30, "xmm13"), + XMM14 = (31, "xmm14"), + XMM15 = (32, "xmm15"), + + ST0 = (33, "st0"), + ST1 = (34, "st1"), + ST2 = (35, "st2"), + ST3 = (36, "st3"), + ST4 = (37, "st4"), + ST5 = (38, "st5"), + ST6 = (39, "st6"), + ST7 = (40, "st7"), + + MM0 = (41, "mm0"), + MM1 = (42, "mm1"), + MM2 = (43, "mm2"), + MM3 = (44, "mm3"), + MM4 = (45, "mm4"), + MM5 = (46, "mm5"), + MM6 = (47, "mm6"), + MM7 = (48, "mm7"), + + RFLAGS = (49, "rFLAGS"), + ES = (50, "es"), + CS = (51, "cs"), + SS = (52, "ss"), + DS = (53, "ds"), + FS = (54, "fs"), + GS = (55, "gs"), + + FS_BASE = (58, "fs.base"), + GS_BASE = (59, "gs.base"), + + TR = (62, "tr"), + LDTR = (63, "ldtr"), + MXCSR = (64, "mxcsr"), + FCW = (65, "fcw"), + FSW = (66, "fsw"), + + XMM16 = (67, "xmm16"), + XMM17 = (68, "xmm17"), + XMM18 = (69, "xmm18"), + XMM19 = (70, "xmm19"), + XMM20 = (71, "xmm20"), + XMM21 = (72, "xmm21"), + XMM22 = (73, "xmm22"), + XMM23 = (74, "xmm23"), + XMM24 = (75, "xmm24"), + XMM25 = (76, "xmm25"), + XMM26 = (77, "xmm26"), + XMM27 = (78, "xmm27"), + XMM28 = (79, "xmm28"), + XMM29 = (80, "xmm29"), + XMM30 = (81, "xmm30"), + XMM31 = (82, "xmm31"), + + K0 = (118, "k0"), + K1 = (119, "k1"), + K2 = (120, "k2"), + K3 = (121, "k3"), + K4 = (122, "k4"), + K5 = (123, "k5"), + K6 = (124, "k6"), + K7 = (125, "k7"), +}); + +#[cfg(test)] +mod tests { + + #[test] + #[cfg(feature = "std")] + fn test_aarch64_registers() { + use super::*; + use std::collections::HashSet; + + let mut names = HashSet::new(); + for n in (0..=39).chain(46..=127) { + let name = AArch64::register_name(Register(n)) + .unwrap_or_else(|| panic!("Register {} should have a name.", n)); + assert!(names.insert(name)); + } + } +} diff --git a/vendor/gimli/src/common.rs b/vendor/gimli/src/common.rs new file mode 100644 index 0000000..fc6693d --- /dev/null +++ b/vendor/gimli/src/common.rs @@ -0,0 +1,391 @@ +/// Whether the format of a compilation unit is 32- or 64-bit. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum Format { + /// 64-bit DWARF + Dwarf64 = 8, + /// 32-bit DWARF + Dwarf32 = 4, +} + +impl Format { + /// Return the serialized size of an initial length field for the format. + #[inline] + pub fn initial_length_size(self) -> u8 { + match self { + Format::Dwarf32 => 4, + Format::Dwarf64 => 12, + } + } + + /// Return the natural word size for the format + #[inline] + pub fn word_size(self) -> u8 { + match self { + Format::Dwarf32 => 4, + Format::Dwarf64 => 8, + } + } +} + +/// Which vendor extensions to support. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[non_exhaustive] +pub enum Vendor { + /// A default set of extensions, including some common GNU extensions. + Default, + /// AAarch64 extensions. + AArch64, +} + +/// Encoding parameters that are commonly used for multiple DWARF sections. +/// +/// This is intended to be small enough to pass by value. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +// `address_size` and `format` are used more often than `version`, so keep +// them first. +#[repr(C)] +pub struct Encoding { + /// The size of an address. + pub address_size: u8, + + // The size of a segment selector. + // TODO: pub segment_size: u8, + /// Whether the DWARF format is 32- or 64-bit. + pub format: Format, + + /// The DWARF version of the header. + pub version: u16, +} + +/// Encoding parameters for a line number program. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct LineEncoding { + /// The size in bytes of the smallest target machine instruction. + pub minimum_instruction_length: u8, + + /// The maximum number of individual operations that may be encoded in an + /// instruction. + pub maximum_operations_per_instruction: u8, + + /// The initial value of the `is_stmt` register. + pub default_is_stmt: bool, + + /// The minimum value which a special opcode can add to the line register. + pub line_base: i8, + + /// The range of values which a special opcode can add to the line register. + pub line_range: u8, +} + +impl Default for LineEncoding { + fn default() -> Self { + // Values from LLVM. + LineEncoding { + minimum_instruction_length: 1, + maximum_operations_per_instruction: 1, + default_is_stmt: true, + line_base: -5, + line_range: 14, + } + } +} + +/// A DWARF register number. +/// +/// The meaning of this value is ABI dependent. This is generally encoded as +/// a ULEB128, but supported architectures need 16 bits at most. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub struct Register(pub u16); + +/// An offset into the `.debug_abbrev` section. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct DebugAbbrevOffset<T = usize>(pub T); + +/// An offset to a set of entries in the `.debug_addr` section. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct DebugAddrBase<T = usize>(pub T); + +/// An index into a set of addresses in the `.debug_addr` section. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct DebugAddrIndex<T = usize>(pub T); + +/// An offset into the `.debug_aranges` section. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct DebugArangesOffset<T = usize>(pub T); + +/// An offset into the `.debug_info` section. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Hash)] +pub struct DebugInfoOffset<T = usize>(pub T); + +/// An offset into the `.debug_line` section. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct DebugLineOffset<T = usize>(pub T); + +/// An offset into the `.debug_line_str` section. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct DebugLineStrOffset<T = usize>(pub T); + +/// An offset into either the `.debug_loc` section or the `.debug_loclists` section, +/// depending on the version of the unit the offset was contained in. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct LocationListsOffset<T = usize>(pub T); + +/// An offset to a set of location list offsets in the `.debug_loclists` section. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct DebugLocListsBase<T = usize>(pub T); + +/// An index into a set of location list offsets in the `.debug_loclists` section. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct DebugLocListsIndex<T = usize>(pub T); + +/// An offset into the `.debug_macinfo` section. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct DebugMacinfoOffset<T = usize>(pub T); + +/// An offset into the `.debug_macro` section. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct DebugMacroOffset<T = usize>(pub T); + +/// An offset into either the `.debug_ranges` section or the `.debug_rnglists` section, +/// depending on the version of the unit the offset was contained in. +/// +/// If this is from a DWARF 4 DWO file, then it must additionally be offset by the +/// value of `DW_AT_GNU_ranges_base`. You can use `Dwarf::ranges_offset_from_raw` to do this. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct RawRangeListsOffset<T = usize>(pub T); + +/// An offset into either the `.debug_ranges` section or the `.debug_rnglists` section, +/// depending on the version of the unit the offset was contained in. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct RangeListsOffset<T = usize>(pub T); + +/// An offset to a set of range list offsets in the `.debug_rnglists` section. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct DebugRngListsBase<T = usize>(pub T); + +/// An index into a set of range list offsets in the `.debug_rnglists` section. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct DebugRngListsIndex<T = usize>(pub T); + +/// An offset into the `.debug_str` section. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct DebugStrOffset<T = usize>(pub T); + +/// An offset to a set of entries in the `.debug_str_offsets` section. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct DebugStrOffsetsBase<T = usize>(pub T); + +/// An index into a set of entries in the `.debug_str_offsets` section. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct DebugStrOffsetsIndex<T = usize>(pub T); + +/// An offset into the `.debug_types` section. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Hash)] +pub struct DebugTypesOffset<T = usize>(pub T); + +/// A type signature as used in the `.debug_types` section. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct DebugTypeSignature(pub u64); + +/// An offset into the `.debug_frame` section. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct DebugFrameOffset<T = usize>(pub T); + +impl<T> From<T> for DebugFrameOffset<T> { + #[inline] + fn from(o: T) -> Self { + DebugFrameOffset(o) + } +} + +/// An offset into the `.eh_frame` section. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct EhFrameOffset<T = usize>(pub T); + +impl<T> From<T> for EhFrameOffset<T> { + #[inline] + fn from(o: T) -> Self { + EhFrameOffset(o) + } +} + +/// An offset into the `.debug_info` or `.debug_types` sections. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Hash)] +pub enum UnitSectionOffset<T = usize> { + /// An offset into the `.debug_info` section. + DebugInfoOffset(DebugInfoOffset<T>), + /// An offset into the `.debug_types` section. + DebugTypesOffset(DebugTypesOffset<T>), +} + +impl<T> From<DebugInfoOffset<T>> for UnitSectionOffset<T> { + fn from(offset: DebugInfoOffset<T>) -> Self { + UnitSectionOffset::DebugInfoOffset(offset) + } +} + +impl<T> From<DebugTypesOffset<T>> for UnitSectionOffset<T> { + fn from(offset: DebugTypesOffset<T>) -> Self { + UnitSectionOffset::DebugTypesOffset(offset) + } +} + +impl<T> UnitSectionOffset<T> +where + T: Clone, +{ + /// Returns the `DebugInfoOffset` inside, or `None` otherwise. + pub fn as_debug_info_offset(&self) -> Option<DebugInfoOffset<T>> { + match self { + UnitSectionOffset::DebugInfoOffset(offset) => Some(offset.clone()), + UnitSectionOffset::DebugTypesOffset(_) => None, + } + } + /// Returns the `DebugTypesOffset` inside, or `None` otherwise. + pub fn as_debug_types_offset(&self) -> Option<DebugTypesOffset<T>> { + match self { + UnitSectionOffset::DebugInfoOffset(_) => None, + UnitSectionOffset::DebugTypesOffset(offset) => Some(offset.clone()), + } + } +} + +/// An identifier for a DWARF section. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Hash)] +pub enum SectionId { + /// The `.debug_abbrev` section. + DebugAbbrev, + /// The `.debug_addr` section. + DebugAddr, + /// The `.debug_aranges` section. + DebugAranges, + /// The `.debug_cu_index` section. + DebugCuIndex, + /// The `.debug_frame` section. + DebugFrame, + /// The `.eh_frame` section. + EhFrame, + /// The `.eh_frame_hdr` section. + EhFrameHdr, + /// The `.debug_info` section. + DebugInfo, + /// The `.debug_line` section. + DebugLine, + /// The `.debug_line_str` section. + DebugLineStr, + /// The `.debug_loc` section. + DebugLoc, + /// The `.debug_loclists` section. + DebugLocLists, + /// The `.debug_macinfo` section. + DebugMacinfo, + /// The `.debug_macro` section. + DebugMacro, + /// The `.debug_pubnames` section. + DebugPubNames, + /// The `.debug_pubtypes` section. + DebugPubTypes, + /// The `.debug_ranges` section. + DebugRanges, + /// The `.debug_rnglists` section. + DebugRngLists, + /// The `.debug_str` section. + DebugStr, + /// The `.debug_str_offsets` section. + DebugStrOffsets, + /// The `.debug_tu_index` section. + DebugTuIndex, + /// The `.debug_types` section. + DebugTypes, +} + +impl SectionId { + /// Returns the ELF section name for this kind. + pub fn name(self) -> &'static str { + match self { + SectionId::DebugAbbrev => ".debug_abbrev", + SectionId::DebugAddr => ".debug_addr", + SectionId::DebugAranges => ".debug_aranges", + SectionId::DebugCuIndex => ".debug_cu_index", + SectionId::DebugFrame => ".debug_frame", + SectionId::EhFrame => ".eh_frame", + SectionId::EhFrameHdr => ".eh_frame_hdr", + SectionId::DebugInfo => ".debug_info", + SectionId::DebugLine => ".debug_line", + SectionId::DebugLineStr => ".debug_line_str", + SectionId::DebugLoc => ".debug_loc", + SectionId::DebugLocLists => ".debug_loclists", + SectionId::DebugMacinfo => ".debug_macinfo", + SectionId::DebugMacro => ".debug_macro", + SectionId::DebugPubNames => ".debug_pubnames", + SectionId::DebugPubTypes => ".debug_pubtypes", + SectionId::DebugRanges => ".debug_ranges", + SectionId::DebugRngLists => ".debug_rnglists", + SectionId::DebugStr => ".debug_str", + SectionId::DebugStrOffsets => ".debug_str_offsets", + SectionId::DebugTuIndex => ".debug_tu_index", + SectionId::DebugTypes => ".debug_types", + } + } + + /// Returns the ELF section name for this kind, when found in a .dwo or .dwp file. + pub fn dwo_name(self) -> Option<&'static str> { + Some(match self { + SectionId::DebugAbbrev => ".debug_abbrev.dwo", + SectionId::DebugCuIndex => ".debug_cu_index", + SectionId::DebugInfo => ".debug_info.dwo", + SectionId::DebugLine => ".debug_line.dwo", + // The debug_loc section can be present in the dwo when using the + // GNU split-dwarf extension to DWARF4. + SectionId::DebugLoc => ".debug_loc.dwo", + SectionId::DebugLocLists => ".debug_loclists.dwo", + SectionId::DebugMacro => ".debug_macro.dwo", + SectionId::DebugRngLists => ".debug_rnglists.dwo", + SectionId::DebugStr => ".debug_str.dwo", + SectionId::DebugStrOffsets => ".debug_str_offsets.dwo", + SectionId::DebugTuIndex => ".debug_tu_index", + SectionId::DebugTypes => ".debug_types.dwo", + _ => return None, + }) + } + + /// Returns the XCOFF section name for this kind. + pub fn xcoff_name(self) -> Option<&'static str> { + Some(match self { + SectionId::DebugAbbrev => ".dwabrev", + SectionId::DebugAranges => ".dwarnge", + SectionId::DebugFrame => ".dwframe", + SectionId::DebugInfo => ".dwinfo", + SectionId::DebugLine => ".dwline", + SectionId::DebugLoc => ".dwloc", + SectionId::DebugMacinfo => ".dwmac", + SectionId::DebugPubNames => ".dwpbnms", + SectionId::DebugPubTypes => ".dwpbtyp", + SectionId::DebugRanges => ".dwrnges", + SectionId::DebugStr => ".dwstr", + _ => return None, + }) + } +} + +/// An optionally-provided implementation-defined compilation unit ID to enable +/// split DWARF and linking a split compilation unit back together. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct DwoId(pub u64); + +/// The "type" of file with DWARF debugging information. This determines, among other things, +/// which files DWARF sections should be loaded from. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum DwarfFileType { + /// A normal executable or object file. + Main, + /// A .dwo split DWARF file. + Dwo, + // TODO: Supplementary files, .dwps? +} + +impl Default for DwarfFileType { + fn default() -> Self { + DwarfFileType::Main + } +} diff --git a/vendor/gimli/src/constants.rs b/vendor/gimli/src/constants.rs new file mode 100644 index 0000000..e58050b --- /dev/null +++ b/vendor/gimli/src/constants.rs @@ -0,0 +1,1435 @@ +// This file originally from https://github.com/philipc/rust-dwarf/ and +// distributed under either MIT or Apache 2.0 licenses. +// +// Copyright 2016 The rust-dwarf Developers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Constant definitions. +//! +//! The DWARF spec's `DW_AT_*` type is represented as `struct DwAt(u16)`, +//! `DW_FORM_*` as `DwForm(u16)`, etc. +//! +//! There are also exported const definitions for each constant. + +#![allow(non_upper_case_globals)] +#![allow(missing_docs)] + +use core::fmt; + +// The `dw!` macro turns this: +// +// dw!(DwFoo(u32) { +// DW_FOO_bar = 0, +// DW_FOO_baz = 1, +// DW_FOO_bang = 2, +// }); +// +// into this: +// +// #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] +// pub struct DwFoo(pub u32); +// +// pub const DW_FOO_bar: DwFoo = DwFoo(0); +// pub const DW_FOO_baz: DwFoo = DwFoo(1); +// pub const DW_FOO_bang: DwFoo = DwFoo(2); +// +// impl DwFoo { +// pub fn static_string(&self) -> Option<&'static str> { +// ... +// } +// } +// +// impl fmt::Display for DwFoo { +// fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { +// ... +// } +// } +macro_rules! dw { + ($(#[$meta:meta])* $struct_name:ident($struct_type:ty) + { $($name:ident = $val:expr),+ $(,)? } + $(, aliases { $($alias_name:ident = $alias_val:expr),+ $(,)? })? + ) => { + $(#[$meta])* + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] + pub struct $struct_name(pub $struct_type); + + $( + pub const $name: $struct_name = $struct_name($val); + )+ + $($( + pub const $alias_name: $struct_name = $struct_name($alias_val); + )+)* + + impl $struct_name { + pub fn static_string(&self) -> Option<&'static str> { + Some(match *self { + $( + $name => stringify!($name), + )+ + _ => return None, + }) + } + } + + impl fmt::Display for $struct_name { + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + if let Some(s) = self.static_string() { + f.pad(s) + } else { + #[cfg(feature = "read")] + { + f.pad(&format!("Unknown {}: {}", stringify!($struct_name), self.0)) + } + #[cfg(not(feature = "read"))] + { + write!(f, "Unknown {}: {}", stringify!($struct_name), self.0) + } + } + } + } + }; +} + +dw!( +/// The section type field in a `.dwp` unit index. +/// +/// This is used for version 5 and later. +/// +/// See Section 7.3.5. +DwSect(u32) { + DW_SECT_INFO = 1, + DW_SECT_ABBREV = 3, + DW_SECT_LINE = 4, + DW_SECT_LOCLISTS = 5, + DW_SECT_STR_OFFSETS = 6, + DW_SECT_MACRO = 7, + DW_SECT_RNGLISTS = 8, +}); + +dw!( +/// The section type field in a `.dwp` unit index with version 2. +DwSectV2(u32) { + DW_SECT_V2_INFO = 1, + DW_SECT_V2_TYPES = 2, + DW_SECT_V2_ABBREV = 3, + DW_SECT_V2_LINE = 4, + DW_SECT_V2_LOC = 5, + DW_SECT_V2_STR_OFFSETS = 6, + DW_SECT_V2_MACINFO = 7, + DW_SECT_V2_MACRO = 8, +}); + +dw!( +/// The unit type field in a unit header. +/// +/// See Section 7.5.1, Table 7.2. +DwUt(u8) { + DW_UT_compile = 0x01, + DW_UT_type = 0x02, + DW_UT_partial = 0x03, + DW_UT_skeleton = 0x04, + DW_UT_split_compile = 0x05, + DW_UT_split_type = 0x06, + DW_UT_lo_user = 0x80, + DW_UT_hi_user = 0xff, +}); + +dw!( +/// The opcode for a call frame instruction. +/// +/// Section 7.24: +/// > Call frame instructions are encoded in one or more bytes. The primary +/// > opcode is encoded in the high order two bits of the first byte (that is, +/// > opcode = byte >> 6). An operand or extended opcode may be encoded in the +/// > low order 6 bits. Additional operands are encoded in subsequent bytes. +DwCfa(u8) { + DW_CFA_advance_loc = 0x01 << 6, + DW_CFA_offset = 0x02 << 6, + DW_CFA_restore = 0x03 << 6, + DW_CFA_nop = 0, + DW_CFA_set_loc = 0x01, + DW_CFA_advance_loc1 = 0x02, + DW_CFA_advance_loc2 = 0x03, + DW_CFA_advance_loc4 = 0x04, + DW_CFA_offset_extended = 0x05, + DW_CFA_restore_extended = 0x06, + DW_CFA_undefined = 0x07, + DW_CFA_same_value = 0x08, + DW_CFA_register = 0x09, + DW_CFA_remember_state = 0x0a, + DW_CFA_restore_state = 0x0b, + DW_CFA_def_cfa = 0x0c, + DW_CFA_def_cfa_register = 0x0d, + DW_CFA_def_cfa_offset = 0x0e, + DW_CFA_def_cfa_expression = 0x0f, + DW_CFA_expression = 0x10, + DW_CFA_offset_extended_sf = 0x11, + DW_CFA_def_cfa_sf = 0x12, + DW_CFA_def_cfa_offset_sf = 0x13, + DW_CFA_val_offset = 0x14, + DW_CFA_val_offset_sf = 0x15, + DW_CFA_val_expression = 0x16, + + DW_CFA_lo_user = 0x1c, + DW_CFA_hi_user = 0x3f, + + DW_CFA_MIPS_advance_loc8 = 0x1d, + DW_CFA_GNU_window_save = 0x2d, + DW_CFA_GNU_args_size = 0x2e, + DW_CFA_GNU_negative_offset_extended = 0x2f, +}, +aliases { + DW_CFA_AARCH64_negate_ra_state = 0x2d, +}); + +dw!( +/// The child determination encodings for DIE attributes. +/// +/// See Section 7.5.3, Table 7.4. +DwChildren(u8) { + DW_CHILDREN_no = 0, + DW_CHILDREN_yes = 1, +}); + +dw!( +/// The tag encodings for DIE attributes. +/// +/// See Section 7.5.3, Table 7.3. +DwTag(u16) { + DW_TAG_null = 0x00, + + DW_TAG_array_type = 0x01, + DW_TAG_class_type = 0x02, + DW_TAG_entry_point = 0x03, + DW_TAG_enumeration_type = 0x04, + DW_TAG_formal_parameter = 0x05, + DW_TAG_imported_declaration = 0x08, + DW_TAG_label = 0x0a, + DW_TAG_lexical_block = 0x0b, + DW_TAG_member = 0x0d, + DW_TAG_pointer_type = 0x0f, + DW_TAG_reference_type = 0x10, + DW_TAG_compile_unit = 0x11, + DW_TAG_string_type = 0x12, + DW_TAG_structure_type = 0x13, + DW_TAG_subroutine_type = 0x15, + DW_TAG_typedef = 0x16, + DW_TAG_union_type = 0x17, + DW_TAG_unspecified_parameters = 0x18, + DW_TAG_variant = 0x19, + DW_TAG_common_block = 0x1a, + DW_TAG_common_inclusion = 0x1b, + DW_TAG_inheritance = 0x1c, + DW_TAG_inlined_subroutine = 0x1d, + DW_TAG_module = 0x1e, + DW_TAG_ptr_to_member_type = 0x1f, + DW_TAG_set_type = 0x20, + DW_TAG_subrange_type = 0x21, + DW_TAG_with_stmt = 0x22, + DW_TAG_access_declaration = 0x23, + DW_TAG_base_type = 0x24, + DW_TAG_catch_block = 0x25, + DW_TAG_const_type = 0x26, + DW_TAG_constant = 0x27, + DW_TAG_enumerator = 0x28, + DW_TAG_file_type = 0x29, + DW_TAG_friend = 0x2a, + DW_TAG_namelist = 0x2b, + DW_TAG_namelist_item = 0x2c, + DW_TAG_packed_type = 0x2d, + DW_TAG_subprogram = 0x2e, + DW_TAG_template_type_parameter = 0x2f, + DW_TAG_template_value_parameter = 0x30, + DW_TAG_thrown_type = 0x31, + DW_TAG_try_block = 0x32, + DW_TAG_variant_part = 0x33, + DW_TAG_variable = 0x34, + DW_TAG_volatile_type = 0x35, + +// DWARF 3. + DW_TAG_dwarf_procedure = 0x36, + DW_TAG_restrict_type = 0x37, + DW_TAG_interface_type = 0x38, + DW_TAG_namespace = 0x39, + DW_TAG_imported_module = 0x3a, + DW_TAG_unspecified_type = 0x3b, + DW_TAG_partial_unit = 0x3c, + DW_TAG_imported_unit = 0x3d, + DW_TAG_condition = 0x3f, + DW_TAG_shared_type = 0x40, + +// DWARF 4. + DW_TAG_type_unit = 0x41, + DW_TAG_rvalue_reference_type = 0x42, + DW_TAG_template_alias = 0x43, + +// DWARF 5. + DW_TAG_coarray_type = 0x44, + DW_TAG_generic_subrange = 0x45, + DW_TAG_dynamic_type = 0x46, + DW_TAG_atomic_type = 0x47, + DW_TAG_call_site = 0x48, + DW_TAG_call_site_parameter = 0x49, + DW_TAG_skeleton_unit = 0x4a, + DW_TAG_immutable_type = 0x4b, + + DW_TAG_lo_user = 0x4080, + DW_TAG_hi_user = 0xffff, + +// SGI/MIPS extensions. + DW_TAG_MIPS_loop = 0x4081, + +// HP extensions. + DW_TAG_HP_array_descriptor = 0x4090, + DW_TAG_HP_Bliss_field = 0x4091, + DW_TAG_HP_Bliss_field_set = 0x4092, + +// GNU extensions. + DW_TAG_format_label = 0x4101, + DW_TAG_function_template = 0x4102, + DW_TAG_class_template = 0x4103, + DW_TAG_GNU_BINCL = 0x4104, + DW_TAG_GNU_EINCL = 0x4105, + DW_TAG_GNU_template_template_param = 0x4106, + DW_TAG_GNU_template_parameter_pack = 0x4107, + DW_TAG_GNU_formal_parameter_pack = 0x4108, + DW_TAG_GNU_call_site = 0x4109, + DW_TAG_GNU_call_site_parameter = 0x410a, + + DW_TAG_APPLE_property = 0x4200, + +// SUN extensions. + DW_TAG_SUN_function_template = 0x4201, + DW_TAG_SUN_class_template = 0x4202, + DW_TAG_SUN_struct_template = 0x4203, + DW_TAG_SUN_union_template = 0x4204, + DW_TAG_SUN_indirect_inheritance = 0x4205, + DW_TAG_SUN_codeflags = 0x4206, + DW_TAG_SUN_memop_info = 0x4207, + DW_TAG_SUN_omp_child_func = 0x4208, + DW_TAG_SUN_rtti_descriptor = 0x4209, + DW_TAG_SUN_dtor_info = 0x420a, + DW_TAG_SUN_dtor = 0x420b, + DW_TAG_SUN_f90_interface = 0x420c, + DW_TAG_SUN_fortran_vax_structure = 0x420d, + +// ALTIUM extensions. + DW_TAG_ALTIUM_circ_type = 0x5101, + DW_TAG_ALTIUM_mwa_circ_type = 0x5102, + DW_TAG_ALTIUM_rev_carry_type = 0x5103, + DW_TAG_ALTIUM_rom = 0x5111, + +// Extensions for UPC. + DW_TAG_upc_shared_type = 0x8765, + DW_TAG_upc_strict_type = 0x8766, + DW_TAG_upc_relaxed_type = 0x8767, + +// PGI (STMicroelectronics) extensions. + DW_TAG_PGI_kanji_type = 0xa000, + DW_TAG_PGI_interface_block = 0xa020, + +// Borland extensions. + DW_TAG_BORLAND_property = 0xb000, + DW_TAG_BORLAND_Delphi_string = 0xb001, + DW_TAG_BORLAND_Delphi_dynamic_array = 0xb002, + DW_TAG_BORLAND_Delphi_set = 0xb003, + DW_TAG_BORLAND_Delphi_variant = 0xb004, +}); + +dw!( +/// The attribute encodings for DIE attributes. +/// +/// See Section 7.5.4, Table 7.5. +DwAt(u16) { + DW_AT_null = 0x00, + + DW_AT_sibling = 0x01, + DW_AT_location = 0x02, + DW_AT_name = 0x03, + DW_AT_ordering = 0x09, + DW_AT_byte_size = 0x0b, + DW_AT_bit_offset = 0x0c, + DW_AT_bit_size = 0x0d, + DW_AT_stmt_list = 0x10, + DW_AT_low_pc = 0x11, + DW_AT_high_pc = 0x12, + DW_AT_language = 0x13, + DW_AT_discr = 0x15, + DW_AT_discr_value = 0x16, + DW_AT_visibility = 0x17, + DW_AT_import = 0x18, + DW_AT_string_length = 0x19, + DW_AT_common_reference = 0x1a, + DW_AT_comp_dir = 0x1b, + DW_AT_const_value = 0x1c, + DW_AT_containing_type = 0x1d, + DW_AT_default_value = 0x1e, + DW_AT_inline = 0x20, + DW_AT_is_optional = 0x21, + DW_AT_lower_bound = 0x22, + DW_AT_producer = 0x25, + DW_AT_prototyped = 0x27, + DW_AT_return_addr = 0x2a, + DW_AT_start_scope = 0x2c, + DW_AT_bit_stride = 0x2e, + DW_AT_upper_bound = 0x2f, + DW_AT_abstract_origin = 0x31, + DW_AT_accessibility = 0x32, + DW_AT_address_class = 0x33, + DW_AT_artificial = 0x34, + DW_AT_base_types = 0x35, + DW_AT_calling_convention = 0x36, + DW_AT_count = 0x37, + DW_AT_data_member_location = 0x38, + DW_AT_decl_column = 0x39, + DW_AT_decl_file = 0x3a, + DW_AT_decl_line = 0x3b, + DW_AT_declaration = 0x3c, + DW_AT_discr_list = 0x3d, + DW_AT_encoding = 0x3e, + DW_AT_external = 0x3f, + DW_AT_frame_base = 0x40, + DW_AT_friend = 0x41, + DW_AT_identifier_case = 0x42, + DW_AT_macro_info = 0x43, + DW_AT_namelist_item = 0x44, + DW_AT_priority = 0x45, + DW_AT_segment = 0x46, + DW_AT_specification = 0x47, + DW_AT_static_link = 0x48, + DW_AT_type = 0x49, + DW_AT_use_location = 0x4a, + DW_AT_variable_parameter = 0x4b, + DW_AT_virtuality = 0x4c, + DW_AT_vtable_elem_location = 0x4d, + +// DWARF 3. + DW_AT_allocated = 0x4e, + DW_AT_associated = 0x4f, + DW_AT_data_location = 0x50, + DW_AT_byte_stride = 0x51, + DW_AT_entry_pc = 0x52, + DW_AT_use_UTF8 = 0x53, + DW_AT_extension = 0x54, + DW_AT_ranges = 0x55, + DW_AT_trampoline = 0x56, + DW_AT_call_column = 0x57, + DW_AT_call_file = 0x58, + DW_AT_call_line = 0x59, + DW_AT_description = 0x5a, + DW_AT_binary_scale = 0x5b, + DW_AT_decimal_scale = 0x5c, + DW_AT_small = 0x5d, + DW_AT_decimal_sign = 0x5e, + DW_AT_digit_count = 0x5f, + DW_AT_picture_string = 0x60, + DW_AT_mutable = 0x61, + DW_AT_threads_scaled = 0x62, + DW_AT_explicit = 0x63, + DW_AT_object_pointer = 0x64, + DW_AT_endianity = 0x65, + DW_AT_elemental = 0x66, + DW_AT_pure = 0x67, + DW_AT_recursive = 0x68, + +// DWARF 4. + DW_AT_signature = 0x69, + DW_AT_main_subprogram = 0x6a, + DW_AT_data_bit_offset = 0x6b, + DW_AT_const_expr = 0x6c, + DW_AT_enum_class = 0x6d, + DW_AT_linkage_name = 0x6e, + +// DWARF 5. + DW_AT_string_length_bit_size = 0x6f, + DW_AT_string_length_byte_size = 0x70, + DW_AT_rank = 0x71, + DW_AT_str_offsets_base = 0x72, + DW_AT_addr_base = 0x73, + DW_AT_rnglists_base = 0x74, + DW_AT_dwo_name = 0x76, + DW_AT_reference = 0x77, + DW_AT_rvalue_reference = 0x78, + DW_AT_macros = 0x79, + DW_AT_call_all_calls = 0x7a, + DW_AT_call_all_source_calls = 0x7b, + DW_AT_call_all_tail_calls = 0x7c, + DW_AT_call_return_pc = 0x7d, + DW_AT_call_value = 0x7e, + DW_AT_call_origin = 0x7f, + DW_AT_call_parameter = 0x80, + DW_AT_call_pc = 0x81, + DW_AT_call_tail_call = 0x82, + DW_AT_call_target = 0x83, + DW_AT_call_target_clobbered = 0x84, + DW_AT_call_data_location = 0x85, + DW_AT_call_data_value = 0x86, + DW_AT_noreturn = 0x87, + DW_AT_alignment = 0x88, + DW_AT_export_symbols = 0x89, + DW_AT_deleted = 0x8a, + DW_AT_defaulted = 0x8b, + DW_AT_loclists_base = 0x8c, + + DW_AT_lo_user = 0x2000, + DW_AT_hi_user = 0x3fff, + +// SGI/MIPS extensions. + DW_AT_MIPS_fde = 0x2001, + DW_AT_MIPS_loop_begin = 0x2002, + DW_AT_MIPS_tail_loop_begin = 0x2003, + DW_AT_MIPS_epilog_begin = 0x2004, + DW_AT_MIPS_loop_unroll_factor = 0x2005, + DW_AT_MIPS_software_pipeline_depth = 0x2006, + DW_AT_MIPS_linkage_name = 0x2007, + DW_AT_MIPS_stride = 0x2008, + DW_AT_MIPS_abstract_name = 0x2009, + DW_AT_MIPS_clone_origin = 0x200a, + DW_AT_MIPS_has_inlines = 0x200b, + DW_AT_MIPS_stride_byte = 0x200c, + DW_AT_MIPS_stride_elem = 0x200d, + DW_AT_MIPS_ptr_dopetype = 0x200e, + DW_AT_MIPS_allocatable_dopetype = 0x200f, + DW_AT_MIPS_assumed_shape_dopetype = 0x2010, + +// This one appears to have only been implemented by Open64 for +// fortran and may conflict with other extensions. + DW_AT_MIPS_assumed_size = 0x2011, + +// TODO: HP/CPQ extensions. +// These conflict with the MIPS extensions. + + DW_AT_INTEL_other_endian = 0x2026, + +// GNU extensions + DW_AT_sf_names = 0x2101, + DW_AT_src_info = 0x2102, + DW_AT_mac_info = 0x2103, + DW_AT_src_coords = 0x2104, + DW_AT_body_begin = 0x2105, + DW_AT_body_end = 0x2106, + DW_AT_GNU_vector = 0x2107, + DW_AT_GNU_guarded_by = 0x2108, + DW_AT_GNU_pt_guarded_by = 0x2109, + DW_AT_GNU_guarded = 0x210a, + DW_AT_GNU_pt_guarded = 0x210b, + DW_AT_GNU_locks_excluded = 0x210c, + DW_AT_GNU_exclusive_locks_required = 0x210d, + DW_AT_GNU_shared_locks_required = 0x210e, + DW_AT_GNU_odr_signature = 0x210f, + DW_AT_GNU_template_name = 0x2110, + DW_AT_GNU_call_site_value = 0x2111, + DW_AT_GNU_call_site_data_value = 0x2112, + DW_AT_GNU_call_site_target = 0x2113, + DW_AT_GNU_call_site_target_clobbered = 0x2114, + DW_AT_GNU_tail_call = 0x2115, + DW_AT_GNU_all_tail_call_sites = 0x2116, + DW_AT_GNU_all_call_sites = 0x2117, + DW_AT_GNU_all_source_call_sites = 0x2118, + DW_AT_GNU_macros = 0x2119, + DW_AT_GNU_deleted = 0x211a, + +// Extensions for Fission proposal. + DW_AT_GNU_dwo_name = 0x2130, + DW_AT_GNU_dwo_id = 0x2131, + DW_AT_GNU_ranges_base = 0x2132, + DW_AT_GNU_addr_base = 0x2133, + DW_AT_GNU_pubnames = 0x2134, + DW_AT_GNU_pubtypes = 0x2135, + DW_AT_GNU_discriminator = 0x2136, + DW_AT_GNU_locviews = 0x2137, + DW_AT_GNU_entry_view = 0x2138, + +// Conflict with Sun. +// DW_AT_VMS_rtnbeg_pd_address = 0x2201, + +// Sun extensions. + DW_AT_SUN_template = 0x2201, + DW_AT_SUN_alignment = 0x2202, + DW_AT_SUN_vtable = 0x2203, + DW_AT_SUN_count_guarantee = 0x2204, + DW_AT_SUN_command_line = 0x2205, + DW_AT_SUN_vbase = 0x2206, + DW_AT_SUN_compile_options = 0x2207, + DW_AT_SUN_language = 0x2208, + DW_AT_SUN_browser_file = 0x2209, + DW_AT_SUN_vtable_abi = 0x2210, + DW_AT_SUN_func_offsets = 0x2211, + DW_AT_SUN_cf_kind = 0x2212, + DW_AT_SUN_vtable_index = 0x2213, + DW_AT_SUN_omp_tpriv_addr = 0x2214, + DW_AT_SUN_omp_child_func = 0x2215, + DW_AT_SUN_func_offset = 0x2216, + DW_AT_SUN_memop_type_ref = 0x2217, + DW_AT_SUN_profile_id = 0x2218, + DW_AT_SUN_memop_signature = 0x2219, + DW_AT_SUN_obj_dir = 0x2220, + DW_AT_SUN_obj_file = 0x2221, + DW_AT_SUN_original_name = 0x2222, + DW_AT_SUN_hwcprof_signature = 0x2223, + DW_AT_SUN_amd64_parmdump = 0x2224, + DW_AT_SUN_part_link_name = 0x2225, + DW_AT_SUN_link_name = 0x2226, + DW_AT_SUN_pass_with_const = 0x2227, + DW_AT_SUN_return_with_const = 0x2228, + DW_AT_SUN_import_by_name = 0x2229, + DW_AT_SUN_f90_pointer = 0x222a, + DW_AT_SUN_pass_by_ref = 0x222b, + DW_AT_SUN_f90_allocatable = 0x222c, + DW_AT_SUN_f90_assumed_shape_array = 0x222d, + DW_AT_SUN_c_vla = 0x222e, + DW_AT_SUN_return_value_ptr = 0x2230, + DW_AT_SUN_dtor_start = 0x2231, + DW_AT_SUN_dtor_length = 0x2232, + DW_AT_SUN_dtor_state_initial = 0x2233, + DW_AT_SUN_dtor_state_final = 0x2234, + DW_AT_SUN_dtor_state_deltas = 0x2235, + DW_AT_SUN_import_by_lname = 0x2236, + DW_AT_SUN_f90_use_only = 0x2237, + DW_AT_SUN_namelist_spec = 0x2238, + DW_AT_SUN_is_omp_child_func = 0x2239, + DW_AT_SUN_fortran_main_alias = 0x223a, + DW_AT_SUN_fortran_based = 0x223b, + + DW_AT_ALTIUM_loclist = 0x2300, + + DW_AT_use_GNAT_descriptive_type = 0x2301, + DW_AT_GNAT_descriptive_type = 0x2302, + DW_AT_GNU_numerator = 0x2303, + DW_AT_GNU_denominator = 0x2304, + DW_AT_GNU_bias = 0x2305, + + DW_AT_upc_threads_scaled = 0x3210, + +// PGI (STMicroelectronics) extensions. + DW_AT_PGI_lbase = 0x3a00, + DW_AT_PGI_soffset = 0x3a01, + DW_AT_PGI_lstride = 0x3a02, + +// Borland extensions. + DW_AT_BORLAND_property_read = 0x3b11, + DW_AT_BORLAND_property_write = 0x3b12, + DW_AT_BORLAND_property_implements = 0x3b13, + DW_AT_BORLAND_property_index = 0x3b14, + DW_AT_BORLAND_property_default = 0x3b15, + DW_AT_BORLAND_Delphi_unit = 0x3b20, + DW_AT_BORLAND_Delphi_class = 0x3b21, + DW_AT_BORLAND_Delphi_record = 0x3b22, + DW_AT_BORLAND_Delphi_metaclass = 0x3b23, + DW_AT_BORLAND_Delphi_constructor = 0x3b24, + DW_AT_BORLAND_Delphi_destructor = 0x3b25, + DW_AT_BORLAND_Delphi_anonymous_method = 0x3b26, + DW_AT_BORLAND_Delphi_interface = 0x3b27, + DW_AT_BORLAND_Delphi_ABI = 0x3b28, + DW_AT_BORLAND_Delphi_return = 0x3b29, + DW_AT_BORLAND_Delphi_frameptr = 0x3b30, + DW_AT_BORLAND_closure = 0x3b31, + +// LLVM project extensions. + DW_AT_LLVM_include_path = 0x3e00, + DW_AT_LLVM_config_macros = 0x3e01, + DW_AT_LLVM_isysroot = 0x3e02, + +// Apple extensions. + DW_AT_APPLE_optimized = 0x3fe1, + DW_AT_APPLE_flags = 0x3fe2, + DW_AT_APPLE_isa = 0x3fe3, + DW_AT_APPLE_block = 0x3fe4, + DW_AT_APPLE_major_runtime_vers = 0x3fe5, + DW_AT_APPLE_runtime_class = 0x3fe6, + DW_AT_APPLE_omit_frame_ptr = 0x3fe7, + DW_AT_APPLE_property_name = 0x3fe8, + DW_AT_APPLE_property_getter = 0x3fe9, + DW_AT_APPLE_property_setter = 0x3fea, + DW_AT_APPLE_property_attribute = 0x3feb, + DW_AT_APPLE_objc_complete_type = 0x3fec, + DW_AT_APPLE_property = 0x3fed +}); + +dw!( +/// The attribute form encodings for DIE attributes. +/// +/// See Section 7.5.6, Table 7.6. +DwForm(u16) { + DW_FORM_null = 0x00, + + DW_FORM_addr = 0x01, + DW_FORM_block2 = 0x03, + DW_FORM_block4 = 0x04, + DW_FORM_data2 = 0x05, + DW_FORM_data4 = 0x06, + DW_FORM_data8 = 0x07, + DW_FORM_string = 0x08, + DW_FORM_block = 0x09, + DW_FORM_block1 = 0x0a, + DW_FORM_data1 = 0x0b, + DW_FORM_flag = 0x0c, + DW_FORM_sdata = 0x0d, + DW_FORM_strp = 0x0e, + DW_FORM_udata = 0x0f, + DW_FORM_ref_addr = 0x10, + DW_FORM_ref1 = 0x11, + DW_FORM_ref2 = 0x12, + DW_FORM_ref4 = 0x13, + DW_FORM_ref8 = 0x14, + DW_FORM_ref_udata = 0x15, + DW_FORM_indirect = 0x16, + +// DWARF 4. + DW_FORM_sec_offset = 0x17, + DW_FORM_exprloc = 0x18, + DW_FORM_flag_present = 0x19, + DW_FORM_ref_sig8 = 0x20, + +// DWARF 5. + DW_FORM_strx = 0x1a, + DW_FORM_addrx = 0x1b, + DW_FORM_ref_sup4 = 0x1c, + DW_FORM_strp_sup = 0x1d, + DW_FORM_data16 = 0x1e, + DW_FORM_line_strp = 0x1f, + DW_FORM_implicit_const = 0x21, + DW_FORM_loclistx = 0x22, + DW_FORM_rnglistx = 0x23, + DW_FORM_ref_sup8 = 0x24, + DW_FORM_strx1 = 0x25, + DW_FORM_strx2 = 0x26, + DW_FORM_strx3 = 0x27, + DW_FORM_strx4 = 0x28, + DW_FORM_addrx1 = 0x29, + DW_FORM_addrx2 = 0x2a, + DW_FORM_addrx3 = 0x2b, + DW_FORM_addrx4 = 0x2c, + +// Extensions for Fission proposal + DW_FORM_GNU_addr_index = 0x1f01, + DW_FORM_GNU_str_index = 0x1f02, + +// Alternate debug sections proposal (output of "dwz" tool). + DW_FORM_GNU_ref_alt = 0x1f20, + DW_FORM_GNU_strp_alt = 0x1f21 +}); + +dw!( +/// The encodings of the constants used in the `DW_AT_encoding` attribute. +/// +/// See Section 7.8, Table 7.11. +DwAte(u8) { + DW_ATE_address = 0x01, + DW_ATE_boolean = 0x02, + DW_ATE_complex_float = 0x03, + DW_ATE_float = 0x04, + DW_ATE_signed = 0x05, + DW_ATE_signed_char = 0x06, + DW_ATE_unsigned = 0x07, + DW_ATE_unsigned_char = 0x08, + +// DWARF 3. + DW_ATE_imaginary_float = 0x09, + DW_ATE_packed_decimal = 0x0a, + DW_ATE_numeric_string = 0x0b, + DW_ATE_edited = 0x0c, + DW_ATE_signed_fixed = 0x0d, + DW_ATE_unsigned_fixed = 0x0e, + DW_ATE_decimal_float = 0x0f , + +// DWARF 4. + DW_ATE_UTF = 0x10, + DW_ATE_UCS = 0x11, + DW_ATE_ASCII = 0x12, + + DW_ATE_lo_user = 0x80, + DW_ATE_hi_user = 0xff, +}); + +dw!( +/// The encodings of the constants used in location list entries. +/// +/// See Section 7.7.3, Table 7.10. +DwLle(u8) { + DW_LLE_end_of_list = 0x00, + DW_LLE_base_addressx = 0x01, + DW_LLE_startx_endx = 0x02, + DW_LLE_startx_length = 0x03, + DW_LLE_offset_pair = 0x04, + DW_LLE_default_location = 0x05, + DW_LLE_base_address = 0x06, + DW_LLE_start_end = 0x07, + DW_LLE_start_length = 0x08, + DW_LLE_GNU_view_pair = 0x09, +}); + +dw!( +/// The encodings of the constants used in the `DW_AT_decimal_sign` attribute. +/// +/// See Section 7.8, Table 7.12. +DwDs(u8) { + DW_DS_unsigned = 0x01, + DW_DS_leading_overpunch = 0x02, + DW_DS_trailing_overpunch = 0x03, + DW_DS_leading_separate = 0x04, + DW_DS_trailing_separate = 0x05, +}); + +dw!( +/// The encodings of the constants used in the `DW_AT_endianity` attribute. +/// +/// See Section 7.8, Table 7.13. +DwEnd(u8) { + DW_END_default = 0x00, + DW_END_big = 0x01, + DW_END_little = 0x02, + DW_END_lo_user = 0x40, + DW_END_hi_user = 0xff, +}); + +dw!( +/// The encodings of the constants used in the `DW_AT_accessibility` attribute. +/// +/// See Section 7.9, Table 7.14. +DwAccess(u8) { + DW_ACCESS_public = 0x01, + DW_ACCESS_protected = 0x02, + DW_ACCESS_private = 0x03, +}); + +dw!( +/// The encodings of the constants used in the `DW_AT_visibility` attribute. +/// +/// See Section 7.10, Table 7.15. +DwVis(u8) { + DW_VIS_local = 0x01, + DW_VIS_exported = 0x02, + DW_VIS_qualified = 0x03, +}); + +dw!( +/// The encodings of the constants used in the `DW_AT_virtuality` attribute. +/// +/// See Section 7.11, Table 7.16. +DwVirtuality(u8) { + DW_VIRTUALITY_none = 0x00, + DW_VIRTUALITY_virtual = 0x01, + DW_VIRTUALITY_pure_virtual = 0x02, +}); + +dw!( +/// The encodings of the constants used in the `DW_AT_language` attribute. +/// +/// See Section 7.12, Table 7.17. +DwLang(u16) { + DW_LANG_C89 = 0x0001, + DW_LANG_C = 0x0002, + DW_LANG_Ada83 = 0x0003, + DW_LANG_C_plus_plus = 0x0004, + DW_LANG_Cobol74 = 0x0005, + DW_LANG_Cobol85 = 0x0006, + DW_LANG_Fortran77 = 0x0007, + DW_LANG_Fortran90 = 0x0008, + DW_LANG_Pascal83 = 0x0009, + DW_LANG_Modula2 = 0x000a, + DW_LANG_Java = 0x000b, + DW_LANG_C99 = 0x000c, + DW_LANG_Ada95 = 0x000d, + DW_LANG_Fortran95 = 0x000e, + DW_LANG_PLI = 0x000f, + DW_LANG_ObjC = 0x0010, + DW_LANG_ObjC_plus_plus = 0x0011, + DW_LANG_UPC = 0x0012, + DW_LANG_D = 0x0013, + DW_LANG_Python = 0x0014, + DW_LANG_OpenCL = 0x0015, + DW_LANG_Go = 0x0016, + DW_LANG_Modula3 = 0x0017, + DW_LANG_Haskell = 0x0018, + DW_LANG_C_plus_plus_03 = 0x0019, + DW_LANG_C_plus_plus_11 = 0x001a, + DW_LANG_OCaml = 0x001b, + DW_LANG_Rust = 0x001c, + DW_LANG_C11 = 0x001d, + DW_LANG_Swift = 0x001e, + DW_LANG_Julia = 0x001f, + DW_LANG_Dylan = 0x0020, + DW_LANG_C_plus_plus_14 = 0x0021, + DW_LANG_Fortran03 = 0x0022, + DW_LANG_Fortran08 = 0x0023, + DW_LANG_RenderScript = 0x0024, + DW_LANG_BLISS = 0x0025, + DW_LANG_Kotlin = 0x0026, + DW_LANG_Zig = 0x0027, + DW_LANG_Crystal = 0x0028, + DW_LANG_C_plus_plus_17 = 0x002a, + DW_LANG_C_plus_plus_20 = 0x002b, + DW_LANG_C17 = 0x002c, + DW_LANG_Fortran18 = 0x002d, + DW_LANG_Ada2005 = 0x002e, + DW_LANG_Ada2012 = 0x002f, + + DW_LANG_lo_user = 0x8000, + DW_LANG_hi_user = 0xffff, + + DW_LANG_Mips_Assembler = 0x8001, + DW_LANG_GOOGLE_RenderScript = 0x8e57, + DW_LANG_SUN_Assembler = 0x9001, + DW_LANG_ALTIUM_Assembler = 0x9101, + DW_LANG_BORLAND_Delphi = 0xb000, +}); + +impl DwLang { + /// Get the default DW_AT_lower_bound for this language. + pub fn default_lower_bound(self) -> Option<usize> { + match self { + DW_LANG_C89 + | DW_LANG_C + | DW_LANG_C_plus_plus + | DW_LANG_Java + | DW_LANG_C99 + | DW_LANG_ObjC + | DW_LANG_ObjC_plus_plus + | DW_LANG_UPC + | DW_LANG_D + | DW_LANG_Python + | DW_LANG_OpenCL + | DW_LANG_Go + | DW_LANG_Haskell + | DW_LANG_C_plus_plus_03 + | DW_LANG_C_plus_plus_11 + | DW_LANG_OCaml + | DW_LANG_Rust + | DW_LANG_C11 + | DW_LANG_Swift + | DW_LANG_Dylan + | DW_LANG_C_plus_plus_14 + | DW_LANG_RenderScript + | DW_LANG_BLISS => Some(0), + DW_LANG_Ada83 | DW_LANG_Cobol74 | DW_LANG_Cobol85 | DW_LANG_Fortran77 + | DW_LANG_Fortran90 | DW_LANG_Pascal83 | DW_LANG_Modula2 | DW_LANG_Ada95 + | DW_LANG_Fortran95 | DW_LANG_PLI | DW_LANG_Modula3 | DW_LANG_Julia + | DW_LANG_Fortran03 | DW_LANG_Fortran08 => Some(1), + _ => None, + } + } +} + +dw!( +/// The encodings of the constants used in the `DW_AT_address_class` attribute. +/// +/// There is only one value that is common to all target architectures. +/// See Section 7.13. +DwAddr(u64) { + DW_ADDR_none = 0x00, +}); + +dw!( +/// The encodings of the constants used in the `DW_AT_identifier_case` attribute. +/// +/// See Section 7.14, Table 7.18. +DwId(u8) { + DW_ID_case_sensitive = 0x00, + DW_ID_up_case = 0x01, + DW_ID_down_case = 0x02, + DW_ID_case_insensitive = 0x03, +}); + +dw!( +/// The encodings of the constants used in the `DW_AT_calling_convention` attribute. +/// +/// See Section 7.15, Table 7.19. +DwCc(u8) { + DW_CC_normal = 0x01, + DW_CC_program = 0x02, + DW_CC_nocall = 0x03, + DW_CC_pass_by_reference = 0x04, + DW_CC_pass_by_value = 0x05, + DW_CC_lo_user = 0x40, + DW_CC_hi_user = 0xff, +}); + +dw!( +/// The encodings of the constants used in the `DW_AT_inline` attribute. +/// +/// See Section 7.16, Table 7.20. +DwInl(u8) { + DW_INL_not_inlined = 0x00, + DW_INL_inlined = 0x01, + DW_INL_declared_not_inlined = 0x02, + DW_INL_declared_inlined = 0x03, +}); + +dw!( +/// The encodings of the constants used in the `DW_AT_ordering` attribute. +/// +/// See Section 7.17, Table 7.17. +DwOrd(u8) { + DW_ORD_row_major = 0x00, + DW_ORD_col_major = 0x01, +}); + +dw!( +/// The encodings of the constants used in the `DW_AT_discr_list` attribute. +/// +/// See Section 7.18, Table 7.22. +DwDsc(u8) { + DW_DSC_label = 0x00, + DW_DSC_range = 0x01, +}); + +dw!( +/// Name index attribute encodings. +/// +/// See Section 7.19, Table 7.23. +DwIdx(u16) { + DW_IDX_compile_unit = 1, + DW_IDX_type_unit = 2, + DW_IDX_die_offset = 3, + DW_IDX_parent = 4, + DW_IDX_type_hash = 5, + DW_IDX_lo_user = 0x2000, + DW_IDX_hi_user = 0x3fff, +}); + +dw!( +/// The encodings of the constants used in the `DW_AT_defaulted` attribute. +/// +/// See Section 7.20, Table 7.24. +DwDefaulted(u8) { + DW_DEFAULTED_no = 0x00, + DW_DEFAULTED_in_class = 0x01, + DW_DEFAULTED_out_of_class = 0x02, +}); + +dw!( +/// The encodings for the standard opcodes for line number information. +/// +/// See Section 7.22, Table 7.25. +DwLns(u8) { + DW_LNS_copy = 0x01, + DW_LNS_advance_pc = 0x02, + DW_LNS_advance_line = 0x03, + DW_LNS_set_file = 0x04, + DW_LNS_set_column = 0x05, + DW_LNS_negate_stmt = 0x06, + DW_LNS_set_basic_block = 0x07, + DW_LNS_const_add_pc = 0x08, + DW_LNS_fixed_advance_pc = 0x09, + DW_LNS_set_prologue_end = 0x0a, + DW_LNS_set_epilogue_begin = 0x0b, + DW_LNS_set_isa = 0x0c, +}); + +dw!( +/// The encodings for the extended opcodes for line number information. +/// +/// See Section 7.22, Table 7.26. +DwLne(u8) { + DW_LNE_end_sequence = 0x01, + DW_LNE_set_address = 0x02, + DW_LNE_define_file = 0x03, + DW_LNE_set_discriminator = 0x04, + + DW_LNE_lo_user = 0x80, + DW_LNE_hi_user = 0xff, +}); + +dw!( +/// The encodings for the line number header entry formats. +/// +/// See Section 7.22, Table 7.27. +DwLnct(u16) { + DW_LNCT_path = 0x1, + DW_LNCT_directory_index = 0x2, + DW_LNCT_timestamp = 0x3, + DW_LNCT_size = 0x4, + DW_LNCT_MD5 = 0x5, + DW_LNCT_lo_user = 0x2000, + DW_LNCT_hi_user = 0x3fff, +}); + +dw!( +/// The encodings for macro information entry types. +/// +/// See Section 7.23, Table 7.28. +DwMacro(u8) { + DW_MACRO_define = 0x01, + DW_MACRO_undef = 0x02, + DW_MACRO_start_file = 0x03, + DW_MACRO_end_file = 0x04, + DW_MACRO_define_strp = 0x05, + DW_MACRO_undef_strp = 0x06, + DW_MACRO_import = 0x07, + DW_MACRO_define_sup = 0x08, + DW_MACRO_undef_sup = 0x09, + DW_MACRO_import_sup = 0x0a, + DW_MACRO_define_strx = 0x0b, + DW_MACRO_undef_strx = 0x0c, + DW_MACRO_lo_user = 0xe0, + DW_MACRO_hi_user = 0xff, +}); + +dw!( +/// Range list entry encoding values. +/// +/// See Section 7.25, Table 7.30. +DwRle(u8) { + DW_RLE_end_of_list = 0x00, + DW_RLE_base_addressx = 0x01, + DW_RLE_startx_endx = 0x02, + DW_RLE_startx_length = 0x03, + DW_RLE_offset_pair = 0x04, + DW_RLE_base_address = 0x05, + DW_RLE_start_end = 0x06, + DW_RLE_start_length = 0x07, +}); + +dw!( +/// The encodings for DWARF expression operations. +/// +/// See Section 7.7.1, Table 7.9. +DwOp(u8) { + DW_OP_addr = 0x03, + DW_OP_deref = 0x06, + DW_OP_const1u = 0x08, + DW_OP_const1s = 0x09, + DW_OP_const2u = 0x0a, + DW_OP_const2s = 0x0b, + DW_OP_const4u = 0x0c, + DW_OP_const4s = 0x0d, + DW_OP_const8u = 0x0e, + DW_OP_const8s = 0x0f, + DW_OP_constu = 0x10, + DW_OP_consts = 0x11, + DW_OP_dup = 0x12, + DW_OP_drop = 0x13, + DW_OP_over = 0x14, + DW_OP_pick = 0x15, + DW_OP_swap = 0x16, + DW_OP_rot = 0x17, + DW_OP_xderef = 0x18, + DW_OP_abs = 0x19, + DW_OP_and = 0x1a, + DW_OP_div = 0x1b, + DW_OP_minus = 0x1c, + DW_OP_mod = 0x1d, + DW_OP_mul = 0x1e, + DW_OP_neg = 0x1f, + DW_OP_not = 0x20, + DW_OP_or = 0x21, + DW_OP_plus = 0x22, + DW_OP_plus_uconst = 0x23, + DW_OP_shl = 0x24, + DW_OP_shr = 0x25, + DW_OP_shra = 0x26, + DW_OP_xor = 0x27, + DW_OP_bra = 0x28, + DW_OP_eq = 0x29, + DW_OP_ge = 0x2a, + DW_OP_gt = 0x2b, + DW_OP_le = 0x2c, + DW_OP_lt = 0x2d, + DW_OP_ne = 0x2e, + DW_OP_skip = 0x2f, + DW_OP_lit0 = 0x30, + DW_OP_lit1 = 0x31, + DW_OP_lit2 = 0x32, + DW_OP_lit3 = 0x33, + DW_OP_lit4 = 0x34, + DW_OP_lit5 = 0x35, + DW_OP_lit6 = 0x36, + DW_OP_lit7 = 0x37, + DW_OP_lit8 = 0x38, + DW_OP_lit9 = 0x39, + DW_OP_lit10 = 0x3a, + DW_OP_lit11 = 0x3b, + DW_OP_lit12 = 0x3c, + DW_OP_lit13 = 0x3d, + DW_OP_lit14 = 0x3e, + DW_OP_lit15 = 0x3f, + DW_OP_lit16 = 0x40, + DW_OP_lit17 = 0x41, + DW_OP_lit18 = 0x42, + DW_OP_lit19 = 0x43, + DW_OP_lit20 = 0x44, + DW_OP_lit21 = 0x45, + DW_OP_lit22 = 0x46, + DW_OP_lit23 = 0x47, + DW_OP_lit24 = 0x48, + DW_OP_lit25 = 0x49, + DW_OP_lit26 = 0x4a, + DW_OP_lit27 = 0x4b, + DW_OP_lit28 = 0x4c, + DW_OP_lit29 = 0x4d, + DW_OP_lit30 = 0x4e, + DW_OP_lit31 = 0x4f, + DW_OP_reg0 = 0x50, + DW_OP_reg1 = 0x51, + DW_OP_reg2 = 0x52, + DW_OP_reg3 = 0x53, + DW_OP_reg4 = 0x54, + DW_OP_reg5 = 0x55, + DW_OP_reg6 = 0x56, + DW_OP_reg7 = 0x57, + DW_OP_reg8 = 0x58, + DW_OP_reg9 = 0x59, + DW_OP_reg10 = 0x5a, + DW_OP_reg11 = 0x5b, + DW_OP_reg12 = 0x5c, + DW_OP_reg13 = 0x5d, + DW_OP_reg14 = 0x5e, + DW_OP_reg15 = 0x5f, + DW_OP_reg16 = 0x60, + DW_OP_reg17 = 0x61, + DW_OP_reg18 = 0x62, + DW_OP_reg19 = 0x63, + DW_OP_reg20 = 0x64, + DW_OP_reg21 = 0x65, + DW_OP_reg22 = 0x66, + DW_OP_reg23 = 0x67, + DW_OP_reg24 = 0x68, + DW_OP_reg25 = 0x69, + DW_OP_reg26 = 0x6a, + DW_OP_reg27 = 0x6b, + DW_OP_reg28 = 0x6c, + DW_OP_reg29 = 0x6d, + DW_OP_reg30 = 0x6e, + DW_OP_reg31 = 0x6f, + DW_OP_breg0 = 0x70, + DW_OP_breg1 = 0x71, + DW_OP_breg2 = 0x72, + DW_OP_breg3 = 0x73, + DW_OP_breg4 = 0x74, + DW_OP_breg5 = 0x75, + DW_OP_breg6 = 0x76, + DW_OP_breg7 = 0x77, + DW_OP_breg8 = 0x78, + DW_OP_breg9 = 0x79, + DW_OP_breg10 = 0x7a, + DW_OP_breg11 = 0x7b, + DW_OP_breg12 = 0x7c, + DW_OP_breg13 = 0x7d, + DW_OP_breg14 = 0x7e, + DW_OP_breg15 = 0x7f, + DW_OP_breg16 = 0x80, + DW_OP_breg17 = 0x81, + DW_OP_breg18 = 0x82, + DW_OP_breg19 = 0x83, + DW_OP_breg20 = 0x84, + DW_OP_breg21 = 0x85, + DW_OP_breg22 = 0x86, + DW_OP_breg23 = 0x87, + DW_OP_breg24 = 0x88, + DW_OP_breg25 = 0x89, + DW_OP_breg26 = 0x8a, + DW_OP_breg27 = 0x8b, + DW_OP_breg28 = 0x8c, + DW_OP_breg29 = 0x8d, + DW_OP_breg30 = 0x8e, + DW_OP_breg31 = 0x8f, + DW_OP_regx = 0x90, + DW_OP_fbreg = 0x91, + DW_OP_bregx = 0x92, + DW_OP_piece = 0x93, + DW_OP_deref_size = 0x94, + DW_OP_xderef_size = 0x95, + DW_OP_nop = 0x96, + DW_OP_push_object_address = 0x97, + DW_OP_call2 = 0x98, + DW_OP_call4 = 0x99, + DW_OP_call_ref = 0x9a, + DW_OP_form_tls_address = 0x9b, + DW_OP_call_frame_cfa = 0x9c, + DW_OP_bit_piece = 0x9d, + DW_OP_implicit_value = 0x9e, + DW_OP_stack_value = 0x9f, + DW_OP_implicit_pointer = 0xa0, + DW_OP_addrx = 0xa1, + DW_OP_constx = 0xa2, + DW_OP_entry_value = 0xa3, + DW_OP_const_type = 0xa4, + DW_OP_regval_type = 0xa5, + DW_OP_deref_type = 0xa6, + DW_OP_xderef_type = 0xa7, + DW_OP_convert = 0xa8, + DW_OP_reinterpret = 0xa9, + + // GNU extensions + DW_OP_GNU_push_tls_address = 0xe0, + DW_OP_GNU_implicit_pointer = 0xf2, + DW_OP_GNU_entry_value = 0xf3, + DW_OP_GNU_const_type = 0xf4, + DW_OP_GNU_regval_type = 0xf5, + DW_OP_GNU_deref_type = 0xf6, + DW_OP_GNU_convert = 0xf7, + DW_OP_GNU_reinterpret = 0xf9, + DW_OP_GNU_parameter_ref = 0xfa, + DW_OP_GNU_addr_index = 0xfb, + DW_OP_GNU_const_index = 0xfc, + + // Wasm extensions + DW_OP_WASM_location = 0xed, +}); + +dw!( +/// Pointer encoding used by `.eh_frame`. +/// +/// The four lower bits describe the +/// format of the pointer, the upper four bits describe how the encoding should +/// be applied. +/// +/// Defined in `<https://refspecs.linuxfoundation.org/LSB_4.0.0/LSB-Core-generic/LSB-Core-generic/dwarfext.html>` +DwEhPe(u8) { +// Format of pointer encoding. + +// "Unsigned value is encoded using the Little Endian Base 128" + DW_EH_PE_uleb128 = 0x1, +// "A 2 bytes unsigned value." + DW_EH_PE_udata2 = 0x2, +// "A 4 bytes unsigned value." + DW_EH_PE_udata4 = 0x3, +// "An 8 bytes unsigned value." + DW_EH_PE_udata8 = 0x4, +// "Signed value is encoded using the Little Endian Base 128" + DW_EH_PE_sleb128 = 0x9, +// "A 2 bytes signed value." + DW_EH_PE_sdata2 = 0x0a, +// "A 4 bytes signed value." + DW_EH_PE_sdata4 = 0x0b, +// "An 8 bytes signed value." + DW_EH_PE_sdata8 = 0x0c, + +// How the pointer encoding should be applied. + +// `DW_EH_PE_pcrel` pointers are relative to their own location. + DW_EH_PE_pcrel = 0x10, +// "Value is relative to the beginning of the .text section." + DW_EH_PE_textrel = 0x20, +// "Value is relative to the beginning of the .got or .eh_frame_hdr section." + DW_EH_PE_datarel = 0x30, +// "Value is relative to the beginning of the function." + DW_EH_PE_funcrel = 0x40, +// "Value is aligned to an address unit sized boundary." + DW_EH_PE_aligned = 0x50, + +// This bit can be set for any of the above encoding applications. When set, +// the encoded value is the address of the real pointer result, not the +// pointer result itself. +// +// This isn't defined in the DWARF or the `.eh_frame` standards, but is +// generated by both GNU/Linux and macOS tooling. + DW_EH_PE_indirect = 0x80, + +// These constants apply to both the lower and upper bits. + +// "The Value is a literal pointer whose size is determined by the +// architecture." + DW_EH_PE_absptr = 0x0, +// The absence of a pointer and encoding. + DW_EH_PE_omit = 0xff, +}); + +const DW_EH_PE_FORMAT_MASK: u8 = 0b0000_1111; + +// Ignores indirection bit. +const DW_EH_PE_APPLICATION_MASK: u8 = 0b0111_0000; + +impl DwEhPe { + /// Get the pointer encoding's format. + #[inline] + pub fn format(self) -> DwEhPe { + DwEhPe(self.0 & DW_EH_PE_FORMAT_MASK) + } + + /// Get the pointer encoding's application. + #[inline] + pub fn application(self) -> DwEhPe { + DwEhPe(self.0 & DW_EH_PE_APPLICATION_MASK) + } + + /// Is this encoding the absent pointer encoding? + #[inline] + pub fn is_absent(self) -> bool { + self == DW_EH_PE_omit + } + + /// Is this coding indirect? If so, its encoded value is the address of the + /// real pointer result, not the pointer result itself. + #[inline] + pub fn is_indirect(self) -> bool { + self.0 & DW_EH_PE_indirect.0 != 0 + } + + /// Is this a known, valid pointer encoding? + pub fn is_valid_encoding(self) -> bool { + if self.is_absent() { + return true; + } + + match self.format() { + DW_EH_PE_absptr | DW_EH_PE_uleb128 | DW_EH_PE_udata2 | DW_EH_PE_udata4 + | DW_EH_PE_udata8 | DW_EH_PE_sleb128 | DW_EH_PE_sdata2 | DW_EH_PE_sdata4 + | DW_EH_PE_sdata8 => {} + _ => return false, + } + + match self.application() { + DW_EH_PE_absptr | DW_EH_PE_pcrel | DW_EH_PE_textrel | DW_EH_PE_datarel + | DW_EH_PE_funcrel | DW_EH_PE_aligned => {} + _ => return false, + } + + true + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_dw_eh_pe_format() { + let encoding = DwEhPe(DW_EH_PE_pcrel.0 | DW_EH_PE_uleb128.0); + assert_eq!(encoding.format(), DW_EH_PE_uleb128); + } + + #[test] + fn test_dw_eh_pe_application() { + let encoding = DwEhPe(DW_EH_PE_pcrel.0 | DW_EH_PE_uleb128.0); + assert_eq!(encoding.application(), DW_EH_PE_pcrel); + } + + #[test] + fn test_dw_eh_pe_is_absent() { + assert_eq!(DW_EH_PE_absptr.is_absent(), false); + assert_eq!(DW_EH_PE_omit.is_absent(), true); + } + + #[test] + fn test_dw_eh_pe_is_valid_encoding_ok() { + let encoding = DwEhPe(DW_EH_PE_uleb128.0 | DW_EH_PE_pcrel.0); + assert!(encoding.is_valid_encoding()); + assert!(DW_EH_PE_absptr.is_valid_encoding()); + assert!(DW_EH_PE_omit.is_valid_encoding()); + } + + #[test] + fn test_dw_eh_pe_is_valid_encoding_bad_format() { + let encoding = DwEhPe((DW_EH_PE_sdata8.0 + 1) | DW_EH_PE_pcrel.0); + assert_eq!(encoding.is_valid_encoding(), false); + } + + #[test] + fn test_dw_eh_pe_is_valid_encoding_bad_application() { + let encoding = DwEhPe(DW_EH_PE_sdata8.0 | (DW_EH_PE_aligned.0 + 1)); + assert_eq!(encoding.is_valid_encoding(), false); + } +} diff --git a/vendor/gimli/src/endianity.rs b/vendor/gimli/src/endianity.rs new file mode 100644 index 0000000..3201551 --- /dev/null +++ b/vendor/gimli/src/endianity.rs @@ -0,0 +1,256 @@ +//! Types for compile-time and run-time endianity. + +use core::convert::TryInto; +use core::fmt::Debug; + +/// A trait describing the endianity of some buffer. +pub trait Endianity: Debug + Default + Clone + Copy + PartialEq + Eq { + /// Return true for big endian byte order. + fn is_big_endian(self) -> bool; + + /// Return true for little endian byte order. + #[inline] + fn is_little_endian(self) -> bool { + !self.is_big_endian() + } + + /// Reads an unsigned 16 bit integer from `buf`. + /// + /// # Panics + /// + /// Panics when `buf.len() < 2`. + #[inline] + fn read_u16(self, buf: &[u8]) -> u16 { + let bytes: &[u8; 2] = buf[..2].try_into().unwrap(); + if self.is_big_endian() { + u16::from_be_bytes(*bytes) + } else { + u16::from_le_bytes(*bytes) + } + } + + /// Reads an unsigned 32 bit integer from `buf`. + /// + /// # Panics + /// + /// Panics when `buf.len() < 4`. + #[inline] + fn read_u32(self, buf: &[u8]) -> u32 { + let bytes: &[u8; 4] = buf[..4].try_into().unwrap(); + if self.is_big_endian() { + u32::from_be_bytes(*bytes) + } else { + u32::from_le_bytes(*bytes) + } + } + + /// Reads an unsigned 64 bit integer from `buf`. + /// + /// # Panics + /// + /// Panics when `buf.len() < 8`. + #[inline] + fn read_u64(self, buf: &[u8]) -> u64 { + let bytes: &[u8; 8] = buf[..8].try_into().unwrap(); + if self.is_big_endian() { + u64::from_be_bytes(*bytes) + } else { + u64::from_le_bytes(*bytes) + } + } + + /// Read an unsigned n-bytes integer u64. + /// + /// # Panics + /// + /// Panics when `buf.len() < 1` or `buf.len() > 8`. + #[inline] + fn read_uint(&mut self, buf: &[u8]) -> u64 { + let mut tmp = [0; 8]; + if self.is_big_endian() { + tmp[8 - buf.len()..].copy_from_slice(buf); + } else { + tmp[..buf.len()].copy_from_slice(buf); + } + self.read_u64(&tmp) + } + + /// Reads a signed 16 bit integer from `buf`. + /// + /// # Panics + /// + /// Panics when `buf.len() < 2`. + #[inline] + fn read_i16(self, buf: &[u8]) -> i16 { + self.read_u16(buf) as i16 + } + + /// Reads a signed 32 bit integer from `buf`. + /// + /// # Panics + /// + /// Panics when `buf.len() < 4`. + #[inline] + fn read_i32(self, buf: &[u8]) -> i32 { + self.read_u32(buf) as i32 + } + + /// Reads a signed 64 bit integer from `buf`. + /// + /// # Panics + /// + /// Panics when `buf.len() < 8`. + #[inline] + fn read_i64(self, buf: &[u8]) -> i64 { + self.read_u64(buf) as i64 + } + + /// Reads a 32 bit floating point number from `buf`. + /// + /// # Panics + /// + /// Panics when `buf.len() < 8`. + #[inline] + fn read_f32(self, buf: &[u8]) -> f32 { + f32::from_bits(self.read_u32(buf)) + } + + /// Reads a 32 bit floating point number from `buf`. + /// + /// # Panics + /// + /// Panics when `buf.len() < 8`. + #[inline] + fn read_f64(self, buf: &[u8]) -> f64 { + f64::from_bits(self.read_u64(buf)) + } + + /// Writes an unsigned 16 bit integer `n` to `buf`. + /// + /// # Panics + /// + /// Panics when `buf.len() < 2`. + #[inline] + fn write_u16(self, buf: &mut [u8], n: u16) { + let bytes = if self.is_big_endian() { + n.to_be_bytes() + } else { + n.to_le_bytes() + }; + buf[..2].copy_from_slice(&bytes); + } + + /// Writes an unsigned 32 bit integer `n` to `buf`. + /// + /// # Panics + /// + /// Panics when `buf.len() < 4`. + #[inline] + fn write_u32(self, buf: &mut [u8], n: u32) { + let bytes = if self.is_big_endian() { + n.to_be_bytes() + } else { + n.to_le_bytes() + }; + buf[..4].copy_from_slice(&bytes); + } + + /// Writes an unsigned 64 bit integer `n` to `buf`. + /// + /// # Panics + /// + /// Panics when `buf.len() < 8`. + #[inline] + fn write_u64(self, buf: &mut [u8], n: u64) { + let bytes = if self.is_big_endian() { + n.to_be_bytes() + } else { + n.to_le_bytes() + }; + buf[..8].copy_from_slice(&bytes); + } +} + +/// Byte order that is selectable at runtime. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum RunTimeEndian { + /// Little endian byte order. + Little, + /// Big endian byte order. + Big, +} + +impl Default for RunTimeEndian { + #[cfg(target_endian = "little")] + #[inline] + fn default() -> RunTimeEndian { + RunTimeEndian::Little + } + + #[cfg(target_endian = "big")] + #[inline] + fn default() -> RunTimeEndian { + RunTimeEndian::Big + } +} + +impl Endianity for RunTimeEndian { + #[inline] + fn is_big_endian(self) -> bool { + self != RunTimeEndian::Little + } +} + +/// Little endian byte order. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct LittleEndian; + +impl Default for LittleEndian { + #[inline] + fn default() -> LittleEndian { + LittleEndian + } +} + +impl Endianity for LittleEndian { + #[inline] + fn is_big_endian(self) -> bool { + false + } +} + +/// Big endian byte order. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct BigEndian; + +impl Default for BigEndian { + #[inline] + fn default() -> BigEndian { + BigEndian + } +} + +impl Endianity for BigEndian { + #[inline] + fn is_big_endian(self) -> bool { + true + } +} + +/// The native endianity for the target platform. +#[cfg(target_endian = "little")] +pub type NativeEndian = LittleEndian; + +#[cfg(target_endian = "little")] +#[allow(non_upper_case_globals)] +#[doc(hidden)] +pub const NativeEndian: LittleEndian = LittleEndian; + +/// The native endianity for the target platform. +#[cfg(target_endian = "big")] +pub type NativeEndian = BigEndian; + +#[cfg(target_endian = "big")] +#[allow(non_upper_case_globals)] +#[doc(hidden)] +pub const NativeEndian: BigEndian = BigEndian; diff --git a/vendor/gimli/src/leb128.rs b/vendor/gimli/src/leb128.rs new file mode 100644 index 0000000..de81cfd --- /dev/null +++ b/vendor/gimli/src/leb128.rs @@ -0,0 +1,612 @@ +//! Read and write DWARF's "Little Endian Base 128" (LEB128) variable length +//! integer encoding. +//! +//! The implementation is a direct translation of the psuedocode in the DWARF 4 +//! standard's appendix C. +//! +//! Read and write signed integers: +//! +//! ``` +//! # #[cfg(all(feature = "read", feature = "write"))] { +//! use gimli::{EndianSlice, NativeEndian, leb128}; +//! +//! let mut buf = [0; 1024]; +//! +//! // Write to anything that implements `std::io::Write`. +//! { +//! let mut writable = &mut buf[..]; +//! leb128::write::signed(&mut writable, -12345).expect("Should write number"); +//! } +//! +//! // Read from anything that implements `gimli::Reader`. +//! let mut readable = EndianSlice::new(&buf[..], NativeEndian); +//! let val = leb128::read::signed(&mut readable).expect("Should read number"); +//! assert_eq!(val, -12345); +//! # } +//! ``` +//! +//! Or read and write unsigned integers: +//! +//! ``` +//! # #[cfg(all(feature = "read", feature = "write"))] { +//! use gimli::{EndianSlice, NativeEndian, leb128}; +//! +//! let mut buf = [0; 1024]; +//! +//! { +//! let mut writable = &mut buf[..]; +//! leb128::write::unsigned(&mut writable, 98765).expect("Should write number"); +//! } +//! +//! let mut readable = EndianSlice::new(&buf[..], NativeEndian); +//! let val = leb128::read::unsigned(&mut readable).expect("Should read number"); +//! assert_eq!(val, 98765); +//! # } +//! ``` + +const CONTINUATION_BIT: u8 = 1 << 7; +#[cfg(feature = "read-core")] +const SIGN_BIT: u8 = 1 << 6; + +#[inline] +fn low_bits_of_byte(byte: u8) -> u8 { + byte & !CONTINUATION_BIT +} + +#[inline] +#[allow(dead_code)] +fn low_bits_of_u64(val: u64) -> u8 { + let byte = val & u64::from(core::u8::MAX); + low_bits_of_byte(byte as u8) +} + +/// A module for reading signed and unsigned integers that have been LEB128 +/// encoded. +#[cfg(feature = "read-core")] +pub mod read { + use super::{low_bits_of_byte, CONTINUATION_BIT, SIGN_BIT}; + use crate::read::{Error, Reader, Result}; + + /// Read bytes until the LEB128 continuation bit is not set. + pub fn skip<R: Reader>(r: &mut R) -> Result<()> { + loop { + let byte = r.read_u8()?; + if byte & CONTINUATION_BIT == 0 { + return Ok(()); + } + } + } + + /// Read an unsigned LEB128 number from the given `Reader` and + /// return it or an error if reading failed. + pub fn unsigned<R: Reader>(r: &mut R) -> Result<u64> { + let mut result = 0; + let mut shift = 0; + + loop { + let byte = r.read_u8()?; + if shift == 63 && byte != 0x00 && byte != 0x01 { + return Err(Error::BadUnsignedLeb128); + } + + let low_bits = u64::from(low_bits_of_byte(byte)); + result |= low_bits << shift; + + if byte & CONTINUATION_BIT == 0 { + return Ok(result); + } + + shift += 7; + } + } + + /// Read an LEB128 u16 from the given `Reader` and + /// return it or an error if reading failed. + pub fn u16<R: Reader>(r: &mut R) -> Result<u16> { + let byte = r.read_u8()?; + let mut result = u16::from(low_bits_of_byte(byte)); + if byte & CONTINUATION_BIT == 0 { + return Ok(result); + } + + let byte = r.read_u8()?; + result |= u16::from(low_bits_of_byte(byte)) << 7; + if byte & CONTINUATION_BIT == 0 { + return Ok(result); + } + + let byte = r.read_u8()?; + if byte > 0x03 { + return Err(Error::BadUnsignedLeb128); + } + result += u16::from(byte) << 14; + Ok(result) + } + + /// Read a signed LEB128 number from the given `Reader` and + /// return it or an error if reading failed. + pub fn signed<R: Reader>(r: &mut R) -> Result<i64> { + let mut result = 0; + let mut shift = 0; + let size = 64; + let mut byte; + + loop { + byte = r.read_u8()?; + if shift == 63 && byte != 0x00 && byte != 0x7f { + return Err(Error::BadSignedLeb128); + } + + let low_bits = i64::from(low_bits_of_byte(byte)); + result |= low_bits << shift; + shift += 7; + + if byte & CONTINUATION_BIT == 0 { + break; + } + } + + if shift < size && (SIGN_BIT & byte) == SIGN_BIT { + // Sign extend the result. + result |= !0 << shift; + } + + Ok(result) + } +} + +/// A module for writing integers encoded as LEB128. +#[cfg(feature = "write")] +pub mod write { + use super::{low_bits_of_u64, CONTINUATION_BIT}; + use std::io; + + /// Write the given unsigned number using the LEB128 encoding to the given + /// `std::io::Write`able. Returns the number of bytes written to `w`, or an + /// error if writing failed. + pub fn unsigned<W>(w: &mut W, mut val: u64) -> Result<usize, io::Error> + where + W: io::Write, + { + let mut bytes_written = 0; + loop { + let mut byte = low_bits_of_u64(val); + val >>= 7; + if val != 0 { + // More bytes to come, so set the continuation bit. + byte |= CONTINUATION_BIT; + } + + let buf = [byte]; + w.write_all(&buf)?; + bytes_written += 1; + + if val == 0 { + return Ok(bytes_written); + } + } + } + + /// Return the size of the LEB128 encoding of the given unsigned number. + pub fn uleb128_size(mut val: u64) -> usize { + let mut size = 0; + loop { + val >>= 7; + size += 1; + if val == 0 { + return size; + } + } + } + + /// Write the given signed number using the LEB128 encoding to the given + /// `std::io::Write`able. Returns the number of bytes written to `w`, or an + /// error if writing failed. + pub fn signed<W>(w: &mut W, mut val: i64) -> Result<usize, io::Error> + where + W: io::Write, + { + let mut bytes_written = 0; + loop { + let mut byte = val as u8; + // Keep the sign bit for testing + val >>= 6; + let done = val == 0 || val == -1; + if done { + byte &= !CONTINUATION_BIT; + } else { + // Remove the sign bit + val >>= 1; + // More bytes to come, so set the continuation bit. + byte |= CONTINUATION_BIT; + } + + let buf = [byte]; + w.write_all(&buf)?; + bytes_written += 1; + + if done { + return Ok(bytes_written); + } + } + } + + /// Return the size of the LEB128 encoding of the given signed number. + pub fn sleb128_size(mut val: i64) -> usize { + let mut size = 0; + loop { + val >>= 6; + let done = val == 0 || val == -1; + val >>= 1; + size += 1; + if done { + return size; + } + } + } +} + +#[cfg(test)] +#[cfg(all(feature = "read", feature = "write"))] +mod tests { + use super::{low_bits_of_byte, low_bits_of_u64, read, write, CONTINUATION_BIT}; + use crate::endianity::NativeEndian; + use crate::read::{EndianSlice, Error, ReaderOffsetId}; + + trait ResultExt { + fn map_eof(self, input: &[u8]) -> Self; + } + + impl<T> ResultExt for Result<T, Error> { + fn map_eof(self, input: &[u8]) -> Self { + match self { + Err(Error::UnexpectedEof(id)) => { + let id = ReaderOffsetId(id.0 - input.as_ptr() as u64); + Err(Error::UnexpectedEof(id)) + } + r => r, + } + } + } + + #[test] + fn test_low_bits_of_byte() { + for i in 0..127 { + assert_eq!(i, low_bits_of_byte(i)); + assert_eq!(i, low_bits_of_byte(i | CONTINUATION_BIT)); + } + } + + #[test] + fn test_low_bits_of_u64() { + for i in 0u64..127 { + assert_eq!(i as u8, low_bits_of_u64(1 << 16 | i)); + assert_eq!( + i as u8, + low_bits_of_u64(i << 16 | i | (u64::from(CONTINUATION_BIT))) + ); + } + } + + // Examples from the DWARF 4 standard, section 7.6, figure 22. + #[test] + fn test_read_unsigned() { + let buf = [2u8]; + let mut readable = EndianSlice::new(&buf[..], NativeEndian); + assert_eq!( + 2, + read::unsigned(&mut readable).expect("Should read number") + ); + + let buf = [127u8]; + let mut readable = EndianSlice::new(&buf[..], NativeEndian); + assert_eq!( + 127, + read::unsigned(&mut readable).expect("Should read number") + ); + + let buf = [CONTINUATION_BIT, 1]; + let mut readable = EndianSlice::new(&buf[..], NativeEndian); + assert_eq!( + 128, + read::unsigned(&mut readable).expect("Should read number") + ); + + let buf = [1u8 | CONTINUATION_BIT, 1]; + let mut readable = EndianSlice::new(&buf[..], NativeEndian); + assert_eq!( + 129, + read::unsigned(&mut readable).expect("Should read number") + ); + + let buf = [2u8 | CONTINUATION_BIT, 1]; + let mut readable = EndianSlice::new(&buf[..], NativeEndian); + assert_eq!( + 130, + read::unsigned(&mut readable).expect("Should read number") + ); + + let buf = [57u8 | CONTINUATION_BIT, 100]; + let mut readable = EndianSlice::new(&buf[..], NativeEndian); + assert_eq!( + 12857, + read::unsigned(&mut readable).expect("Should read number") + ); + } + + // Examples from the DWARF 4 standard, section 7.6, figure 23. + #[test] + fn test_read_signed() { + let buf = [2u8]; + let mut readable = EndianSlice::new(&buf[..], NativeEndian); + assert_eq!(2, read::signed(&mut readable).expect("Should read number")); + + let buf = [0x7eu8]; + let mut readable = EndianSlice::new(&buf[..], NativeEndian); + assert_eq!(-2, read::signed(&mut readable).expect("Should read number")); + + let buf = [127u8 | CONTINUATION_BIT, 0]; + let mut readable = EndianSlice::new(&buf[..], NativeEndian); + assert_eq!( + 127, + read::signed(&mut readable).expect("Should read number") + ); + + let buf = [1u8 | CONTINUATION_BIT, 0x7f]; + let mut readable = EndianSlice::new(&buf[..], NativeEndian); + assert_eq!( + -127, + read::signed(&mut readable).expect("Should read number") + ); + + let buf = [CONTINUATION_BIT, 1]; + let mut readable = EndianSlice::new(&buf[..], NativeEndian); + assert_eq!( + 128, + read::signed(&mut readable).expect("Should read number") + ); + + let buf = [CONTINUATION_BIT, 0x7f]; + let mut readable = EndianSlice::new(&buf[..], NativeEndian); + assert_eq!( + -128, + read::signed(&mut readable).expect("Should read number") + ); + + let buf = [1u8 | CONTINUATION_BIT, 1]; + let mut readable = EndianSlice::new(&buf[..], NativeEndian); + assert_eq!( + 129, + read::signed(&mut readable).expect("Should read number") + ); + + let buf = [0x7fu8 | CONTINUATION_BIT, 0x7e]; + let mut readable = EndianSlice::new(&buf[..], NativeEndian); + assert_eq!( + -129, + read::signed(&mut readable).expect("Should read number") + ); + } + + #[test] + fn test_read_signed_63_bits() { + let buf = [ + CONTINUATION_BIT, + CONTINUATION_BIT, + CONTINUATION_BIT, + CONTINUATION_BIT, + CONTINUATION_BIT, + CONTINUATION_BIT, + CONTINUATION_BIT, + CONTINUATION_BIT, + 0x40, + ]; + let mut readable = EndianSlice::new(&buf[..], NativeEndian); + assert_eq!( + -0x4000_0000_0000_0000, + read::signed(&mut readable).expect("Should read number") + ); + } + + #[test] + fn test_read_unsigned_not_enough_data() { + let buf = [CONTINUATION_BIT]; + let mut readable = EndianSlice::new(&buf[..], NativeEndian); + assert_eq!( + read::unsigned(&mut readable).map_eof(&buf), + Err(Error::UnexpectedEof(ReaderOffsetId(1))) + ); + } + + #[test] + fn test_read_signed_not_enough_data() { + let buf = [CONTINUATION_BIT]; + let mut readable = EndianSlice::new(&buf[..], NativeEndian); + assert_eq!( + read::signed(&mut readable).map_eof(&buf), + Err(Error::UnexpectedEof(ReaderOffsetId(1))) + ); + } + + #[test] + fn test_write_unsigned_not_enough_space() { + let mut buf = [0; 1]; + let mut writable = &mut buf[..]; + match write::unsigned(&mut writable, 128) { + Err(e) => assert_eq!(e.kind(), std::io::ErrorKind::WriteZero), + otherwise => panic!("Unexpected: {:?}", otherwise), + } + } + + #[test] + fn test_write_signed_not_enough_space() { + let mut buf = [0; 1]; + let mut writable = &mut buf[..]; + match write::signed(&mut writable, 128) { + Err(e) => assert_eq!(e.kind(), std::io::ErrorKind::WriteZero), + otherwise => panic!("Unexpected: {:?}", otherwise), + } + } + + #[test] + fn dogfood_signed() { + fn inner(i: i64) { + let mut buf = [0u8; 1024]; + + { + let mut writable = &mut buf[..]; + write::signed(&mut writable, i).expect("Should write signed number"); + } + + let mut readable = EndianSlice::new(&buf[..], NativeEndian); + let result = read::signed(&mut readable).expect("Should be able to read it back again"); + assert_eq!(i, result); + } + for i in -513..513 { + inner(i); + } + inner(core::i64::MIN); + } + + #[test] + fn dogfood_unsigned() { + for i in 0..1025 { + let mut buf = [0u8; 1024]; + + { + let mut writable = &mut buf[..]; + write::unsigned(&mut writable, i).expect("Should write signed number"); + } + + let mut readable = EndianSlice::new(&buf[..], NativeEndian); + let result = + read::unsigned(&mut readable).expect("Should be able to read it back again"); + assert_eq!(i, result); + } + } + + #[test] + fn test_read_unsigned_overflow() { + let buf = [ + 2u8 | CONTINUATION_BIT, + 2 | CONTINUATION_BIT, + 2 | CONTINUATION_BIT, + 2 | CONTINUATION_BIT, + 2 | CONTINUATION_BIT, + 2 | CONTINUATION_BIT, + 2 | CONTINUATION_BIT, + 2 | CONTINUATION_BIT, + 2 | CONTINUATION_BIT, + 2 | CONTINUATION_BIT, + 2 | CONTINUATION_BIT, + 2 | CONTINUATION_BIT, + 2 | CONTINUATION_BIT, + 2 | CONTINUATION_BIT, + 2 | CONTINUATION_BIT, + 2 | CONTINUATION_BIT, + 2 | CONTINUATION_BIT, + 2 | CONTINUATION_BIT, + 2 | CONTINUATION_BIT, + 2 | CONTINUATION_BIT, + 2 | CONTINUATION_BIT, + 2 | CONTINUATION_BIT, + 2 | CONTINUATION_BIT, + 2 | CONTINUATION_BIT, + 2 | CONTINUATION_BIT, + 2 | CONTINUATION_BIT, + 2 | CONTINUATION_BIT, + 2 | CONTINUATION_BIT, + 2 | CONTINUATION_BIT, + 2 | CONTINUATION_BIT, + 1, + ]; + let mut readable = EndianSlice::new(&buf[..], NativeEndian); + assert!(read::unsigned(&mut readable).is_err()); + } + + #[test] + fn test_read_signed_overflow() { + let buf = [ + 2u8 | CONTINUATION_BIT, + 2 | CONTINUATION_BIT, + 2 | CONTINUATION_BIT, + 2 | CONTINUATION_BIT, + 2 | CONTINUATION_BIT, + 2 | CONTINUATION_BIT, + 2 | CONTINUATION_BIT, + 2 | CONTINUATION_BIT, + 2 | CONTINUATION_BIT, + 2 | CONTINUATION_BIT, + 2 | CONTINUATION_BIT, + 2 | CONTINUATION_BIT, + 2 | CONTINUATION_BIT, + 2 | CONTINUATION_BIT, + 2 | CONTINUATION_BIT, + 2 | CONTINUATION_BIT, + 2 | CONTINUATION_BIT, + 2 | CONTINUATION_BIT, + 2 | CONTINUATION_BIT, + 2 | CONTINUATION_BIT, + 2 | CONTINUATION_BIT, + 2 | CONTINUATION_BIT, + 2 | CONTINUATION_BIT, + 2 | CONTINUATION_BIT, + 2 | CONTINUATION_BIT, + 2 | CONTINUATION_BIT, + 2 | CONTINUATION_BIT, + 2 | CONTINUATION_BIT, + 2 | CONTINUATION_BIT, + 2 | CONTINUATION_BIT, + 1, + ]; + let mut readable = EndianSlice::new(&buf[..], NativeEndian); + assert!(read::signed(&mut readable).is_err()); + } + + #[test] + fn test_read_multiple() { + let buf = [2u8 | CONTINUATION_BIT, 1u8, 1u8]; + + let mut readable = EndianSlice::new(&buf[..], NativeEndian); + assert_eq!( + read::unsigned(&mut readable).expect("Should read first number"), + 130u64 + ); + assert_eq!( + read::unsigned(&mut readable).expect("Should read first number"), + 1u64 + ); + } + + #[test] + fn test_read_u16() { + for (buf, val) in [ + (&[2][..], 2), + (&[0x7f][..], 0x7f), + (&[0x80, 1][..], 0x80), + (&[0x81, 1][..], 0x81), + (&[0x82, 1][..], 0x82), + (&[0xff, 0x7f][..], 0x3fff), + (&[0x80, 0x80, 1][..], 0x4000), + (&[0xff, 0xff, 1][..], 0x7fff), + (&[0xff, 0xff, 3][..], 0xffff), + ] + .iter() + { + let mut readable = EndianSlice::new(buf, NativeEndian); + assert_eq!(*val, read::u16(&mut readable).expect("Should read number")); + } + + for buf in [ + &[0x80][..], + &[0x80, 0x80][..], + &[0x80, 0x80, 4][..], + &[0x80, 0x80, 0x80, 3][..], + ] + .iter() + { + let mut readable = EndianSlice::new(buf, NativeEndian); + assert!(read::u16(&mut readable).is_err(), "{:?}", buf); + } + } +} diff --git a/vendor/gimli/src/lib.rs b/vendor/gimli/src/lib.rs new file mode 100644 index 0000000..5dea344 --- /dev/null +++ b/vendor/gimli/src/lib.rs @@ -0,0 +1,79 @@ +//! `gimli` is a library for reading and writing the +//! [DWARF debugging format](https://dwarfstd.org/). +//! +//! See the [read](./read/index.html) and [write](./write/index.html) modules +//! for examples and API documentation. +//! +//! ## Cargo Features +//! +//! Cargo features that can be enabled with `gimli`: +//! +//! * `std`: Enabled by default. Use the `std` library. Disabling this feature +//! allows using `gimli` in embedded environments that do not have access to +//! `std`. Note that even when `std` is disabled, `gimli` still requires an +//! implementation of the `alloc` crate. +//! +//! * `read`: Enabled by default. Enables the `read` module. Use of `std` is +//! optional. +//! +//! * `write`: Enabled by default. Enables the `write` module. Always uses +//! the `std` library. +#![deny(missing_docs)] +#![deny(missing_debug_implementations)] +// Selectively enable rust 2018 warnings +#![warn(bare_trait_objects)] +#![warn(unused_extern_crates)] +#![warn(ellipsis_inclusive_range_patterns)] +//#![warn(elided_lifetimes_in_paths)] +#![warn(explicit_outlives_requirements)] +// Style. +#![allow(clippy::bool_to_int_with_if)] +#![allow(clippy::collapsible_else_if)] +#![allow(clippy::comparison_chain)] +#![allow(clippy::manual_range_contains)] +#![allow(clippy::needless_late_init)] +#![allow(clippy::too_many_arguments)] +// False positives with `fallible_iterator`. +#![allow(clippy::should_implement_trait)] +// False positives. +#![allow(clippy::derive_partial_eq_without_eq)] +#![no_std] + +#[allow(unused_imports)] +#[cfg(any(feature = "read", feature = "write"))] +#[macro_use] +extern crate alloc; + +#[cfg(any(feature = "std", feature = "write"))] +#[macro_use] +extern crate std; + +#[cfg(feature = "endian-reader")] +pub use stable_deref_trait::{CloneStableDeref, StableDeref}; + +mod common; +pub use crate::common::*; + +mod arch; +pub use crate::arch::*; + +pub mod constants; +// For backwards compat. +pub use crate::constants::*; + +mod endianity; +pub use crate::endianity::*; + +pub mod leb128; + +#[cfg(feature = "read-core")] +pub mod read; +// For backwards compat. +#[cfg(feature = "read-core")] +pub use crate::read::*; + +#[cfg(feature = "write")] +pub mod write; + +#[cfg(test)] +mod test_util; diff --git a/vendor/gimli/src/read/abbrev.rs b/vendor/gimli/src/read/abbrev.rs new file mode 100644 index 0000000..1162583 --- /dev/null +++ b/vendor/gimli/src/read/abbrev.rs @@ -0,0 +1,1102 @@ +//! Functions for parsing DWARF debugging abbreviations. + +use alloc::collections::btree_map; +use alloc::sync::Arc; +use alloc::vec::Vec; +use core::convert::TryFrom; +use core::fmt::{self, Debug}; +use core::iter::FromIterator; +use core::ops::Deref; + +use crate::common::{DebugAbbrevOffset, Encoding, SectionId}; +use crate::constants; +use crate::endianity::Endianity; +use crate::read::{ + DebugInfoUnitHeadersIter, EndianSlice, Error, Reader, ReaderOffset, Result, Section, UnitHeader, +}; + +/// The `DebugAbbrev` struct represents the abbreviations describing +/// `DebuggingInformationEntry`s' attribute names and forms found in the +/// `.debug_abbrev` section. +#[derive(Debug, Default, Clone, Copy)] +pub struct DebugAbbrev<R> { + debug_abbrev_section: R, +} + +impl<'input, Endian> DebugAbbrev<EndianSlice<'input, Endian>> +where + Endian: Endianity, +{ + /// Construct a new `DebugAbbrev` instance from the data in the `.debug_abbrev` + /// section. + /// + /// It is the caller's responsibility to read the `.debug_abbrev` section and + /// present it as a `&[u8]` slice. That means using some ELF loader on + /// Linux, a Mach-O loader on macOS, etc. + /// + /// ``` + /// use gimli::{DebugAbbrev, LittleEndian}; + /// + /// # let buf = [0x00, 0x01, 0x02, 0x03]; + /// # let read_debug_abbrev_section_somehow = || &buf; + /// let debug_abbrev = DebugAbbrev::new(read_debug_abbrev_section_somehow(), LittleEndian); + /// ``` + pub fn new(debug_abbrev_section: &'input [u8], endian: Endian) -> Self { + Self::from(EndianSlice::new(debug_abbrev_section, endian)) + } +} + +impl<R: Reader> DebugAbbrev<R> { + /// Parse the abbreviations at the given `offset` within this + /// `.debug_abbrev` section. + /// + /// The `offset` should generally be retrieved from a unit header. + pub fn abbreviations( + &self, + debug_abbrev_offset: DebugAbbrevOffset<R::Offset>, + ) -> Result<Abbreviations> { + let input = &mut self.debug_abbrev_section.clone(); + input.skip(debug_abbrev_offset.0)?; + Abbreviations::parse(input) + } +} + +impl<T> DebugAbbrev<T> { + /// Create a `DebugAbbrev` section that references the data in `self`. + /// + /// This is useful when `R` implements `Reader` but `T` does not. + /// + /// ## Example Usage + /// + /// ```rust,no_run + /// # let load_section = || unimplemented!(); + /// // Read the DWARF section into a `Vec` with whatever object loader you're using. + /// let owned_section: gimli::DebugAbbrev<Vec<u8>> = load_section(); + /// // Create a reference to the DWARF section. + /// let section = owned_section.borrow(|section| { + /// gimli::EndianSlice::new(§ion, gimli::LittleEndian) + /// }); + /// ``` + pub fn borrow<'a, F, R>(&'a self, mut borrow: F) -> DebugAbbrev<R> + where + F: FnMut(&'a T) -> R, + { + borrow(&self.debug_abbrev_section).into() + } +} + +impl<R> Section<R> for DebugAbbrev<R> { + fn id() -> SectionId { + SectionId::DebugAbbrev + } + + fn reader(&self) -> &R { + &self.debug_abbrev_section + } +} + +impl<R> From<R> for DebugAbbrev<R> { + fn from(debug_abbrev_section: R) -> Self { + DebugAbbrev { + debug_abbrev_section, + } + } +} + +/// The strategy to use for caching abbreviations. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[non_exhaustive] +pub enum AbbreviationsCacheStrategy { + /// Cache abbreviations that are used more than once. + /// + /// This is useful if the units in the `.debug_info` section will be parsed only once. + Duplicates, + /// Cache all abbreviations. + /// + /// This is useful if the units in the `.debug_info` section will be parsed more than once. + All, +} + +/// A cache of previously parsed `Abbreviations`. +#[derive(Debug, Default)] +pub struct AbbreviationsCache { + abbreviations: btree_map::BTreeMap<u64, Result<Arc<Abbreviations>>>, +} + +impl AbbreviationsCache { + /// Create an empty abbreviations cache. + pub fn new() -> Self { + Self::default() + } + + /// Parse abbreviations and store them in the cache. + /// + /// This will iterate over the given units to determine the abbreviations + /// offsets. Any existing cache entries are discarded. + /// + /// Errors during parsing abbreviations are also stored in the cache. + /// Errors during iterating over the units are ignored. + pub fn populate<R: Reader>( + &mut self, + strategy: AbbreviationsCacheStrategy, + debug_abbrev: &DebugAbbrev<R>, + mut units: DebugInfoUnitHeadersIter<R>, + ) { + let mut offsets = Vec::new(); + match strategy { + AbbreviationsCacheStrategy::Duplicates => { + while let Ok(Some(unit)) = units.next() { + offsets.push(unit.debug_abbrev_offset()); + } + offsets.sort_unstable_by_key(|offset| offset.0); + let mut prev_offset = R::Offset::from_u8(0); + let mut count = 0; + offsets.retain(|offset| { + if count == 0 || prev_offset != offset.0 { + prev_offset = offset.0; + count = 1; + } else { + count += 1; + } + count == 2 + }); + } + AbbreviationsCacheStrategy::All => { + while let Ok(Some(unit)) = units.next() { + offsets.push(unit.debug_abbrev_offset()); + } + offsets.sort_unstable_by_key(|offset| offset.0); + offsets.dedup(); + } + } + self.abbreviations = offsets + .into_iter() + .map(|offset| { + ( + offset.0.into_u64(), + debug_abbrev.abbreviations(offset).map(Arc::new), + ) + }) + .collect(); + } + + /// Set an entry in the abbreviations cache. + /// + /// This is only required if you want to manually populate the cache. + pub fn set<R: Reader>( + &mut self, + offset: DebugAbbrevOffset<R::Offset>, + abbreviations: Arc<Abbreviations>, + ) { + self.abbreviations + .insert(offset.0.into_u64(), Ok(abbreviations)); + } + + /// Parse the abbreviations at the given offset. + /// + /// This uses the cache if possible, but does not update it. + pub fn get<R: Reader>( + &self, + debug_abbrev: &DebugAbbrev<R>, + offset: DebugAbbrevOffset<R::Offset>, + ) -> Result<Arc<Abbreviations>> { + match self.abbreviations.get(&offset.0.into_u64()) { + Some(entry) => entry.clone(), + None => debug_abbrev.abbreviations(offset).map(Arc::new), + } + } +} + +/// A set of type abbreviations. +/// +/// Construct an `Abbreviations` instance with the +/// [`abbreviations()`](struct.UnitHeader.html#method.abbreviations) +/// method. +#[derive(Debug, Default, Clone)] +pub struct Abbreviations { + vec: Vec<Abbreviation>, + map: btree_map::BTreeMap<u64, Abbreviation>, +} + +impl Abbreviations { + /// Construct a new, empty set of abbreviations. + fn empty() -> Abbreviations { + Abbreviations { + vec: Vec::new(), + map: btree_map::BTreeMap::new(), + } + } + + /// Insert an abbreviation into the set. + /// + /// Returns `Ok` if it is the first abbreviation in the set with its code, + /// `Err` if the code is a duplicate and there already exists an + /// abbreviation in the set with the given abbreviation's code. + fn insert(&mut self, abbrev: Abbreviation) -> ::core::result::Result<(), ()> { + let code_usize = abbrev.code as usize; + if code_usize as u64 == abbrev.code { + // Optimize for sequential abbreviation codes by storing them + // in a Vec, as long as the map doesn't already contain them. + // A potential further optimization would be to allow some + // holes in the Vec, but there's no need for that yet. + if code_usize - 1 < self.vec.len() { + return Err(()); + } else if code_usize - 1 == self.vec.len() { + if !self.map.is_empty() && self.map.contains_key(&abbrev.code) { + return Err(()); + } else { + self.vec.push(abbrev); + return Ok(()); + } + } + } + match self.map.entry(abbrev.code) { + btree_map::Entry::Occupied(_) => Err(()), + btree_map::Entry::Vacant(entry) => { + entry.insert(abbrev); + Ok(()) + } + } + } + + /// Get the abbreviation associated with the given code. + #[inline] + pub fn get(&self, code: u64) -> Option<&Abbreviation> { + if let Ok(code) = usize::try_from(code) { + let index = code.checked_sub(1)?; + if index < self.vec.len() { + return Some(&self.vec[index]); + } + } + + self.map.get(&code) + } + + /// Parse a series of abbreviations, terminated by a null abbreviation. + fn parse<R: Reader>(input: &mut R) -> Result<Abbreviations> { + let mut abbrevs = Abbreviations::empty(); + + while let Some(abbrev) = Abbreviation::parse(input)? { + if abbrevs.insert(abbrev).is_err() { + return Err(Error::DuplicateAbbreviationCode); + } + } + + Ok(abbrevs) + } +} + +/// An abbreviation describes the shape of a `DebuggingInformationEntry`'s type: +/// its code, tag type, whether it has children, and its set of attributes. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Abbreviation { + code: u64, + tag: constants::DwTag, + has_children: constants::DwChildren, + attributes: Attributes, +} + +impl Abbreviation { + /// Construct a new `Abbreviation`. + /// + /// ### Panics + /// + /// Panics if `code` is `0`. + pub(crate) fn new( + code: u64, + tag: constants::DwTag, + has_children: constants::DwChildren, + attributes: Attributes, + ) -> Abbreviation { + assert_ne!(code, 0); + Abbreviation { + code, + tag, + has_children, + attributes, + } + } + + /// Get this abbreviation's code. + #[inline] + pub fn code(&self) -> u64 { + self.code + } + + /// Get this abbreviation's tag. + #[inline] + pub fn tag(&self) -> constants::DwTag { + self.tag + } + + /// Return true if this abbreviation's type has children, false otherwise. + #[inline] + pub fn has_children(&self) -> bool { + self.has_children == constants::DW_CHILDREN_yes + } + + /// Get this abbreviation's attributes. + #[inline] + pub fn attributes(&self) -> &[AttributeSpecification] { + &self.attributes[..] + } + + /// Parse an abbreviation's tag. + fn parse_tag<R: Reader>(input: &mut R) -> Result<constants::DwTag> { + let val = input.read_uleb128_u16()?; + if val == 0 { + Err(Error::AbbreviationTagZero) + } else { + Ok(constants::DwTag(val)) + } + } + + /// Parse an abbreviation's "does the type have children?" byte. + fn parse_has_children<R: Reader>(input: &mut R) -> Result<constants::DwChildren> { + let val = input.read_u8()?; + let val = constants::DwChildren(val); + if val == constants::DW_CHILDREN_no || val == constants::DW_CHILDREN_yes { + Ok(val) + } else { + Err(Error::BadHasChildren) + } + } + + /// Parse a series of attribute specifications, terminated by a null attribute + /// specification. + fn parse_attributes<R: Reader>(input: &mut R) -> Result<Attributes> { + let mut attrs = Attributes::new(); + + while let Some(attr) = AttributeSpecification::parse(input)? { + attrs.push(attr); + } + + Ok(attrs) + } + + /// Parse an abbreviation. Return `None` for the null abbreviation, `Some` + /// for an actual abbreviation. + fn parse<R: Reader>(input: &mut R) -> Result<Option<Abbreviation>> { + let code = input.read_uleb128()?; + if code == 0 { + return Ok(None); + } + + let tag = Self::parse_tag(input)?; + let has_children = Self::parse_has_children(input)?; + let attributes = Self::parse_attributes(input)?; + let abbrev = Abbreviation::new(code, tag, has_children, attributes); + Ok(Some(abbrev)) + } +} + +/// A list of attributes found in an `Abbreviation` +#[derive(Clone)] +pub(crate) enum Attributes { + Inline { + buf: [AttributeSpecification; MAX_ATTRIBUTES_INLINE], + len: usize, + }, + Heap(Vec<AttributeSpecification>), +} + +// Length of 5 based on benchmark results for both x86-64 and i686. +const MAX_ATTRIBUTES_INLINE: usize = 5; + +impl Attributes { + /// Returns a new empty list of attributes + fn new() -> Attributes { + let default = + AttributeSpecification::new(constants::DW_AT_null, constants::DW_FORM_null, None); + Attributes::Inline { + buf: [default; 5], + len: 0, + } + } + + /// Pushes a new value onto this list of attributes. + fn push(&mut self, attr: AttributeSpecification) { + match self { + Attributes::Heap(list) => list.push(attr), + Attributes::Inline { + buf, + len: MAX_ATTRIBUTES_INLINE, + } => { + let mut list = buf.to_vec(); + list.push(attr); + *self = Attributes::Heap(list); + } + Attributes::Inline { buf, len } => { + buf[*len] = attr; + *len += 1; + } + } + } +} + +impl Debug for Attributes { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + (**self).fmt(f) + } +} + +impl PartialEq for Attributes { + fn eq(&self, other: &Attributes) -> bool { + **self == **other + } +} + +impl Eq for Attributes {} + +impl Deref for Attributes { + type Target = [AttributeSpecification]; + fn deref(&self) -> &[AttributeSpecification] { + match self { + Attributes::Inline { buf, len } => &buf[..*len], + Attributes::Heap(list) => list, + } + } +} + +impl FromIterator<AttributeSpecification> for Attributes { + fn from_iter<I>(iter: I) -> Attributes + where + I: IntoIterator<Item = AttributeSpecification>, + { + let mut list = Attributes::new(); + for item in iter { + list.push(item); + } + list + } +} + +impl From<Vec<AttributeSpecification>> for Attributes { + fn from(list: Vec<AttributeSpecification>) -> Attributes { + Attributes::Heap(list) + } +} + +/// The description of an attribute in an abbreviated type. It is a pair of name +/// and form. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct AttributeSpecification { + name: constants::DwAt, + form: constants::DwForm, + implicit_const_value: i64, +} + +impl AttributeSpecification { + /// Construct a new `AttributeSpecification` from the given name and form + /// and implicit const value. + #[inline] + pub fn new( + name: constants::DwAt, + form: constants::DwForm, + implicit_const_value: Option<i64>, + ) -> AttributeSpecification { + debug_assert!( + (form == constants::DW_FORM_implicit_const && implicit_const_value.is_some()) + || (form != constants::DW_FORM_implicit_const && implicit_const_value.is_none()) + ); + AttributeSpecification { + name, + form, + implicit_const_value: implicit_const_value.unwrap_or(0), + } + } + + /// Get the attribute's name. + #[inline] + pub fn name(&self) -> constants::DwAt { + self.name + } + + /// Get the attribute's form. + #[inline] + pub fn form(&self) -> constants::DwForm { + self.form + } + + /// Get the attribute's implicit const value. + #[inline] + pub fn implicit_const_value(&self) -> Option<i64> { + if self.form == constants::DW_FORM_implicit_const { + Some(self.implicit_const_value) + } else { + None + } + } + + /// Return the size of the attribute, in bytes. + /// + /// Note that because some attributes are variably sized, the size cannot + /// always be known without parsing, in which case we return `None`. + pub fn size<R: Reader>(&self, header: &UnitHeader<R>) -> Option<usize> { + get_attribute_size(self.form, header.encoding()).map(usize::from) + } + + /// Parse an attribute's form. + fn parse_form<R: Reader>(input: &mut R) -> Result<constants::DwForm> { + let val = input.read_uleb128_u16()?; + if val == 0 { + Err(Error::AttributeFormZero) + } else { + Ok(constants::DwForm(val)) + } + } + + /// Parse an attribute specification. Returns `None` for the null attribute + /// specification, `Some` for an actual attribute specification. + fn parse<R: Reader>(input: &mut R) -> Result<Option<AttributeSpecification>> { + let name = input.read_uleb128_u16()?; + if name == 0 { + // Parse the null attribute specification. + let form = input.read_uleb128_u16()?; + return if form == 0 { + Ok(None) + } else { + Err(Error::ExpectedZero) + }; + } + + let name = constants::DwAt(name); + let form = Self::parse_form(input)?; + let implicit_const_value = if form == constants::DW_FORM_implicit_const { + Some(input.read_sleb128()?) + } else { + None + }; + let spec = AttributeSpecification::new(name, form, implicit_const_value); + Ok(Some(spec)) + } +} + +#[inline] +pub(crate) fn get_attribute_size(form: constants::DwForm, encoding: Encoding) -> Option<u8> { + match form { + constants::DW_FORM_addr => Some(encoding.address_size), + + constants::DW_FORM_implicit_const | constants::DW_FORM_flag_present => Some(0), + + constants::DW_FORM_data1 + | constants::DW_FORM_flag + | constants::DW_FORM_strx1 + | constants::DW_FORM_ref1 + | constants::DW_FORM_addrx1 => Some(1), + + constants::DW_FORM_data2 + | constants::DW_FORM_ref2 + | constants::DW_FORM_addrx2 + | constants::DW_FORM_strx2 => Some(2), + + constants::DW_FORM_addrx3 | constants::DW_FORM_strx3 => Some(3), + + constants::DW_FORM_data4 + | constants::DW_FORM_ref_sup4 + | constants::DW_FORM_ref4 + | constants::DW_FORM_strx4 + | constants::DW_FORM_addrx4 => Some(4), + + constants::DW_FORM_data8 + | constants::DW_FORM_ref8 + | constants::DW_FORM_ref_sig8 + | constants::DW_FORM_ref_sup8 => Some(8), + + constants::DW_FORM_data16 => Some(16), + + constants::DW_FORM_sec_offset + | constants::DW_FORM_GNU_ref_alt + | constants::DW_FORM_strp + | constants::DW_FORM_strp_sup + | constants::DW_FORM_GNU_strp_alt + | constants::DW_FORM_line_strp => Some(encoding.format.word_size()), + + constants::DW_FORM_ref_addr => { + // This is an offset, but DWARF version 2 specifies that DW_FORM_ref_addr + // has the same size as an address on the target system. This was changed + // in DWARF version 3. + Some(if encoding.version == 2 { + encoding.address_size + } else { + encoding.format.word_size() + }) + } + + // Variably sized forms. + constants::DW_FORM_block + | constants::DW_FORM_block1 + | constants::DW_FORM_block2 + | constants::DW_FORM_block4 + | constants::DW_FORM_exprloc + | constants::DW_FORM_ref_udata + | constants::DW_FORM_string + | constants::DW_FORM_sdata + | constants::DW_FORM_udata + | constants::DW_FORM_indirect => None, + + // We don't know the size of unknown forms. + _ => None, + } +} + +#[cfg(test)] +pub mod tests { + use super::*; + use crate::constants; + use crate::endianity::LittleEndian; + use crate::read::{EndianSlice, Error}; + use crate::test_util::GimliSectionMethods; + #[cfg(target_pointer_width = "32")] + use core::u32; + use test_assembler::Section; + + pub trait AbbrevSectionMethods { + fn abbrev(self, code: u64, tag: constants::DwTag, children: constants::DwChildren) -> Self; + fn abbrev_null(self) -> Self; + fn abbrev_attr(self, name: constants::DwAt, form: constants::DwForm) -> Self; + fn abbrev_attr_implicit_const(self, name: constants::DwAt, value: i64) -> Self; + fn abbrev_attr_null(self) -> Self; + } + + impl AbbrevSectionMethods for Section { + fn abbrev(self, code: u64, tag: constants::DwTag, children: constants::DwChildren) -> Self { + self.uleb(code).uleb(tag.0.into()).D8(children.0) + } + + fn abbrev_null(self) -> Self { + self.D8(0) + } + + fn abbrev_attr(self, name: constants::DwAt, form: constants::DwForm) -> Self { + self.uleb(name.0.into()).uleb(form.0.into()) + } + + fn abbrev_attr_implicit_const(self, name: constants::DwAt, value: i64) -> Self { + self.uleb(name.0.into()) + .uleb(constants::DW_FORM_implicit_const.0.into()) + .sleb(value) + } + + fn abbrev_attr_null(self) -> Self { + self.D8(0).D8(0) + } + } + + #[test] + fn test_debug_abbrev_ok() { + let extra_start = [1, 2, 3, 4]; + let expected_rest = [5, 6, 7, 8]; + #[rustfmt::skip] + let buf = Section::new() + .append_bytes(&extra_start) + .abbrev(2, constants::DW_TAG_subprogram, constants::DW_CHILDREN_no) + .abbrev_attr(constants::DW_AT_name, constants::DW_FORM_string) + .abbrev_attr_null() + .abbrev(1, constants::DW_TAG_compile_unit, constants::DW_CHILDREN_yes) + .abbrev_attr(constants::DW_AT_producer, constants::DW_FORM_strp) + .abbrev_attr(constants::DW_AT_language, constants::DW_FORM_data2) + .abbrev_attr_null() + .abbrev_null() + .append_bytes(&expected_rest) + .get_contents() + .unwrap(); + + let abbrev1 = Abbreviation::new( + 1, + constants::DW_TAG_compile_unit, + constants::DW_CHILDREN_yes, + vec![ + AttributeSpecification::new( + constants::DW_AT_producer, + constants::DW_FORM_strp, + None, + ), + AttributeSpecification::new( + constants::DW_AT_language, + constants::DW_FORM_data2, + None, + ), + ] + .into(), + ); + + let abbrev2 = Abbreviation::new( + 2, + constants::DW_TAG_subprogram, + constants::DW_CHILDREN_no, + vec![AttributeSpecification::new( + constants::DW_AT_name, + constants::DW_FORM_string, + None, + )] + .into(), + ); + + let debug_abbrev = DebugAbbrev::new(&buf, LittleEndian); + let debug_abbrev_offset = DebugAbbrevOffset(extra_start.len()); + let abbrevs = debug_abbrev + .abbreviations(debug_abbrev_offset) + .expect("Should parse abbreviations"); + assert_eq!(abbrevs.get(1), Some(&abbrev1)); + assert_eq!(abbrevs.get(2), Some(&abbrev2)); + } + + #[test] + fn test_abbreviations_insert() { + fn abbrev(code: u16) -> Abbreviation { + Abbreviation::new( + code.into(), + constants::DwTag(code), + constants::DW_CHILDREN_no, + vec![].into(), + ) + } + + fn assert_abbrev(abbrevs: &Abbreviations, code: u16) { + let abbrev = abbrevs.get(code.into()).unwrap(); + assert_eq!(abbrev.tag(), constants::DwTag(code)); + } + + // Sequential insert. + let mut abbrevs = Abbreviations::empty(); + abbrevs.insert(abbrev(1)).unwrap(); + abbrevs.insert(abbrev(2)).unwrap(); + assert_eq!(abbrevs.vec.len(), 2); + assert!(abbrevs.map.is_empty()); + assert_abbrev(&abbrevs, 1); + assert_abbrev(&abbrevs, 2); + + // Out of order insert. + let mut abbrevs = Abbreviations::empty(); + abbrevs.insert(abbrev(2)).unwrap(); + abbrevs.insert(abbrev(3)).unwrap(); + assert!(abbrevs.vec.is_empty()); + assert_abbrev(&abbrevs, 2); + assert_abbrev(&abbrevs, 3); + + // Mixed order insert. + let mut abbrevs = Abbreviations::empty(); + abbrevs.insert(abbrev(1)).unwrap(); + abbrevs.insert(abbrev(3)).unwrap(); + abbrevs.insert(abbrev(2)).unwrap(); + assert_eq!(abbrevs.vec.len(), 2); + assert_abbrev(&abbrevs, 1); + assert_abbrev(&abbrevs, 2); + assert_abbrev(&abbrevs, 3); + + // Duplicate code in vec. + let mut abbrevs = Abbreviations::empty(); + abbrevs.insert(abbrev(1)).unwrap(); + abbrevs.insert(abbrev(2)).unwrap(); + assert_eq!(abbrevs.insert(abbrev(1)), Err(())); + assert_eq!(abbrevs.insert(abbrev(2)), Err(())); + + // Duplicate code in map when adding to map. + let mut abbrevs = Abbreviations::empty(); + abbrevs.insert(abbrev(2)).unwrap(); + assert_eq!(abbrevs.insert(abbrev(2)), Err(())); + + // Duplicate code in map when adding to vec. + let mut abbrevs = Abbreviations::empty(); + abbrevs.insert(abbrev(2)).unwrap(); + abbrevs.insert(abbrev(1)).unwrap(); + assert_eq!(abbrevs.insert(abbrev(2)), Err(())); + + // 32-bit usize conversions. + let mut abbrevs = Abbreviations::empty(); + abbrevs.insert(abbrev(2)).unwrap(); + } + + #[test] + #[cfg(target_pointer_width = "32")] + fn test_abbreviations_insert_32() { + fn abbrev(code: u64) -> Abbreviation { + Abbreviation::new( + code, + constants::DwTag(code as u16), + constants::DW_CHILDREN_no, + vec![].into(), + ) + } + + fn assert_abbrev(abbrevs: &Abbreviations, code: u64) { + let abbrev = abbrevs.get(code).unwrap(); + assert_eq!(abbrev.tag(), constants::DwTag(code as u16)); + } + + let mut abbrevs = Abbreviations::empty(); + abbrevs.insert(abbrev(1)).unwrap(); + + let wrap_code = (u32::MAX as u64 + 1) + 1; + // `get` should not treat the wrapped code as `1`. + assert_eq!(abbrevs.get(wrap_code), None); + // `insert` should not treat the wrapped code as `1`. + abbrevs.insert(abbrev(wrap_code)).unwrap(); + assert_abbrev(&abbrevs, 1); + assert_abbrev(&abbrevs, wrap_code); + } + + #[test] + fn test_parse_abbreviations_ok() { + let expected_rest = [1, 2, 3, 4]; + #[rustfmt::skip] + let buf = Section::new() + .abbrev(2, constants::DW_TAG_subprogram, constants::DW_CHILDREN_no) + .abbrev_attr(constants::DW_AT_name, constants::DW_FORM_string) + .abbrev_attr_null() + .abbrev(1, constants::DW_TAG_compile_unit, constants::DW_CHILDREN_yes) + .abbrev_attr(constants::DW_AT_producer, constants::DW_FORM_strp) + .abbrev_attr(constants::DW_AT_language, constants::DW_FORM_data2) + .abbrev_attr_null() + .abbrev_null() + .append_bytes(&expected_rest) + .get_contents() + .unwrap(); + let rest = &mut EndianSlice::new(&*buf, LittleEndian); + + let abbrev1 = Abbreviation::new( + 1, + constants::DW_TAG_compile_unit, + constants::DW_CHILDREN_yes, + vec![ + AttributeSpecification::new( + constants::DW_AT_producer, + constants::DW_FORM_strp, + None, + ), + AttributeSpecification::new( + constants::DW_AT_language, + constants::DW_FORM_data2, + None, + ), + ] + .into(), + ); + + let abbrev2 = Abbreviation::new( + 2, + constants::DW_TAG_subprogram, + constants::DW_CHILDREN_no, + vec![AttributeSpecification::new( + constants::DW_AT_name, + constants::DW_FORM_string, + None, + )] + .into(), + ); + + let abbrevs = Abbreviations::parse(rest).expect("Should parse abbreviations"); + assert_eq!(abbrevs.get(1), Some(&abbrev1)); + assert_eq!(abbrevs.get(2), Some(&abbrev2)); + assert_eq!(*rest, EndianSlice::new(&expected_rest, LittleEndian)); + } + + #[test] + fn test_parse_abbreviations_duplicate() { + let expected_rest = [1, 2, 3, 4]; + #[rustfmt::skip] + let buf = Section::new() + .abbrev(1, constants::DW_TAG_subprogram, constants::DW_CHILDREN_no) + .abbrev_attr(constants::DW_AT_name, constants::DW_FORM_string) + .abbrev_attr_null() + .abbrev(1, constants::DW_TAG_compile_unit, constants::DW_CHILDREN_yes) + .abbrev_attr(constants::DW_AT_producer, constants::DW_FORM_strp) + .abbrev_attr(constants::DW_AT_language, constants::DW_FORM_data2) + .abbrev_attr_null() + .abbrev_null() + .append_bytes(&expected_rest) + .get_contents() + .unwrap(); + let buf = &mut EndianSlice::new(&*buf, LittleEndian); + + match Abbreviations::parse(buf) { + Err(Error::DuplicateAbbreviationCode) => {} + otherwise => panic!("Unexpected result: {:?}", otherwise), + }; + } + + #[test] + fn test_parse_abbreviation_tag_ok() { + let buf = [0x01, 0x02]; + let rest = &mut EndianSlice::new(&buf, LittleEndian); + let tag = Abbreviation::parse_tag(rest).expect("Should parse tag"); + assert_eq!(tag, constants::DW_TAG_array_type); + assert_eq!(*rest, EndianSlice::new(&buf[1..], LittleEndian)); + } + + #[test] + fn test_parse_abbreviation_tag_zero() { + let buf = [0x00]; + let buf = &mut EndianSlice::new(&buf, LittleEndian); + match Abbreviation::parse_tag(buf) { + Err(Error::AbbreviationTagZero) => {} + otherwise => panic!("Unexpected result: {:?}", otherwise), + }; + } + + #[test] + fn test_parse_abbreviation_has_children() { + let buf = [0x00, 0x01, 0x02]; + let rest = &mut EndianSlice::new(&buf, LittleEndian); + let val = Abbreviation::parse_has_children(rest).expect("Should parse children"); + assert_eq!(val, constants::DW_CHILDREN_no); + let val = Abbreviation::parse_has_children(rest).expect("Should parse children"); + assert_eq!(val, constants::DW_CHILDREN_yes); + match Abbreviation::parse_has_children(rest) { + Err(Error::BadHasChildren) => {} + otherwise => panic!("Unexpected result: {:?}", otherwise), + }; + } + + #[test] + fn test_parse_abbreviation_ok() { + let expected_rest = [0x01, 0x02, 0x03, 0x04]; + let buf = Section::new() + .abbrev(1, constants::DW_TAG_subprogram, constants::DW_CHILDREN_no) + .abbrev_attr(constants::DW_AT_name, constants::DW_FORM_string) + .abbrev_attr_null() + .append_bytes(&expected_rest) + .get_contents() + .unwrap(); + let rest = &mut EndianSlice::new(&*buf, LittleEndian); + + let expect = Some(Abbreviation::new( + 1, + constants::DW_TAG_subprogram, + constants::DW_CHILDREN_no, + vec![AttributeSpecification::new( + constants::DW_AT_name, + constants::DW_FORM_string, + None, + )] + .into(), + )); + + let abbrev = Abbreviation::parse(rest).expect("Should parse abbreviation"); + assert_eq!(abbrev, expect); + assert_eq!(*rest, EndianSlice::new(&expected_rest, LittleEndian)); + } + + #[test] + fn test_parse_abbreviation_implicit_const_ok() { + let expected_rest = [0x01, 0x02, 0x03, 0x04]; + let buf = Section::new() + .abbrev(1, constants::DW_TAG_subprogram, constants::DW_CHILDREN_no) + .abbrev_attr_implicit_const(constants::DW_AT_name, -42) + .abbrev_attr_null() + .append_bytes(&expected_rest) + .get_contents() + .unwrap(); + let rest = &mut EndianSlice::new(&*buf, LittleEndian); + + let expect = Some(Abbreviation::new( + 1, + constants::DW_TAG_subprogram, + constants::DW_CHILDREN_no, + vec![AttributeSpecification::new( + constants::DW_AT_name, + constants::DW_FORM_implicit_const, + Some(-42), + )] + .into(), + )); + + let abbrev = Abbreviation::parse(rest).expect("Should parse abbreviation"); + assert_eq!(abbrev, expect); + assert_eq!(*rest, EndianSlice::new(&expected_rest, LittleEndian)); + } + + #[test] + fn test_parse_abbreviation_implicit_const_no_const() { + let buf = Section::new() + .abbrev(1, constants::DW_TAG_subprogram, constants::DW_CHILDREN_no) + .abbrev_attr(constants::DW_AT_name, constants::DW_FORM_implicit_const) + .get_contents() + .unwrap(); + let buf = &mut EndianSlice::new(&*buf, LittleEndian); + + match Abbreviation::parse(buf) { + Err(Error::UnexpectedEof(_)) => {} + otherwise => panic!("Unexpected result: {:?}", otherwise), + } + } + + #[test] + fn test_parse_null_abbreviation_ok() { + let expected_rest = [0x01, 0x02, 0x03, 0x04]; + let buf = Section::new() + .abbrev_null() + .append_bytes(&expected_rest) + .get_contents() + .unwrap(); + let rest = &mut EndianSlice::new(&*buf, LittleEndian); + + let abbrev = Abbreviation::parse(rest).expect("Should parse null abbreviation"); + assert!(abbrev.is_none()); + assert_eq!(*rest, EndianSlice::new(&expected_rest, LittleEndian)); + } + + #[test] + fn test_parse_attribute_form_ok() { + let buf = [0x01, 0x02]; + let rest = &mut EndianSlice::new(&buf, LittleEndian); + let tag = AttributeSpecification::parse_form(rest).expect("Should parse form"); + assert_eq!(tag, constants::DW_FORM_addr); + assert_eq!(*rest, EndianSlice::new(&buf[1..], LittleEndian)); + } + + #[test] + fn test_parse_attribute_form_zero() { + let buf = [0x00]; + let buf = &mut EndianSlice::new(&buf, LittleEndian); + match AttributeSpecification::parse_form(buf) { + Err(Error::AttributeFormZero) => {} + otherwise => panic!("Unexpected result: {:?}", otherwise), + }; + } + + #[test] + fn test_parse_null_attribute_specification_ok() { + let buf = [0x00, 0x00, 0x01]; + let rest = &mut EndianSlice::new(&buf, LittleEndian); + let attr = + AttributeSpecification::parse(rest).expect("Should parse null attribute specification"); + assert!(attr.is_none()); + assert_eq!(*rest, EndianSlice::new(&buf[2..], LittleEndian)); + } + + #[test] + fn test_parse_attribute_specifications_name_zero() { + let buf = [0x00, 0x01, 0x00, 0x00]; + let buf = &mut EndianSlice::new(&buf, LittleEndian); + match AttributeSpecification::parse(buf) { + Err(Error::ExpectedZero) => {} + otherwise => panic!("Unexpected result: {:?}", otherwise), + }; + } + + #[test] + fn test_parse_attribute_specifications_form_zero() { + let buf = [0x01, 0x00, 0x00, 0x00]; + let buf = &mut EndianSlice::new(&buf, LittleEndian); + match AttributeSpecification::parse(buf) { + Err(Error::AttributeFormZero) => {} + otherwise => panic!("Unexpected result: {:?}", otherwise), + }; + } + + #[test] + fn test_get_abbrev_zero() { + let mut abbrevs = Abbreviations::empty(); + abbrevs + .insert(Abbreviation::new( + 1, + constants::DwTag(1), + constants::DW_CHILDREN_no, + vec![].into(), + )) + .unwrap(); + assert!(abbrevs.get(0).is_none()); + } +} diff --git a/vendor/gimli/src/read/addr.rs b/vendor/gimli/src/read/addr.rs new file mode 100644 index 0000000..593f9fe --- /dev/null +++ b/vendor/gimli/src/read/addr.rs @@ -0,0 +1,128 @@ +use crate::common::{DebugAddrBase, DebugAddrIndex, SectionId}; +use crate::read::{Reader, ReaderOffset, Result, Section}; + +/// The raw contents of the `.debug_addr` section. +#[derive(Debug, Default, Clone, Copy)] +pub struct DebugAddr<R> { + section: R, +} + +impl<R: Reader> DebugAddr<R> { + // TODO: add an iterator over the sets of addresses in the section. + // This is not needed for common usage of the section though. + + /// Returns the address at the given `base` and `index`. + /// + /// A set of addresses in the `.debug_addr` section consists of a header + /// followed by a series of addresses. + /// + /// The `base` must be the `DW_AT_addr_base` value from the compilation unit DIE. + /// This is an offset that points to the first address following the header. + /// + /// The `index` is the value of a `DW_FORM_addrx` attribute. + /// + /// The `address_size` must be the size of the address for the compilation unit. + /// This value must also match the header. However, note that we do not parse the + /// header to validate this, since locating the header is unreliable, and the GNU + /// extensions do not emit it. + pub fn get_address( + &self, + address_size: u8, + base: DebugAddrBase<R::Offset>, + index: DebugAddrIndex<R::Offset>, + ) -> Result<u64> { + let input = &mut self.section.clone(); + input.skip(base.0)?; + input.skip(R::Offset::from_u64( + index.0.into_u64() * u64::from(address_size), + )?)?; + input.read_address(address_size) + } +} + +impl<T> DebugAddr<T> { + /// Create a `DebugAddr` section that references the data in `self`. + /// + /// This is useful when `R` implements `Reader` but `T` does not. + /// + /// ## Example Usage + /// + /// ```rust,no_run + /// # let load_section = || unimplemented!(); + /// // Read the DWARF section into a `Vec` with whatever object loader you're using. + /// let owned_section: gimli::DebugAddr<Vec<u8>> = load_section(); + /// // Create a reference to the DWARF section. + /// let section = owned_section.borrow(|section| { + /// gimli::EndianSlice::new(§ion, gimli::LittleEndian) + /// }); + /// ``` + pub fn borrow<'a, F, R>(&'a self, mut borrow: F) -> DebugAddr<R> + where + F: FnMut(&'a T) -> R, + { + borrow(&self.section).into() + } +} + +impl<R> Section<R> for DebugAddr<R> { + fn id() -> SectionId { + SectionId::DebugAddr + } + + fn reader(&self) -> &R { + &self.section + } +} + +impl<R> From<R> for DebugAddr<R> { + fn from(section: R) -> Self { + DebugAddr { section } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::read::EndianSlice; + use crate::test_util::GimliSectionMethods; + use crate::{Format, LittleEndian}; + use test_assembler::{Endian, Label, LabelMaker, Section}; + + #[test] + fn test_get_address() { + for format in vec![Format::Dwarf32, Format::Dwarf64] { + for address_size in vec![4, 8] { + let zero = Label::new(); + let length = Label::new(); + let start = Label::new(); + let first = Label::new(); + let end = Label::new(); + let mut section = Section::with_endian(Endian::Little) + .mark(&zero) + .initial_length(format, &length, &start) + .D16(5) + .D8(address_size) + .D8(0) + .mark(&first); + for i in 0..20 { + section = section.word(address_size, 1000 + i); + } + section = section.mark(&end); + length.set_const((&end - &start) as u64); + + let section = section.get_contents().unwrap(); + let debug_addr = DebugAddr::from(EndianSlice::new(§ion, LittleEndian)); + let base = DebugAddrBase((&first - &zero) as usize); + + assert_eq!( + debug_addr.get_address(address_size, base, DebugAddrIndex(0)), + Ok(1000) + ); + assert_eq!( + debug_addr.get_address(address_size, base, DebugAddrIndex(19)), + Ok(1019) + ); + } + } + } +} diff --git a/vendor/gimli/src/read/aranges.rs b/vendor/gimli/src/read/aranges.rs new file mode 100644 index 0000000..83159b6 --- /dev/null +++ b/vendor/gimli/src/read/aranges.rs @@ -0,0 +1,660 @@ +use crate::common::{DebugArangesOffset, DebugInfoOffset, Encoding, SectionId}; +use crate::endianity::Endianity; +use crate::read::{EndianSlice, Error, Range, Reader, ReaderOffset, Result, Section}; + +/// The `DebugAranges` struct represents the DWARF address range information +/// found in the `.debug_aranges` section. +#[derive(Debug, Default, Clone, Copy)] +pub struct DebugAranges<R> { + section: R, +} + +impl<'input, Endian> DebugAranges<EndianSlice<'input, Endian>> +where + Endian: Endianity, +{ + /// Construct a new `DebugAranges` instance from the data in the `.debug_aranges` + /// section. + /// + /// It is the caller's responsibility to read the `.debug_aranges` section and + /// present it as a `&[u8]` slice. That means using some ELF loader on + /// Linux, a Mach-O loader on macOS, etc. + /// + /// ``` + /// use gimli::{DebugAranges, LittleEndian}; + /// + /// # let buf = []; + /// # let read_debug_aranges_section = || &buf; + /// let debug_aranges = + /// DebugAranges::new(read_debug_aranges_section(), LittleEndian); + /// ``` + pub fn new(section: &'input [u8], endian: Endian) -> Self { + DebugAranges { + section: EndianSlice::new(section, endian), + } + } +} + +impl<R: Reader> DebugAranges<R> { + /// Iterate the sets of entries in the `.debug_aranges` section. + /// + /// Each set of entries belongs to a single unit. + pub fn headers(&self) -> ArangeHeaderIter<R> { + ArangeHeaderIter { + input: self.section.clone(), + offset: DebugArangesOffset(R::Offset::from_u8(0)), + } + } + + /// Get the header at the given offset. + pub fn header(&self, offset: DebugArangesOffset<R::Offset>) -> Result<ArangeHeader<R>> { + let mut input = self.section.clone(); + input.skip(offset.0)?; + ArangeHeader::parse(&mut input, offset) + } +} + +impl<T> DebugAranges<T> { + /// Create a `DebugAranges` section that references the data in `self`. + /// + /// This is useful when `R` implements `Reader` but `T` does not. + /// + /// ## Example Usage + /// + /// ```rust,no_run + /// # let load_section = || unimplemented!(); + /// // Read the DWARF section into a `Vec` with whatever object loader you're using. + /// let owned_section: gimli::DebugAranges<Vec<u8>> = load_section(); + /// // Create a reference to the DWARF section. + /// let section = owned_section.borrow(|section| { + /// gimli::EndianSlice::new(§ion, gimli::LittleEndian) + /// }); + /// ``` + pub fn borrow<'a, F, R>(&'a self, mut borrow: F) -> DebugAranges<R> + where + F: FnMut(&'a T) -> R, + { + borrow(&self.section).into() + } +} + +impl<R> Section<R> for DebugAranges<R> { + fn id() -> SectionId { + SectionId::DebugAranges + } + + fn reader(&self) -> &R { + &self.section + } +} + +impl<R> From<R> for DebugAranges<R> { + fn from(section: R) -> Self { + DebugAranges { section } + } +} + +/// An iterator over the headers of a `.debug_aranges` section. +#[derive(Clone, Debug)] +pub struct ArangeHeaderIter<R: Reader> { + input: R, + offset: DebugArangesOffset<R::Offset>, +} + +impl<R: Reader> ArangeHeaderIter<R> { + /// Advance the iterator to the next header. + pub fn next(&mut self) -> Result<Option<ArangeHeader<R>>> { + if self.input.is_empty() { + return Ok(None); + } + + let len = self.input.len(); + match ArangeHeader::parse(&mut self.input, self.offset) { + Ok(header) => { + self.offset.0 += len - self.input.len(); + Ok(Some(header)) + } + Err(e) => { + self.input.empty(); + Err(e) + } + } + } +} + +#[cfg(feature = "fallible-iterator")] +impl<R: Reader> fallible_iterator::FallibleIterator for ArangeHeaderIter<R> { + type Item = ArangeHeader<R>; + type Error = Error; + + fn next(&mut self) -> ::core::result::Result<Option<Self::Item>, Self::Error> { + ArangeHeaderIter::next(self) + } +} + +/// A header for a set of entries in the `.debug_arange` section. +/// +/// These entries all belong to a single unit. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ArangeHeader<R, Offset = <R as Reader>::Offset> +where + R: Reader<Offset = Offset>, + Offset: ReaderOffset, +{ + offset: DebugArangesOffset<Offset>, + encoding: Encoding, + length: Offset, + debug_info_offset: DebugInfoOffset<Offset>, + segment_size: u8, + entries: R, +} + +impl<R, Offset> ArangeHeader<R, Offset> +where + R: Reader<Offset = Offset>, + Offset: ReaderOffset, +{ + fn parse(input: &mut R, offset: DebugArangesOffset<Offset>) -> Result<Self> { + let (length, format) = input.read_initial_length()?; + let mut rest = input.split(length)?; + + // Check the version. The DWARF 5 spec says that this is always 2, but version 3 + // has been observed in the wild, potentially due to a bug; see + // https://github.com/gimli-rs/gimli/issues/559 for more information. + // lldb allows versions 2 through 5, possibly by mistake. + let version = rest.read_u16()?; + if version != 2 && version != 3 { + return Err(Error::UnknownVersion(u64::from(version))); + } + + let debug_info_offset = rest.read_offset(format).map(DebugInfoOffset)?; + let address_size = rest.read_u8()?; + let segment_size = rest.read_u8()?; + + // unit_length + version + offset + address_size + segment_size + let header_length = format.initial_length_size() + 2 + format.word_size() + 1 + 1; + + // The first tuple following the header in each set begins at an offset that is + // a multiple of the size of a single tuple (that is, the size of a segment selector + // plus twice the size of an address). + let tuple_length = address_size + .checked_mul(2) + .and_then(|x| x.checked_add(segment_size)) + .ok_or(Error::InvalidAddressRange)?; + if tuple_length == 0 { + return Err(Error::InvalidAddressRange)?; + } + let padding = if header_length % tuple_length == 0 { + 0 + } else { + tuple_length - header_length % tuple_length + }; + rest.skip(R::Offset::from_u8(padding))?; + + let encoding = Encoding { + format, + version, + address_size, + // TODO: segment_size + }; + Ok(ArangeHeader { + offset, + encoding, + length, + debug_info_offset, + segment_size, + entries: rest, + }) + } + + /// Return the offset of this header within the `.debug_aranges` section. + #[inline] + pub fn offset(&self) -> DebugArangesOffset<Offset> { + self.offset + } + + /// Return the length of this set of entries, including the header. + #[inline] + pub fn length(&self) -> Offset { + self.length + } + + /// Return the encoding parameters for this set of entries. + #[inline] + pub fn encoding(&self) -> Encoding { + self.encoding + } + + /// Return the segment size for this set of entries. + #[inline] + pub fn segment_size(&self) -> u8 { + self.segment_size + } + + /// Return the offset into the .debug_info section for this set of arange entries. + #[inline] + pub fn debug_info_offset(&self) -> DebugInfoOffset<Offset> { + self.debug_info_offset + } + + /// Return the arange entries in this set. + #[inline] + pub fn entries(&self) -> ArangeEntryIter<R> { + ArangeEntryIter { + input: self.entries.clone(), + encoding: self.encoding, + segment_size: self.segment_size, + } + } +} + +/// An iterator over the aranges from a `.debug_aranges` section. +/// +/// Can be [used with +/// `FallibleIterator`](./index.html#using-with-fallibleiterator). +#[derive(Debug, Clone)] +pub struct ArangeEntryIter<R: Reader> { + input: R, + encoding: Encoding, + segment_size: u8, +} + +impl<R: Reader> ArangeEntryIter<R> { + /// Advance the iterator and return the next arange. + /// + /// Returns the newly parsed arange as `Ok(Some(arange))`. Returns `Ok(None)` + /// when iteration is complete and all aranges have already been parsed and + /// yielded. If an error occurs while parsing the next arange, then this error + /// is returned as `Err(e)`, and all subsequent calls return `Ok(None)`. + pub fn next(&mut self) -> Result<Option<ArangeEntry>> { + if self.input.is_empty() { + return Ok(None); + } + + match ArangeEntry::parse(&mut self.input, self.encoding, self.segment_size) { + Ok(Some(entry)) => Ok(Some(entry)), + Ok(None) => { + self.input.empty(); + Ok(None) + } + Err(e) => { + self.input.empty(); + Err(e) + } + } + } +} + +#[cfg(feature = "fallible-iterator")] +impl<R: Reader> fallible_iterator::FallibleIterator for ArangeEntryIter<R> { + type Item = ArangeEntry; + type Error = Error; + + fn next(&mut self) -> ::core::result::Result<Option<Self::Item>, Self::Error> { + ArangeEntryIter::next(self) + } +} + +/// A single parsed arange. +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] +pub struct ArangeEntry { + segment: Option<u64>, + address: u64, + length: u64, +} + +impl ArangeEntry { + /// Parse a single arange. Return `None` for the null arange, `Some` for an actual arange. + fn parse<R: Reader>( + input: &mut R, + encoding: Encoding, + segment_size: u8, + ) -> Result<Option<Self>> { + let address_size = encoding.address_size; + + let tuple_length = R::Offset::from_u8(2 * address_size + segment_size); + if tuple_length > input.len() { + input.empty(); + return Ok(None); + } + + let segment = if segment_size != 0 { + input.read_address(segment_size)? + } else { + 0 + }; + let address = input.read_address(address_size)?; + let length = input.read_address(address_size)?; + + match (segment, address, length) { + // This is meant to be a null terminator, but in practice it can occur + // before the end, possibly due to a linker omitting a function and + // leaving an unrelocated entry. + (0, 0, 0) => Self::parse(input, encoding, segment_size), + _ => Ok(Some(ArangeEntry { + segment: if segment_size != 0 { + Some(segment) + } else { + None + }, + address, + length, + })), + } + } + + /// Return the segment selector of this arange. + #[inline] + pub fn segment(&self) -> Option<u64> { + self.segment + } + + /// Return the beginning address of this arange. + #[inline] + pub fn address(&self) -> u64 { + self.address + } + + /// Return the length of this arange. + #[inline] + pub fn length(&self) -> u64 { + self.length + } + + /// Return the range. + #[inline] + pub fn range(&self) -> Range { + Range { + begin: self.address, + end: self.address.wrapping_add(self.length), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::common::{DebugInfoOffset, Format}; + use crate::endianity::LittleEndian; + use crate::read::EndianSlice; + + #[test] + fn test_iterate_headers() { + #[rustfmt::skip] + let buf = [ + // 32-bit length = 28. + 0x1c, 0x00, 0x00, 0x00, + // Version. + 0x02, 0x00, + // Offset. + 0x01, 0x02, 0x03, 0x04, + // Address size. + 0x04, + // Segment size. + 0x00, + // Dummy padding and arange tuples. + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // 32-bit length = 36. + 0x24, 0x00, 0x00, 0x00, + // Version. + 0x02, 0x00, + // Offset. + 0x11, 0x12, 0x13, 0x14, + // Address size. + 0x04, + // Segment size. + 0x00, + // Dummy padding and arange tuples. + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ]; + + let debug_aranges = DebugAranges::new(&buf, LittleEndian); + let mut headers = debug_aranges.headers(); + + let header = headers + .next() + .expect("should parse header ok") + .expect("should have a header"); + assert_eq!(header.offset(), DebugArangesOffset(0)); + assert_eq!(header.debug_info_offset(), DebugInfoOffset(0x0403_0201)); + + let header = headers + .next() + .expect("should parse header ok") + .expect("should have a header"); + assert_eq!(header.offset(), DebugArangesOffset(0x20)); + assert_eq!(header.debug_info_offset(), DebugInfoOffset(0x1413_1211)); + } + + #[test] + fn test_parse_header_ok() { + #[rustfmt::skip] + let buf = [ + // 32-bit length = 32. + 0x20, 0x00, 0x00, 0x00, + // Version. + 0x02, 0x00, + // Offset. + 0x01, 0x02, 0x03, 0x04, + // Address size. + 0x08, + // Segment size. + 0x04, + // Length to here = 12, tuple length = 20. + // Padding to tuple length multiple = 4. + 0x10, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + // Dummy arange tuple data. + 0x20, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + // Dummy next arange. + 0x30, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + ]; + + let rest = &mut EndianSlice::new(&buf, LittleEndian); + + let header = + ArangeHeader::parse(rest, DebugArangesOffset(0x10)).expect("should parse header ok"); + + assert_eq!( + *rest, + EndianSlice::new(&buf[buf.len() - 16..], LittleEndian) + ); + assert_eq!( + header, + ArangeHeader { + offset: DebugArangesOffset(0x10), + encoding: Encoding { + format: Format::Dwarf32, + version: 2, + address_size: 8, + }, + length: 0x20, + debug_info_offset: DebugInfoOffset(0x0403_0201), + segment_size: 4, + entries: EndianSlice::new(&buf[buf.len() - 32..buf.len() - 16], LittleEndian), + } + ); + } + + #[test] + fn test_parse_header_overflow_error() { + #[rustfmt::skip] + let buf = [ + // 32-bit length = 32. + 0x20, 0x00, 0x00, 0x00, + // Version. + 0x02, 0x00, + // Offset. + 0x01, 0x02, 0x03, 0x04, + // Address size. + 0xff, + // Segment size. + 0xff, + // Length to here = 12, tuple length = 20. + // Padding to tuple length multiple = 4. + 0x10, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + // Dummy arange tuple data. + 0x20, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + // Dummy next arange. + 0x30, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + ]; + + let rest = &mut EndianSlice::new(&buf, LittleEndian); + + let error = ArangeHeader::parse(rest, DebugArangesOffset(0x10)) + .expect_err("should fail to parse header"); + assert_eq!(error, Error::InvalidAddressRange); + } + + #[test] + fn test_parse_header_div_by_zero_error() { + #[rustfmt::skip] + let buf = [ + // 32-bit length = 32. + 0x20, 0x00, 0x00, 0x00, + // Version. + 0x02, 0x00, + // Offset. + 0x01, 0x02, 0x03, 0x04, + // Address size = 0. Could cause a division by zero if we aren't + // careful. + 0x00, + // Segment size. + 0x00, + // Length to here = 12, tuple length = 20. + // Padding to tuple length multiple = 4. + 0x10, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + // Dummy arange tuple data. + 0x20, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + // Dummy next arange. + 0x30, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + ]; + + let rest = &mut EndianSlice::new(&buf, LittleEndian); + + let error = ArangeHeader::parse(rest, DebugArangesOffset(0x10)) + .expect_err("should fail to parse header"); + assert_eq!(error, Error::InvalidAddressRange); + } + + #[test] + fn test_parse_entry_ok() { + let encoding = Encoding { + format: Format::Dwarf32, + version: 2, + address_size: 4, + }; + let segment_size = 0; + let buf = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09]; + let rest = &mut EndianSlice::new(&buf, LittleEndian); + let entry = + ArangeEntry::parse(rest, encoding, segment_size).expect("should parse entry ok"); + assert_eq!(*rest, EndianSlice::new(&buf[buf.len() - 1..], LittleEndian)); + assert_eq!( + entry, + Some(ArangeEntry { + segment: None, + address: 0x0403_0201, + length: 0x0807_0605, + }) + ); + } + + #[test] + fn test_parse_entry_segment() { + let encoding = Encoding { + format: Format::Dwarf32, + version: 2, + address_size: 4, + }; + let segment_size = 8; + #[rustfmt::skip] + let buf = [ + // Segment. + 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, + // Address. + 0x01, 0x02, 0x03, 0x04, + // Length. + 0x05, 0x06, 0x07, 0x08, + // Next tuple. + 0x09 + ]; + let rest = &mut EndianSlice::new(&buf, LittleEndian); + let entry = + ArangeEntry::parse(rest, encoding, segment_size).expect("should parse entry ok"); + assert_eq!(*rest, EndianSlice::new(&buf[buf.len() - 1..], LittleEndian)); + assert_eq!( + entry, + Some(ArangeEntry { + segment: Some(0x1817_1615_1413_1211), + address: 0x0403_0201, + length: 0x0807_0605, + }) + ); + } + + #[test] + fn test_parse_entry_zero() { + let encoding = Encoding { + format: Format::Dwarf32, + version: 2, + address_size: 4, + }; + let segment_size = 0; + #[rustfmt::skip] + let buf = [ + // Zero tuple. + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + // Address. + 0x01, 0x02, 0x03, 0x04, + // Length. + 0x05, 0x06, 0x07, 0x08, + // Next tuple. + 0x09 + ]; + let rest = &mut EndianSlice::new(&buf, LittleEndian); + let entry = + ArangeEntry::parse(rest, encoding, segment_size).expect("should parse entry ok"); + assert_eq!(*rest, EndianSlice::new(&buf[buf.len() - 1..], LittleEndian)); + assert_eq!( + entry, + Some(ArangeEntry { + segment: None, + address: 0x0403_0201, + length: 0x0807_0605, + }) + ); + } +} diff --git a/vendor/gimli/src/read/cfi.rs b/vendor/gimli/src/read/cfi.rs new file mode 100644 index 0000000..d92c8b2 --- /dev/null +++ b/vendor/gimli/src/read/cfi.rs @@ -0,0 +1,7823 @@ +#[cfg(feature = "read")] +use alloc::boxed::Box; + +use core::cmp::{Ord, Ordering}; +use core::fmt::{self, Debug}; +use core::iter::FromIterator; +use core::mem; +use core::num::Wrapping; + +use super::util::{ArrayLike, ArrayVec}; +use crate::common::{ + DebugFrameOffset, EhFrameOffset, Encoding, Format, Register, SectionId, Vendor, +}; +use crate::constants::{self, DwEhPe}; +use crate::endianity::Endianity; +use crate::read::{ + EndianSlice, Error, Expression, Reader, ReaderOffset, Result, Section, StoreOnHeap, +}; + +/// `DebugFrame` contains the `.debug_frame` section's frame unwinding +/// information required to unwind to and recover registers from older frames on +/// the stack. For example, this is useful for a debugger that wants to print +/// locals in a backtrace. +/// +/// Most interesting methods are defined in the +/// [`UnwindSection`](trait.UnwindSection.html) trait. +/// +/// ### Differences between `.debug_frame` and `.eh_frame` +/// +/// While the `.debug_frame` section's information has a lot of overlap with the +/// `.eh_frame` section's information, the `.eh_frame` information tends to only +/// encode the subset of information needed for exception handling. Often, only +/// one of `.eh_frame` or `.debug_frame` will be present in an object file. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct DebugFrame<R: Reader> { + section: R, + address_size: u8, + segment_size: u8, + vendor: Vendor, +} + +impl<R: Reader> DebugFrame<R> { + /// Set the size of a target address in bytes. + /// + /// This defaults to the native word size. + /// This is only used if the CIE version is less than 4. + pub fn set_address_size(&mut self, address_size: u8) { + self.address_size = address_size + } + + /// Set the size of a segment selector in bytes. + /// + /// This defaults to 0. + /// This is only used if the CIE version is less than 4. + pub fn set_segment_size(&mut self, segment_size: u8) { + self.segment_size = segment_size + } + + /// Set the vendor extensions to use. + /// + /// This defaults to `Vendor::Default`. + pub fn set_vendor(&mut self, vendor: Vendor) { + self.vendor = vendor; + } +} + +impl<'input, Endian> DebugFrame<EndianSlice<'input, Endian>> +where + Endian: Endianity, +{ + /// Construct a new `DebugFrame` instance from the data in the + /// `.debug_frame` section. + /// + /// It is the caller's responsibility to read the section and present it as + /// a `&[u8]` slice. That means using some ELF loader on Linux, a Mach-O + /// loader on macOS, etc. + /// + /// ``` + /// use gimli::{DebugFrame, NativeEndian}; + /// + /// // Use with `.debug_frame` + /// # let buf = [0x00, 0x01, 0x02, 0x03]; + /// # let read_debug_frame_section_somehow = || &buf; + /// let debug_frame = DebugFrame::new(read_debug_frame_section_somehow(), NativeEndian); + /// ``` + pub fn new(section: &'input [u8], endian: Endian) -> Self { + Self::from(EndianSlice::new(section, endian)) + } +} + +impl<R: Reader> Section<R> for DebugFrame<R> { + fn id() -> SectionId { + SectionId::DebugFrame + } + + fn reader(&self) -> &R { + &self.section + } +} + +impl<R: Reader> From<R> for DebugFrame<R> { + fn from(section: R) -> Self { + // Default to no segments and native word size. + DebugFrame { + section, + address_size: mem::size_of::<usize>() as u8, + segment_size: 0, + vendor: Vendor::Default, + } + } +} + +/// `EhFrameHdr` contains the information about the `.eh_frame_hdr` section. +/// +/// A pointer to the start of the `.eh_frame` data, and optionally, a binary +/// search table of pointers to the `.eh_frame` records that are found in this section. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct EhFrameHdr<R: Reader>(R); + +/// `ParsedEhFrameHdr` contains the parsed information from the `.eh_frame_hdr` section. +#[derive(Clone, Debug)] +pub struct ParsedEhFrameHdr<R: Reader> { + address_size: u8, + section: R, + + eh_frame_ptr: Pointer, + fde_count: u64, + table_enc: DwEhPe, + table: R, +} + +impl<'input, Endian> EhFrameHdr<EndianSlice<'input, Endian>> +where + Endian: Endianity, +{ + /// Constructs a new `EhFrameHdr` instance from the data in the `.eh_frame_hdr` section. + pub fn new(section: &'input [u8], endian: Endian) -> Self { + Self::from(EndianSlice::new(section, endian)) + } +} + +impl<R: Reader> EhFrameHdr<R> { + /// Parses this `EhFrameHdr` to a `ParsedEhFrameHdr`. + pub fn parse(&self, bases: &BaseAddresses, address_size: u8) -> Result<ParsedEhFrameHdr<R>> { + let mut reader = self.0.clone(); + let version = reader.read_u8()?; + if version != 1 { + return Err(Error::UnknownVersion(u64::from(version))); + } + + let eh_frame_ptr_enc = parse_pointer_encoding(&mut reader)?; + let fde_count_enc = parse_pointer_encoding(&mut reader)?; + let table_enc = parse_pointer_encoding(&mut reader)?; + + let parameters = PointerEncodingParameters { + bases: &bases.eh_frame_hdr, + func_base: None, + address_size, + section: &self.0, + }; + + // Omitting this pointer is not valid (defeats the purpose of .eh_frame_hdr entirely) + if eh_frame_ptr_enc == constants::DW_EH_PE_omit { + return Err(Error::CannotParseOmitPointerEncoding); + } + let eh_frame_ptr = parse_encoded_pointer(eh_frame_ptr_enc, ¶meters, &mut reader)?; + + let fde_count; + if fde_count_enc == constants::DW_EH_PE_omit || table_enc == constants::DW_EH_PE_omit { + fde_count = 0 + } else { + fde_count = parse_encoded_pointer(fde_count_enc, ¶meters, &mut reader)?.direct()?; + } + + Ok(ParsedEhFrameHdr { + address_size, + section: self.0.clone(), + + eh_frame_ptr, + fde_count, + table_enc, + table: reader, + }) + } +} + +impl<R: Reader> Section<R> for EhFrameHdr<R> { + fn id() -> SectionId { + SectionId::EhFrameHdr + } + + fn reader(&self) -> &R { + &self.0 + } +} + +impl<R: Reader> From<R> for EhFrameHdr<R> { + fn from(section: R) -> Self { + EhFrameHdr(section) + } +} + +impl<R: Reader> ParsedEhFrameHdr<R> { + /// Returns the address of the binary's `.eh_frame` section. + pub fn eh_frame_ptr(&self) -> Pointer { + self.eh_frame_ptr + } + + /// Retrieves the CFI binary search table, if there is one. + pub fn table(&self) -> Option<EhHdrTable<R>> { + // There are two big edge cases here: + // * You search the table for an invalid address. As this is just a binary + // search table, we always have to return a valid result for that (unless + // you specify an address that is lower than the first address in the + // table). Since this means that you have to recheck that the FDE contains + // your address anyways, we just return the first FDE even when the address + // is too low. After all, we're just doing a normal binary search. + // * This falls apart when the table is empty - there is no entry we could + // return. We conclude that an empty table is not really a table at all. + if self.fde_count == 0 { + None + } else { + Some(EhHdrTable { hdr: self }) + } + } +} + +/// An iterator for `.eh_frame_hdr` section's binary search table. +/// +/// Each table entry consists of a tuple containing an `initial_location` and `address`. +/// The `initial location` represents the first address that the targeted FDE +/// is able to decode. The `address` is the address of the FDE in the `.eh_frame` section. +/// The `address` can be converted with `EhHdrTable::pointer_to_offset` and `EhFrame::fde_from_offset` to an FDE. +#[derive(Debug)] +pub struct EhHdrTableIter<'a, 'bases, R: Reader> { + hdr: &'a ParsedEhFrameHdr<R>, + table: R, + bases: &'bases BaseAddresses, + remain: u64, +} + +impl<'a, 'bases, R: Reader> EhHdrTableIter<'a, 'bases, R> { + /// Yield the next entry in the `EhHdrTableIter`. + pub fn next(&mut self) -> Result<Option<(Pointer, Pointer)>> { + if self.remain == 0 { + return Ok(None); + } + + let parameters = PointerEncodingParameters { + bases: &self.bases.eh_frame_hdr, + func_base: None, + address_size: self.hdr.address_size, + section: &self.hdr.section, + }; + + self.remain -= 1; + let from = parse_encoded_pointer(self.hdr.table_enc, ¶meters, &mut self.table)?; + let to = parse_encoded_pointer(self.hdr.table_enc, ¶meters, &mut self.table)?; + Ok(Some((from, to))) + } + /// Yield the nth entry in the `EhHdrTableIter` + pub fn nth(&mut self, n: usize) -> Result<Option<(Pointer, Pointer)>> { + use core::convert::TryFrom; + let size = match self.hdr.table_enc.format() { + constants::DW_EH_PE_uleb128 | constants::DW_EH_PE_sleb128 => { + return Err(Error::VariableLengthSearchTable); + } + constants::DW_EH_PE_sdata2 | constants::DW_EH_PE_udata2 => 2, + constants::DW_EH_PE_sdata4 | constants::DW_EH_PE_udata4 => 4, + constants::DW_EH_PE_sdata8 | constants::DW_EH_PE_udata8 => 8, + _ => return Err(Error::UnknownPointerEncoding), + }; + + let row_size = size * 2; + let n = u64::try_from(n).map_err(|_| Error::UnsupportedOffset)?; + self.remain = self.remain.saturating_sub(n); + self.table.skip(R::Offset::from_u64(n * row_size)?)?; + self.next() + } +} + +#[cfg(feature = "fallible-iterator")] +impl<'a, 'bases, R: Reader> fallible_iterator::FallibleIterator for EhHdrTableIter<'a, 'bases, R> { + type Item = (Pointer, Pointer); + type Error = Error; + fn next(&mut self) -> Result<Option<Self::Item>> { + EhHdrTableIter::next(self) + } + + fn size_hint(&self) -> (usize, Option<usize>) { + use core::convert::TryInto; + ( + self.remain.try_into().unwrap_or(0), + self.remain.try_into().ok(), + ) + } + + fn nth(&mut self, n: usize) -> Result<Option<Self::Item>> { + EhHdrTableIter::nth(self, n) + } +} + +/// The CFI binary search table that is an optional part of the `.eh_frame_hdr` section. +#[derive(Debug, Clone)] +pub struct EhHdrTable<'a, R: Reader> { + hdr: &'a ParsedEhFrameHdr<R>, +} + +impl<'a, R: Reader + 'a> EhHdrTable<'a, R> { + /// Return an iterator that can walk the `.eh_frame_hdr` table. + /// + /// Each table entry consists of a tuple containing an `initial_location` and `address`. + /// The `initial location` represents the first address that the targeted FDE + /// is able to decode. The `address` is the address of the FDE in the `.eh_frame` section. + /// The `address` can be converted with `EhHdrTable::pointer_to_offset` and `EhFrame::fde_from_offset` to an FDE. + pub fn iter<'bases>(&self, bases: &'bases BaseAddresses) -> EhHdrTableIter<'_, 'bases, R> { + EhHdrTableIter { + hdr: self.hdr, + bases, + remain: self.hdr.fde_count, + table: self.hdr.table.clone(), + } + } + /// *Probably* returns a pointer to the FDE for the given address. + /// + /// This performs a binary search, so if there is no FDE for the given address, + /// this function **will** return a pointer to any other FDE that's close by. + /// + /// To be sure, you **must** call `contains` on the FDE. + pub fn lookup(&self, address: u64, bases: &BaseAddresses) -> Result<Pointer> { + let size = match self.hdr.table_enc.format() { + constants::DW_EH_PE_uleb128 | constants::DW_EH_PE_sleb128 => { + return Err(Error::VariableLengthSearchTable); + } + constants::DW_EH_PE_sdata2 | constants::DW_EH_PE_udata2 => 2, + constants::DW_EH_PE_sdata4 | constants::DW_EH_PE_udata4 => 4, + constants::DW_EH_PE_sdata8 | constants::DW_EH_PE_udata8 => 8, + _ => return Err(Error::UnknownPointerEncoding), + }; + + let row_size = size * 2; + + let mut len = self.hdr.fde_count; + + let mut reader = self.hdr.table.clone(); + + let parameters = PointerEncodingParameters { + bases: &bases.eh_frame_hdr, + func_base: None, + address_size: self.hdr.address_size, + section: &self.hdr.section, + }; + + while len > 1 { + let head = reader.split(R::Offset::from_u64((len / 2) * row_size)?)?; + let tail = reader.clone(); + + let pivot = + parse_encoded_pointer(self.hdr.table_enc, ¶meters, &mut reader)?.direct()?; + + match pivot.cmp(&address) { + Ordering::Equal => { + reader = tail; + break; + } + Ordering::Less => { + reader = tail; + len = len - (len / 2); + } + Ordering::Greater => { + reader = head; + len /= 2; + } + } + } + + reader.skip(R::Offset::from_u64(size)?)?; + + parse_encoded_pointer(self.hdr.table_enc, ¶meters, &mut reader) + } + + /// Convert a `Pointer` to a section offset. + /// + /// This does not support indirect pointers. + pub fn pointer_to_offset(&self, ptr: Pointer) -> Result<EhFrameOffset<R::Offset>> { + let ptr = ptr.direct()?; + let eh_frame_ptr = self.hdr.eh_frame_ptr().direct()?; + + // Calculate the offset in the EhFrame section + R::Offset::from_u64(ptr - eh_frame_ptr).map(EhFrameOffset) + } + + /// Returns a parsed FDE for the given address, or `NoUnwindInfoForAddress` + /// if there are none. + /// + /// You must provide a function to get its associated CIE. See + /// `PartialFrameDescriptionEntry::parse` for more information. + /// + /// # Example + /// + /// ``` + /// # use gimli::{BaseAddresses, EhFrame, ParsedEhFrameHdr, EndianSlice, NativeEndian, Error, UnwindSection}; + /// # fn foo() -> Result<(), Error> { + /// # let eh_frame: EhFrame<EndianSlice<NativeEndian>> = unreachable!(); + /// # let eh_frame_hdr: ParsedEhFrameHdr<EndianSlice<NativeEndian>> = unimplemented!(); + /// # let addr = 0; + /// # let bases = unimplemented!(); + /// let table = eh_frame_hdr.table().unwrap(); + /// let fde = table.fde_for_address(&eh_frame, &bases, addr, EhFrame::cie_from_offset)?; + /// # Ok(()) + /// # } + /// ``` + pub fn fde_for_address<F>( + &self, + frame: &EhFrame<R>, + bases: &BaseAddresses, + address: u64, + get_cie: F, + ) -> Result<FrameDescriptionEntry<R>> + where + F: FnMut( + &EhFrame<R>, + &BaseAddresses, + EhFrameOffset<R::Offset>, + ) -> Result<CommonInformationEntry<R>>, + { + let fdeptr = self.lookup(address, bases)?; + let offset = self.pointer_to_offset(fdeptr)?; + let entry = frame.fde_from_offset(bases, offset, get_cie)?; + if entry.contains(address) { + Ok(entry) + } else { + Err(Error::NoUnwindInfoForAddress) + } + } + + #[inline] + #[doc(hidden)] + #[deprecated(note = "Method renamed to fde_for_address; use that instead.")] + pub fn lookup_and_parse<F>( + &self, + address: u64, + bases: &BaseAddresses, + frame: EhFrame<R>, + get_cie: F, + ) -> Result<FrameDescriptionEntry<R>> + where + F: FnMut( + &EhFrame<R>, + &BaseAddresses, + EhFrameOffset<R::Offset>, + ) -> Result<CommonInformationEntry<R>>, + { + self.fde_for_address(&frame, bases, address, get_cie) + } + + /// Returns the frame unwind information for the given address, + /// or `NoUnwindInfoForAddress` if there are none. + /// + /// You must provide a function to get the associated CIE. See + /// `PartialFrameDescriptionEntry::parse` for more information. + pub fn unwind_info_for_address<'ctx, F, A: UnwindContextStorage<R>>( + &self, + frame: &EhFrame<R>, + bases: &BaseAddresses, + ctx: &'ctx mut UnwindContext<R, A>, + address: u64, + get_cie: F, + ) -> Result<&'ctx UnwindTableRow<R, A>> + where + F: FnMut( + &EhFrame<R>, + &BaseAddresses, + EhFrameOffset<R::Offset>, + ) -> Result<CommonInformationEntry<R>>, + { + let fde = self.fde_for_address(frame, bases, address, get_cie)?; + fde.unwind_info_for_address(frame, bases, ctx, address) + } +} + +/// `EhFrame` contains the frame unwinding information needed during exception +/// handling found in the `.eh_frame` section. +/// +/// Most interesting methods are defined in the +/// [`UnwindSection`](trait.UnwindSection.html) trait. +/// +/// See +/// [`DebugFrame`](./struct.DebugFrame.html#differences-between-debug_frame-and-eh_frame) +/// for some discussion on the differences between `.debug_frame` and +/// `.eh_frame`. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct EhFrame<R: Reader> { + section: R, + address_size: u8, + vendor: Vendor, +} + +impl<R: Reader> EhFrame<R> { + /// Set the size of a target address in bytes. + /// + /// This defaults to the native word size. + pub fn set_address_size(&mut self, address_size: u8) { + self.address_size = address_size + } + + /// Set the vendor extensions to use. + /// + /// This defaults to `Vendor::Default`. + pub fn set_vendor(&mut self, vendor: Vendor) { + self.vendor = vendor; + } +} + +impl<'input, Endian> EhFrame<EndianSlice<'input, Endian>> +where + Endian: Endianity, +{ + /// Construct a new `EhFrame` instance from the data in the + /// `.eh_frame` section. + /// + /// It is the caller's responsibility to read the section and present it as + /// a `&[u8]` slice. That means using some ELF loader on Linux, a Mach-O + /// loader on macOS, etc. + /// + /// ``` + /// use gimli::{EhFrame, EndianSlice, NativeEndian}; + /// + /// // Use with `.eh_frame` + /// # let buf = [0x00, 0x01, 0x02, 0x03]; + /// # let read_eh_frame_section_somehow = || &buf; + /// let eh_frame = EhFrame::new(read_eh_frame_section_somehow(), NativeEndian); + /// ``` + pub fn new(section: &'input [u8], endian: Endian) -> Self { + Self::from(EndianSlice::new(section, endian)) + } +} + +impl<R: Reader> Section<R> for EhFrame<R> { + fn id() -> SectionId { + SectionId::EhFrame + } + + fn reader(&self) -> &R { + &self.section + } +} + +impl<R: Reader> From<R> for EhFrame<R> { + fn from(section: R) -> Self { + // Default to native word size. + EhFrame { + section, + address_size: mem::size_of::<usize>() as u8, + vendor: Vendor::Default, + } + } +} + +// This has to be `pub` to silence a warning (that is deny(..)'d by default) in +// rustc. Eventually, not having this `pub` will become a hard error. +#[doc(hidden)] +#[allow(missing_docs)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum CieOffsetEncoding { + U32, + U64, +} + +/// An offset into an `UnwindSection`. +// +// Needed to avoid conflicting implementations of `Into<T>`. +pub trait UnwindOffset<T = usize>: Copy + Debug + Eq + From<T> +where + T: ReaderOffset, +{ + /// Convert an `UnwindOffset<T>` into a `T`. + fn into(self) -> T; +} + +impl<T> UnwindOffset<T> for DebugFrameOffset<T> +where + T: ReaderOffset, +{ + #[inline] + fn into(self) -> T { + self.0 + } +} + +impl<T> UnwindOffset<T> for EhFrameOffset<T> +where + T: ReaderOffset, +{ + #[inline] + fn into(self) -> T { + self.0 + } +} + +/// This trait completely encapsulates everything that is different between +/// `.eh_frame` and `.debug_frame`, as well as all the bits that can change +/// between DWARF versions. +#[doc(hidden)] +pub trait _UnwindSectionPrivate<R: Reader> { + /// Get the underlying section data. + fn section(&self) -> &R; + + /// Returns true if the given length value should be considered an + /// end-of-entries sentinel. + fn length_value_is_end_of_entries(length: R::Offset) -> bool; + + /// Return true if the given offset if the CIE sentinel, false otherwise. + fn is_cie(format: Format, id: u64) -> bool; + + /// Return the CIE offset/ID encoding used by this unwind section with the + /// given DWARF format. + fn cie_offset_encoding(format: Format) -> CieOffsetEncoding; + + /// For `.eh_frame`, CIE offsets are relative to the current position. For + /// `.debug_frame`, they are relative to the start of the section. We always + /// internally store them relative to the section, so we handle translating + /// `.eh_frame`'s relative offsets in this method. If the offset calculation + /// underflows, return `None`. + fn resolve_cie_offset(&self, base: R::Offset, offset: R::Offset) -> Option<R::Offset>; + + /// Does this version of this unwind section encode address and segment + /// sizes in its CIEs? + fn has_address_and_segment_sizes(version: u8) -> bool; + + /// The address size to use if `has_address_and_segment_sizes` returns false. + fn address_size(&self) -> u8; + + /// The segment size to use if `has_address_and_segment_sizes` returns false. + fn segment_size(&self) -> u8; + + /// The vendor extensions to use. + fn vendor(&self) -> Vendor; +} + +/// A section holding unwind information: either `.debug_frame` or +/// `.eh_frame`. See [`DebugFrame`](./struct.DebugFrame.html) and +/// [`EhFrame`](./struct.EhFrame.html) respectively. +pub trait UnwindSection<R: Reader>: Clone + Debug + _UnwindSectionPrivate<R> { + /// The offset type associated with this CFI section. Either + /// `DebugFrameOffset` or `EhFrameOffset`. + type Offset: UnwindOffset<R::Offset>; + + /// Iterate over the `CommonInformationEntry`s and `FrameDescriptionEntry`s + /// in this `.debug_frame` section. + /// + /// Can be [used with + /// `FallibleIterator`](./index.html#using-with-fallibleiterator). + fn entries<'bases>(&self, bases: &'bases BaseAddresses) -> CfiEntriesIter<'bases, Self, R> { + CfiEntriesIter { + section: self.clone(), + bases, + input: self.section().clone(), + } + } + + /// Parse the `CommonInformationEntry` at the given offset. + fn cie_from_offset( + &self, + bases: &BaseAddresses, + offset: Self::Offset, + ) -> Result<CommonInformationEntry<R>> { + let offset = UnwindOffset::into(offset); + let input = &mut self.section().clone(); + input.skip(offset)?; + CommonInformationEntry::parse(bases, self, input) + } + + /// Parse the `PartialFrameDescriptionEntry` at the given offset. + fn partial_fde_from_offset<'bases>( + &self, + bases: &'bases BaseAddresses, + offset: Self::Offset, + ) -> Result<PartialFrameDescriptionEntry<'bases, Self, R>> { + let offset = UnwindOffset::into(offset); + let input = &mut self.section().clone(); + input.skip(offset)?; + PartialFrameDescriptionEntry::parse_partial(self, bases, input) + } + + /// Parse the `FrameDescriptionEntry` at the given offset. + fn fde_from_offset<F>( + &self, + bases: &BaseAddresses, + offset: Self::Offset, + get_cie: F, + ) -> Result<FrameDescriptionEntry<R>> + where + F: FnMut(&Self, &BaseAddresses, Self::Offset) -> Result<CommonInformationEntry<R>>, + { + let partial = self.partial_fde_from_offset(bases, offset)?; + partial.parse(get_cie) + } + + /// Find the `FrameDescriptionEntry` for the given address. + /// + /// If found, the FDE is returned. If not found, + /// `Err(gimli::Error::NoUnwindInfoForAddress)` is returned. + /// If parsing fails, the error is returned. + /// + /// You must provide a function to get its associated CIE. See + /// `PartialFrameDescriptionEntry::parse` for more information. + /// + /// Note: this iterates over all FDEs. If available, it is possible + /// to do a binary search with `EhFrameHdr::fde_for_address` instead. + fn fde_for_address<F>( + &self, + bases: &BaseAddresses, + address: u64, + mut get_cie: F, + ) -> Result<FrameDescriptionEntry<R>> + where + F: FnMut(&Self, &BaseAddresses, Self::Offset) -> Result<CommonInformationEntry<R>>, + { + let mut entries = self.entries(bases); + while let Some(entry) = entries.next()? { + match entry { + CieOrFde::Cie(_) => {} + CieOrFde::Fde(partial) => { + let fde = partial.parse(&mut get_cie)?; + if fde.contains(address) { + return Ok(fde); + } + } + } + } + Err(Error::NoUnwindInfoForAddress) + } + + /// Find the frame unwind information for the given address. + /// + /// If found, the unwind information is returned. If not found, + /// `Err(gimli::Error::NoUnwindInfoForAddress)` is returned. If parsing or + /// CFI evaluation fails, the error is returned. + /// + /// ``` + /// use gimli::{BaseAddresses, EhFrame, EndianSlice, NativeEndian, UnwindContext, + /// UnwindSection}; + /// + /// # fn foo() -> gimli::Result<()> { + /// # let read_eh_frame_section = || unimplemented!(); + /// // Get the `.eh_frame` section from the object file. Alternatively, + /// // use `EhFrame` with the `.eh_frame` section of the object file. + /// let eh_frame = EhFrame::new(read_eh_frame_section(), NativeEndian); + /// + /// # let get_frame_pc = || unimplemented!(); + /// // Get the address of the PC for a frame you'd like to unwind. + /// let address = get_frame_pc(); + /// + /// // This context is reusable, which cuts down on heap allocations. + /// let ctx = UnwindContext::new(); + /// + /// // Optionally provide base addresses for any relative pointers. If a + /// // base address isn't provided and a pointer is found that is relative to + /// // it, we will return an `Err`. + /// # let address_of_text_section_in_memory = unimplemented!(); + /// # let address_of_got_section_in_memory = unimplemented!(); + /// let bases = BaseAddresses::default() + /// .set_text(address_of_text_section_in_memory) + /// .set_got(address_of_got_section_in_memory); + /// + /// let unwind_info = eh_frame.unwind_info_for_address( + /// &bases, + /// &mut ctx, + /// address, + /// EhFrame::cie_from_offset, + /// )?; + /// + /// # let do_stuff_with = |_| unimplemented!(); + /// do_stuff_with(unwind_info); + /// # let _ = ctx; + /// # unreachable!() + /// # } + /// ``` + #[inline] + fn unwind_info_for_address<'ctx, F, A: UnwindContextStorage<R>>( + &self, + bases: &BaseAddresses, + ctx: &'ctx mut UnwindContext<R, A>, + address: u64, + get_cie: F, + ) -> Result<&'ctx UnwindTableRow<R, A>> + where + F: FnMut(&Self, &BaseAddresses, Self::Offset) -> Result<CommonInformationEntry<R>>, + { + let fde = self.fde_for_address(bases, address, get_cie)?; + fde.unwind_info_for_address(self, bases, ctx, address) + } +} + +impl<R: Reader> _UnwindSectionPrivate<R> for DebugFrame<R> { + fn section(&self) -> &R { + &self.section + } + + fn length_value_is_end_of_entries(_: R::Offset) -> bool { + false + } + + fn is_cie(format: Format, id: u64) -> bool { + match format { + Format::Dwarf32 => id == 0xffff_ffff, + Format::Dwarf64 => id == 0xffff_ffff_ffff_ffff, + } + } + + fn cie_offset_encoding(format: Format) -> CieOffsetEncoding { + match format { + Format::Dwarf32 => CieOffsetEncoding::U32, + Format::Dwarf64 => CieOffsetEncoding::U64, + } + } + + fn resolve_cie_offset(&self, _: R::Offset, offset: R::Offset) -> Option<R::Offset> { + Some(offset) + } + + fn has_address_and_segment_sizes(version: u8) -> bool { + version == 4 + } + + fn address_size(&self) -> u8 { + self.address_size + } + + fn segment_size(&self) -> u8 { + self.segment_size + } + + fn vendor(&self) -> Vendor { + self.vendor + } +} + +impl<R: Reader> UnwindSection<R> for DebugFrame<R> { + type Offset = DebugFrameOffset<R::Offset>; +} + +impl<R: Reader> _UnwindSectionPrivate<R> for EhFrame<R> { + fn section(&self) -> &R { + &self.section + } + + fn length_value_is_end_of_entries(length: R::Offset) -> bool { + length.into_u64() == 0 + } + + fn is_cie(_: Format, id: u64) -> bool { + id == 0 + } + + fn cie_offset_encoding(_format: Format) -> CieOffsetEncoding { + // `.eh_frame` offsets are always 4 bytes, regardless of the DWARF + // format. + CieOffsetEncoding::U32 + } + + fn resolve_cie_offset(&self, base: R::Offset, offset: R::Offset) -> Option<R::Offset> { + base.checked_sub(offset) + } + + fn has_address_and_segment_sizes(_version: u8) -> bool { + false + } + + fn address_size(&self) -> u8 { + self.address_size + } + + fn segment_size(&self) -> u8 { + 0 + } + + fn vendor(&self) -> Vendor { + self.vendor + } +} + +impl<R: Reader> UnwindSection<R> for EhFrame<R> { + type Offset = EhFrameOffset<R::Offset>; +} + +/// Optional base addresses for the relative `DW_EH_PE_*` encoded pointers. +/// +/// During CIE/FDE parsing, if a relative pointer is encountered for a base +/// address that is unknown, an Err will be returned. +/// +/// ``` +/// use gimli::BaseAddresses; +/// +/// # fn foo() { +/// # let address_of_eh_frame_hdr_section_in_memory = unimplemented!(); +/// # let address_of_eh_frame_section_in_memory = unimplemented!(); +/// # let address_of_text_section_in_memory = unimplemented!(); +/// # let address_of_got_section_in_memory = unimplemented!(); +/// # let address_of_the_start_of_current_func = unimplemented!(); +/// let bases = BaseAddresses::default() +/// .set_eh_frame_hdr(address_of_eh_frame_hdr_section_in_memory) +/// .set_eh_frame(address_of_eh_frame_section_in_memory) +/// .set_text(address_of_text_section_in_memory) +/// .set_got(address_of_got_section_in_memory); +/// # let _ = bases; +/// # } +/// ``` +#[derive(Clone, Default, Debug, PartialEq, Eq)] +pub struct BaseAddresses { + /// The base addresses to use for pointers in the `.eh_frame_hdr` section. + pub eh_frame_hdr: SectionBaseAddresses, + + /// The base addresses to use for pointers in the `.eh_frame` section. + pub eh_frame: SectionBaseAddresses, +} + +/// Optional base addresses for the relative `DW_EH_PE_*` encoded pointers +/// in a particular section. +/// +/// See `BaseAddresses` for methods that are helpful in setting these addresses. +#[derive(Clone, Default, Debug, PartialEq, Eq)] +pub struct SectionBaseAddresses { + /// The address of the section containing the pointer. + pub section: Option<u64>, + + /// The base address for text relative pointers. + /// This is generally the address of the `.text` section. + pub text: Option<u64>, + + /// The base address for data relative pointers. + /// + /// For pointers in the `.eh_frame_hdr` section, this is the address + /// of the `.eh_frame_hdr` section + /// + /// For pointers in the `.eh_frame` section, this is generally the + /// global pointer, such as the address of the `.got` section. + pub data: Option<u64>, +} + +impl BaseAddresses { + /// Set the `.eh_frame_hdr` section base address. + #[inline] + pub fn set_eh_frame_hdr(mut self, addr: u64) -> Self { + self.eh_frame_hdr.section = Some(addr); + self.eh_frame_hdr.data = Some(addr); + self + } + + /// Set the `.eh_frame` section base address. + #[inline] + pub fn set_eh_frame(mut self, addr: u64) -> Self { + self.eh_frame.section = Some(addr); + self + } + + /// Set the `.text` section base address. + #[inline] + pub fn set_text(mut self, addr: u64) -> Self { + self.eh_frame_hdr.text = Some(addr); + self.eh_frame.text = Some(addr); + self + } + + /// Set the `.got` section base address. + #[inline] + pub fn set_got(mut self, addr: u64) -> Self { + self.eh_frame.data = Some(addr); + self + } +} + +/// An iterator over CIE and FDE entries in a `.debug_frame` or `.eh_frame` +/// section. +/// +/// Some pointers may be encoded relative to various base addresses. Use the +/// [`BaseAddresses`](./struct.BaseAddresses.html) parameter to provide them. By +/// default, none are provided. If a relative pointer is encountered for a base +/// address that is unknown, an `Err` will be returned and iteration will abort. +/// +/// Can be [used with +/// `FallibleIterator`](./index.html#using-with-fallibleiterator). +/// +/// ``` +/// use gimli::{BaseAddresses, EhFrame, EndianSlice, NativeEndian, UnwindSection}; +/// +/// # fn foo() -> gimli::Result<()> { +/// # let read_eh_frame_somehow = || unimplemented!(); +/// let eh_frame = EhFrame::new(read_eh_frame_somehow(), NativeEndian); +/// +/// # let address_of_eh_frame_hdr_section_in_memory = unimplemented!(); +/// # let address_of_eh_frame_section_in_memory = unimplemented!(); +/// # let address_of_text_section_in_memory = unimplemented!(); +/// # let address_of_got_section_in_memory = unimplemented!(); +/// # let address_of_the_start_of_current_func = unimplemented!(); +/// // Provide base addresses for relative pointers. +/// let bases = BaseAddresses::default() +/// .set_eh_frame_hdr(address_of_eh_frame_hdr_section_in_memory) +/// .set_eh_frame(address_of_eh_frame_section_in_memory) +/// .set_text(address_of_text_section_in_memory) +/// .set_got(address_of_got_section_in_memory); +/// +/// let mut entries = eh_frame.entries(&bases); +/// +/// # let do_stuff_with = |_| unimplemented!(); +/// while let Some(entry) = entries.next()? { +/// do_stuff_with(entry) +/// } +/// # unreachable!() +/// # } +/// ``` +#[derive(Clone, Debug)] +pub struct CfiEntriesIter<'bases, Section, R> +where + R: Reader, + Section: UnwindSection<R>, +{ + section: Section, + bases: &'bases BaseAddresses, + input: R, +} + +impl<'bases, Section, R> CfiEntriesIter<'bases, Section, R> +where + R: Reader, + Section: UnwindSection<R>, +{ + /// Advance the iterator to the next entry. + pub fn next(&mut self) -> Result<Option<CieOrFde<'bases, Section, R>>> { + if self.input.is_empty() { + return Ok(None); + } + + match parse_cfi_entry(self.bases, &self.section, &mut self.input) { + Err(e) => { + self.input.empty(); + Err(e) + } + Ok(None) => { + self.input.empty(); + Ok(None) + } + Ok(Some(entry)) => Ok(Some(entry)), + } + } +} + +#[cfg(feature = "fallible-iterator")] +impl<'bases, Section, R> fallible_iterator::FallibleIterator for CfiEntriesIter<'bases, Section, R> +where + R: Reader, + Section: UnwindSection<R>, +{ + type Item = CieOrFde<'bases, Section, R>; + type Error = Error; + + fn next(&mut self) -> ::core::result::Result<Option<Self::Item>, Self::Error> { + CfiEntriesIter::next(self) + } +} + +/// Either a `CommonInformationEntry` (CIE) or a `FrameDescriptionEntry` (FDE). +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum CieOrFde<'bases, Section, R> +where + R: Reader, + Section: UnwindSection<R>, +{ + /// This CFI entry is a `CommonInformationEntry`. + Cie(CommonInformationEntry<R>), + /// This CFI entry is a `FrameDescriptionEntry`, however fully parsing it + /// requires parsing its CIE first, so it is left in a partially parsed + /// state. + Fde(PartialFrameDescriptionEntry<'bases, Section, R>), +} + +fn parse_cfi_entry<'bases, Section, R>( + bases: &'bases BaseAddresses, + section: &Section, + input: &mut R, +) -> Result<Option<CieOrFde<'bases, Section, R>>> +where + R: Reader, + Section: UnwindSection<R>, +{ + let (offset, length, format) = loop { + let offset = input.offset_from(section.section()); + let (length, format) = input.read_initial_length()?; + + if Section::length_value_is_end_of_entries(length) { + return Ok(None); + } + + // Hack: skip zero padding inserted by buggy compilers/linkers. + // We require that the padding is a multiple of 32-bits, otherwise + // there is no reliable way to determine when the padding ends. This + // should be okay since CFI entries must be aligned to the address size. + + if length.into_u64() != 0 || format != Format::Dwarf32 { + break (offset, length, format); + } + }; + + let mut rest = input.split(length)?; + let cie_offset_base = rest.offset_from(section.section()); + let cie_id_or_offset = match Section::cie_offset_encoding(format) { + CieOffsetEncoding::U32 => rest.read_u32().map(u64::from)?, + CieOffsetEncoding::U64 => rest.read_u64()?, + }; + + if Section::is_cie(format, cie_id_or_offset) { + let cie = CommonInformationEntry::parse_rest(offset, length, format, bases, section, rest)?; + Ok(Some(CieOrFde::Cie(cie))) + } else { + let cie_offset = R::Offset::from_u64(cie_id_or_offset)?; + let cie_offset = match section.resolve_cie_offset(cie_offset_base, cie_offset) { + None => return Err(Error::OffsetOutOfBounds), + Some(cie_offset) => cie_offset, + }; + + let fde = PartialFrameDescriptionEntry { + offset, + length, + format, + cie_offset: cie_offset.into(), + rest, + section: section.clone(), + bases, + }; + + Ok(Some(CieOrFde::Fde(fde))) + } +} + +/// We support the z-style augmentation [defined by `.eh_frame`][ehframe]. +/// +/// [ehframe]: https://refspecs.linuxfoundation.org/LSB_3.0.0/LSB-Core-generic/LSB-Core-generic/ehframechpt.html +#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] +pub struct Augmentation { + /// > A 'L' may be present at any position after the first character of the + /// > string. This character may only be present if 'z' is the first character + /// > of the string. If present, it indicates the presence of one argument in + /// > the Augmentation Data of the CIE, and a corresponding argument in the + /// > Augmentation Data of the FDE. The argument in the Augmentation Data of + /// > the CIE is 1-byte and represents the pointer encoding used for the + /// > argument in the Augmentation Data of the FDE, which is the address of a + /// > language-specific data area (LSDA). The size of the LSDA pointer is + /// > specified by the pointer encoding used. + lsda: Option<constants::DwEhPe>, + + /// > A 'P' may be present at any position after the first character of the + /// > string. This character may only be present if 'z' is the first character + /// > of the string. If present, it indicates the presence of two arguments in + /// > the Augmentation Data of the CIE. The first argument is 1-byte and + /// > represents the pointer encoding used for the second argument, which is + /// > the address of a personality routine handler. The size of the + /// > personality routine pointer is specified by the pointer encoding used. + personality: Option<(constants::DwEhPe, Pointer)>, + + /// > A 'R' may be present at any position after the first character of the + /// > string. This character may only be present if 'z' is the first character + /// > of the string. If present, The Augmentation Data shall include a 1 byte + /// > argument that represents the pointer encoding for the address pointers + /// > used in the FDE. + fde_address_encoding: Option<constants::DwEhPe>, + + /// True if this CIE's FDEs are trampolines for signal handlers. + is_signal_trampoline: bool, +} + +impl Augmentation { + fn parse<Section, R>( + augmentation_str: &mut R, + bases: &BaseAddresses, + address_size: u8, + section: &Section, + input: &mut R, + ) -> Result<Augmentation> + where + R: Reader, + Section: UnwindSection<R>, + { + debug_assert!( + !augmentation_str.is_empty(), + "Augmentation::parse should only be called if we have an augmentation" + ); + + let mut augmentation = Augmentation::default(); + + let mut parsed_first = false; + let mut data = None; + + while !augmentation_str.is_empty() { + let ch = augmentation_str.read_u8()?; + match ch { + b'z' => { + if parsed_first { + return Err(Error::UnknownAugmentation); + } + + let augmentation_length = input.read_uleb128().and_then(R::Offset::from_u64)?; + data = Some(input.split(augmentation_length)?); + } + b'L' => { + let rest = data.as_mut().ok_or(Error::UnknownAugmentation)?; + let encoding = parse_pointer_encoding(rest)?; + augmentation.lsda = Some(encoding); + } + b'P' => { + let rest = data.as_mut().ok_or(Error::UnknownAugmentation)?; + let encoding = parse_pointer_encoding(rest)?; + let parameters = PointerEncodingParameters { + bases: &bases.eh_frame, + func_base: None, + address_size, + section: section.section(), + }; + + let personality = parse_encoded_pointer(encoding, ¶meters, rest)?; + augmentation.personality = Some((encoding, personality)); + } + b'R' => { + let rest = data.as_mut().ok_or(Error::UnknownAugmentation)?; + let encoding = parse_pointer_encoding(rest)?; + augmentation.fde_address_encoding = Some(encoding); + } + b'S' => augmentation.is_signal_trampoline = true, + _ => return Err(Error::UnknownAugmentation), + } + + parsed_first = true; + } + + Ok(augmentation) + } +} + +/// Parsed augmentation data for a `FrameDescriptEntry`. +#[derive(Clone, Debug, Default, PartialEq, Eq)] +struct AugmentationData { + lsda: Option<Pointer>, +} + +impl AugmentationData { + fn parse<R: Reader>( + augmentation: &Augmentation, + encoding_parameters: &PointerEncodingParameters<R>, + input: &mut R, + ) -> Result<AugmentationData> { + // In theory, we should be iterating over the original augmentation + // string, interpreting each character, and reading the appropriate bits + // out of the augmentation data as we go. However, the only character + // that defines augmentation data in the FDE is the 'L' character, so we + // can just check for its presence directly. + + let aug_data_len = input.read_uleb128().and_then(R::Offset::from_u64)?; + let rest = &mut input.split(aug_data_len)?; + let mut augmentation_data = AugmentationData::default(); + if let Some(encoding) = augmentation.lsda { + let lsda = parse_encoded_pointer(encoding, encoding_parameters, rest)?; + augmentation_data.lsda = Some(lsda); + } + Ok(augmentation_data) + } +} + +/// > A Common Information Entry holds information that is shared among many +/// > Frame Description Entries. There is at least one CIE in every non-empty +/// > `.debug_frame` section. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct CommonInformationEntry<R, Offset = <R as Reader>::Offset> +where + R: Reader<Offset = Offset>, + Offset: ReaderOffset, +{ + /// The offset of this entry from the start of its containing section. + offset: Offset, + + /// > A constant that gives the number of bytes of the CIE structure, not + /// > including the length field itself (see Section 7.2.2). The size of the + /// > length field plus the value of length must be an integral multiple of + /// > the address size. + length: Offset, + + format: Format, + + /// > A version number (see Section 7.23). This number is specific to the + /// > call frame information and is independent of the DWARF version number. + version: u8, + + /// The parsed augmentation, if any. + augmentation: Option<Augmentation>, + + /// > The size of a target address in this CIE and any FDEs that use it, in + /// > bytes. If a compilation unit exists for this frame, its address size + /// > must match the address size here. + address_size: u8, + + /// "The size of a segment selector in this CIE and any FDEs that use it, in + /// bytes." + segment_size: u8, + + /// "A constant that is factored out of all advance location instructions + /// (see Section 6.4.2.1)." + code_alignment_factor: u64, + + /// > A constant that is factored out of certain offset instructions (see + /// > below). The resulting value is (operand * data_alignment_factor). + data_alignment_factor: i64, + + /// > An unsigned LEB128 constant that indicates which column in the rule + /// > table represents the return address of the function. Note that this + /// > column might not correspond to an actual machine register. + return_address_register: Register, + + /// > A sequence of rules that are interpreted to create the initial setting + /// > of each column in the table. + /// + /// > The default rule for all columns before interpretation of the initial + /// > instructions is the undefined rule. However, an ABI authoring body or a + /// > compilation system authoring body may specify an alternate default + /// > value for any or all columns. + /// + /// This is followed by `DW_CFA_nop` padding until the end of `length` bytes + /// in the input. + initial_instructions: R, +} + +impl<R: Reader> CommonInformationEntry<R> { + fn parse<Section: UnwindSection<R>>( + bases: &BaseAddresses, + section: &Section, + input: &mut R, + ) -> Result<CommonInformationEntry<R>> { + match parse_cfi_entry(bases, section, input)? { + Some(CieOrFde::Cie(cie)) => Ok(cie), + Some(CieOrFde::Fde(_)) => Err(Error::NotCieId), + None => Err(Error::NoEntryAtGivenOffset), + } + } + + fn parse_rest<Section: UnwindSection<R>>( + offset: R::Offset, + length: R::Offset, + format: Format, + bases: &BaseAddresses, + section: &Section, + mut rest: R, + ) -> Result<CommonInformationEntry<R>> { + let version = rest.read_u8()?; + + // Version 1 of `.debug_frame` corresponds to DWARF 2, and then for + // DWARF 3 and 4, I think they decided to just match the standard's + // version. + match version { + 1 | 3 | 4 => (), + _ => return Err(Error::UnknownVersion(u64::from(version))), + } + + let mut augmentation_string = rest.read_null_terminated_slice()?; + + let (address_size, segment_size) = if Section::has_address_and_segment_sizes(version) { + let address_size = rest.read_u8()?; + let segment_size = rest.read_u8()?; + (address_size, segment_size) + } else { + (section.address_size(), section.segment_size()) + }; + + let code_alignment_factor = rest.read_uleb128()?; + let data_alignment_factor = rest.read_sleb128()?; + + let return_address_register = if version == 1 { + Register(rest.read_u8()?.into()) + } else { + rest.read_uleb128().and_then(Register::from_u64)? + }; + + let augmentation = if augmentation_string.is_empty() { + None + } else { + Some(Augmentation::parse( + &mut augmentation_string, + bases, + address_size, + section, + &mut rest, + )?) + }; + + let entry = CommonInformationEntry { + offset, + length, + format, + version, + augmentation, + address_size, + segment_size, + code_alignment_factor, + data_alignment_factor, + return_address_register, + initial_instructions: rest, + }; + + Ok(entry) + } +} + +/// # Signal Safe Methods +/// +/// These methods are guaranteed not to allocate, acquire locks, or perform any +/// other signal-unsafe operations. +impl<R: Reader> CommonInformationEntry<R> { + /// Get the offset of this entry from the start of its containing section. + pub fn offset(&self) -> R::Offset { + self.offset + } + + /// Return the encoding parameters for this CIE. + pub fn encoding(&self) -> Encoding { + Encoding { + format: self.format, + version: u16::from(self.version), + address_size: self.address_size, + } + } + + /// The size of addresses (in bytes) in this CIE. + pub fn address_size(&self) -> u8 { + self.address_size + } + + /// Iterate over this CIE's initial instructions. + /// + /// Can be [used with + /// `FallibleIterator`](./index.html#using-with-fallibleiterator). + pub fn instructions<'a, Section>( + &self, + section: &'a Section, + bases: &'a BaseAddresses, + ) -> CallFrameInstructionIter<'a, R> + where + Section: UnwindSection<R>, + { + CallFrameInstructionIter { + input: self.initial_instructions.clone(), + address_encoding: None, + parameters: PointerEncodingParameters { + bases: &bases.eh_frame, + func_base: None, + address_size: self.address_size, + section: section.section(), + }, + vendor: section.vendor(), + } + } + + /// > A constant that gives the number of bytes of the CIE structure, not + /// > including the length field itself (see Section 7.2.2). The size of the + /// > length field plus the value of length must be an integral multiple of + /// > the address size. + pub fn entry_len(&self) -> R::Offset { + self.length + } + + /// > A version number (see Section 7.23). This number is specific to the + /// > call frame information and is independent of the DWARF version number. + pub fn version(&self) -> u8 { + self.version + } + + /// Get the augmentation data, if any exists. + /// + /// The only augmentation understood by `gimli` is that which is defined by + /// `.eh_frame`. + pub fn augmentation(&self) -> Option<&Augmentation> { + self.augmentation.as_ref() + } + + /// True if this CIE's FDEs have a LSDA. + pub fn has_lsda(&self) -> bool { + self.augmentation.map_or(false, |a| a.lsda.is_some()) + } + + /// Return the encoding of the LSDA address for this CIE's FDEs. + pub fn lsda_encoding(&self) -> Option<constants::DwEhPe> { + self.augmentation.and_then(|a| a.lsda) + } + + /// Return the encoding and address of the personality routine handler + /// for this CIE's FDEs. + pub fn personality_with_encoding(&self) -> Option<(constants::DwEhPe, Pointer)> { + self.augmentation.as_ref().and_then(|a| a.personality) + } + + /// Return the address of the personality routine handler + /// for this CIE's FDEs. + pub fn personality(&self) -> Option<Pointer> { + self.augmentation + .as_ref() + .and_then(|a| a.personality) + .map(|(_, p)| p) + } + + /// Return the encoding of the addresses for this CIE's FDEs. + pub fn fde_address_encoding(&self) -> Option<constants::DwEhPe> { + self.augmentation.and_then(|a| a.fde_address_encoding) + } + + /// True if this CIE's FDEs are trampolines for signal handlers. + pub fn is_signal_trampoline(&self) -> bool { + self.augmentation.map_or(false, |a| a.is_signal_trampoline) + } + + /// > A constant that is factored out of all advance location instructions + /// > (see Section 6.4.2.1). + pub fn code_alignment_factor(&self) -> u64 { + self.code_alignment_factor + } + + /// > A constant that is factored out of certain offset instructions (see + /// > below). The resulting value is (operand * data_alignment_factor). + pub fn data_alignment_factor(&self) -> i64 { + self.data_alignment_factor + } + + /// > An unsigned ... constant that indicates which column in the rule + /// > table represents the return address of the function. Note that this + /// > column might not correspond to an actual machine register. + pub fn return_address_register(&self) -> Register { + self.return_address_register + } +} + +/// A partially parsed `FrameDescriptionEntry`. +/// +/// Fully parsing this FDE requires first parsing its CIE. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct PartialFrameDescriptionEntry<'bases, Section, R> +where + R: Reader, + Section: UnwindSection<R>, +{ + offset: R::Offset, + length: R::Offset, + format: Format, + cie_offset: Section::Offset, + rest: R, + section: Section, + bases: &'bases BaseAddresses, +} + +impl<'bases, Section, R> PartialFrameDescriptionEntry<'bases, Section, R> +where + R: Reader, + Section: UnwindSection<R>, +{ + fn parse_partial( + section: &Section, + bases: &'bases BaseAddresses, + input: &mut R, + ) -> Result<PartialFrameDescriptionEntry<'bases, Section, R>> { + match parse_cfi_entry(bases, section, input)? { + Some(CieOrFde::Cie(_)) => Err(Error::NotFdePointer), + Some(CieOrFde::Fde(partial)) => Ok(partial), + None => Err(Error::NoEntryAtGivenOffset), + } + } + + /// Fully parse this FDE. + /// + /// You must provide a function get its associated CIE (either by parsing it + /// on demand, or looking it up in some table mapping offsets to CIEs that + /// you've already parsed, etc.) + pub fn parse<F>(&self, get_cie: F) -> Result<FrameDescriptionEntry<R>> + where + F: FnMut(&Section, &BaseAddresses, Section::Offset) -> Result<CommonInformationEntry<R>>, + { + FrameDescriptionEntry::parse_rest( + self.offset, + self.length, + self.format, + self.cie_offset, + self.rest.clone(), + &self.section, + self.bases, + get_cie, + ) + } + + /// Get the offset of this entry from the start of its containing section. + pub fn offset(&self) -> R::Offset { + self.offset + } + + /// Get the offset of this FDE's CIE. + pub fn cie_offset(&self) -> Section::Offset { + self.cie_offset + } + + /// > A constant that gives the number of bytes of the header and + /// > instruction stream for this function, not including the length field + /// > itself (see Section 7.2.2). The size of the length field plus the value + /// > of length must be an integral multiple of the address size. + pub fn entry_len(&self) -> R::Offset { + self.length + } +} + +/// A `FrameDescriptionEntry` is a set of CFA instructions for an address range. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct FrameDescriptionEntry<R, Offset = <R as Reader>::Offset> +where + R: Reader<Offset = Offset>, + Offset: ReaderOffset, +{ + /// The start of this entry within its containing section. + offset: Offset, + + /// > A constant that gives the number of bytes of the header and + /// > instruction stream for this function, not including the length field + /// > itself (see Section 7.2.2). The size of the length field plus the value + /// > of length must be an integral multiple of the address size. + length: Offset, + + format: Format, + + /// "A constant offset into the .debug_frame section that denotes the CIE + /// that is associated with this FDE." + /// + /// This is the CIE at that offset. + cie: CommonInformationEntry<R, Offset>, + + /// > The address of the first location associated with this table entry. If + /// > the segment_size field of this FDE's CIE is non-zero, the initial + /// > location is preceded by a segment selector of the given length. + initial_segment: u64, + initial_address: u64, + + /// "The number of bytes of program instructions described by this entry." + address_range: u64, + + /// The parsed augmentation data, if we have any. + augmentation: Option<AugmentationData>, + + /// "A sequence of table defining instructions that are described below." + /// + /// This is followed by `DW_CFA_nop` padding until `length` bytes of the + /// input are consumed. + instructions: R, +} + +impl<R: Reader> FrameDescriptionEntry<R> { + fn parse_rest<Section, F>( + offset: R::Offset, + length: R::Offset, + format: Format, + cie_pointer: Section::Offset, + mut rest: R, + section: &Section, + bases: &BaseAddresses, + mut get_cie: F, + ) -> Result<FrameDescriptionEntry<R>> + where + Section: UnwindSection<R>, + F: FnMut(&Section, &BaseAddresses, Section::Offset) -> Result<CommonInformationEntry<R>>, + { + let cie = get_cie(section, bases, cie_pointer)?; + + let initial_segment = if cie.segment_size > 0 { + rest.read_address(cie.segment_size)? + } else { + 0 + }; + + let mut parameters = PointerEncodingParameters { + bases: &bases.eh_frame, + func_base: None, + address_size: cie.address_size, + section: section.section(), + }; + + let (initial_address, address_range) = Self::parse_addresses(&mut rest, &cie, ¶meters)?; + parameters.func_base = Some(initial_address); + + let aug_data = if let Some(ref augmentation) = cie.augmentation { + Some(AugmentationData::parse( + augmentation, + ¶meters, + &mut rest, + )?) + } else { + None + }; + + let entry = FrameDescriptionEntry { + offset, + length, + format, + cie, + initial_segment, + initial_address, + address_range, + augmentation: aug_data, + instructions: rest, + }; + + Ok(entry) + } + + fn parse_addresses( + input: &mut R, + cie: &CommonInformationEntry<R>, + parameters: &PointerEncodingParameters<R>, + ) -> Result<(u64, u64)> { + let encoding = cie.augmentation().and_then(|a| a.fde_address_encoding); + if let Some(encoding) = encoding { + let initial_address = parse_encoded_pointer(encoding, parameters, input)?; + + // Ignore indirection. + let initial_address = initial_address.pointer(); + + // Address ranges cannot be relative to anything, so just grab the + // data format bits from the encoding. + let address_range = parse_encoded_pointer(encoding.format(), parameters, input)?; + Ok((initial_address, address_range.pointer())) + } else { + let initial_address = input.read_address(cie.address_size)?; + let address_range = input.read_address(cie.address_size)?; + Ok((initial_address, address_range)) + } + } + + /// Return the table of unwind information for this FDE. + #[inline] + pub fn rows<'a, 'ctx, Section: UnwindSection<R>, A: UnwindContextStorage<R>>( + &self, + section: &'a Section, + bases: &'a BaseAddresses, + ctx: &'ctx mut UnwindContext<R, A>, + ) -> Result<UnwindTable<'a, 'ctx, R, A>> { + UnwindTable::new(section, bases, ctx, self) + } + + /// Find the frame unwind information for the given address. + /// + /// If found, the unwind information is returned along with the reset + /// context in the form `Ok((unwind_info, context))`. If not found, + /// `Err(gimli::Error::NoUnwindInfoForAddress)` is returned. If parsing or + /// CFI evaluation fails, the error is returned. + pub fn unwind_info_for_address<'ctx, Section: UnwindSection<R>, A: UnwindContextStorage<R>>( + &self, + section: &Section, + bases: &BaseAddresses, + ctx: &'ctx mut UnwindContext<R, A>, + address: u64, + ) -> Result<&'ctx UnwindTableRow<R, A>> { + let mut table = self.rows(section, bases, ctx)?; + while let Some(row) = table.next_row()? { + if row.contains(address) { + return Ok(table.ctx.row()); + } + } + Err(Error::NoUnwindInfoForAddress) + } +} + +/// # Signal Safe Methods +/// +/// These methods are guaranteed not to allocate, acquire locks, or perform any +/// other signal-unsafe operations. +#[allow(clippy::len_without_is_empty)] +impl<R: Reader> FrameDescriptionEntry<R> { + /// Get the offset of this entry from the start of its containing section. + pub fn offset(&self) -> R::Offset { + self.offset + } + + /// Get a reference to this FDE's CIE. + pub fn cie(&self) -> &CommonInformationEntry<R> { + &self.cie + } + + /// > A constant that gives the number of bytes of the header and + /// > instruction stream for this function, not including the length field + /// > itself (see Section 7.2.2). The size of the length field plus the value + /// > of length must be an integral multiple of the address size. + pub fn entry_len(&self) -> R::Offset { + self.length + } + + /// Iterate over this FDE's instructions. + /// + /// Will not include the CIE's initial instructions, if you want those do + /// `fde.cie().instructions()` first. + /// + /// Can be [used with + /// `FallibleIterator`](./index.html#using-with-fallibleiterator). + pub fn instructions<'a, Section>( + &self, + section: &'a Section, + bases: &'a BaseAddresses, + ) -> CallFrameInstructionIter<'a, R> + where + Section: UnwindSection<R>, + { + CallFrameInstructionIter { + input: self.instructions.clone(), + address_encoding: self.cie.augmentation().and_then(|a| a.fde_address_encoding), + parameters: PointerEncodingParameters { + bases: &bases.eh_frame, + func_base: None, + address_size: self.cie.address_size, + section: section.section(), + }, + vendor: section.vendor(), + } + } + + /// The first address for which this entry has unwind information for. + pub fn initial_address(&self) -> u64 { + self.initial_address + } + + /// The number of bytes of instructions that this entry has unwind + /// information for. + pub fn len(&self) -> u64 { + self.address_range + } + + /// Return `true` if the given address is within this FDE, `false` + /// otherwise. + /// + /// This is equivalent to `entry.initial_address() <= address < + /// entry.initial_address() + entry.len()`. + pub fn contains(&self, address: u64) -> bool { + let start = self.initial_address(); + let end = start + self.len(); + start <= address && address < end + } + + /// The address of this FDE's language-specific data area (LSDA), if it has + /// any. + pub fn lsda(&self) -> Option<Pointer> { + self.augmentation.as_ref().and_then(|a| a.lsda) + } + + /// Return true if this FDE's function is a trampoline for a signal handler. + #[inline] + pub fn is_signal_trampoline(&self) -> bool { + self.cie().is_signal_trampoline() + } + + /// Return the address of the FDE's function's personality routine + /// handler. The personality routine does language-specific clean up when + /// unwinding the stack frames with the intent to not run them again. + #[inline] + pub fn personality(&self) -> Option<Pointer> { + self.cie().personality() + } +} + +/// Specification of what storage should be used for [`UnwindContext`]. +/// +#[cfg_attr( + feature = "read", + doc = " +Normally you would only need to use [`StoreOnHeap`], which places the stack +on the heap using [`Vec`]. This is the default storage type parameter for [`UnwindContext`]. +" +)] +/// +/// If you need to avoid [`UnwindContext`] from allocating memory, e.g. for signal safety, +/// you can provide you own storage specification: +/// ```rust,no_run +/// # use gimli::*; +/// # +/// # fn foo<'a>(some_fde: gimli::FrameDescriptionEntry<gimli::EndianSlice<'a, gimli::LittleEndian>>) +/// # -> gimli::Result<()> { +/// # let eh_frame: gimli::EhFrame<_> = unreachable!(); +/// # let bases = unimplemented!(); +/// # +/// struct StoreOnStack; +/// +/// impl<R: Reader> UnwindContextStorage<R> for StoreOnStack { +/// type Rules = [(Register, RegisterRule<R>); 192]; +/// type Stack = [UnwindTableRow<R, Self>; 4]; +/// } +/// +/// let mut ctx = UnwindContext::<_, StoreOnStack>::new_in(); +/// +/// // Initialize the context by evaluating the CIE's initial instruction program, +/// // and generate the unwind table. +/// let mut table = some_fde.rows(&eh_frame, &bases, &mut ctx)?; +/// while let Some(row) = table.next_row()? { +/// // Do stuff with each row... +/// # let _ = row; +/// } +/// # unreachable!() +/// # } +/// ``` +pub trait UnwindContextStorage<R: Reader>: Sized { + /// The storage used for register rules in a unwind table row. + /// + /// Note that this is nested within the stack. + type Rules: ArrayLike<Item = (Register, RegisterRule<R>)>; + + /// The storage used for unwind table row stack. + type Stack: ArrayLike<Item = UnwindTableRow<R, Self>>; +} + +#[cfg(feature = "read")] +const MAX_RULES: usize = 192; +#[cfg(feature = "read")] +const MAX_UNWIND_STACK_DEPTH: usize = 4; + +#[cfg(feature = "read")] +impl<R: Reader> UnwindContextStorage<R> for StoreOnHeap { + type Rules = [(Register, RegisterRule<R>); MAX_RULES]; + type Stack = Box<[UnwindTableRow<R, Self>; MAX_UNWIND_STACK_DEPTH]>; +} + +/// Common context needed when evaluating the call frame unwinding information. +/// +/// This structure can be large so it is advisable to place it on the heap. +/// To avoid re-allocating the context multiple times when evaluating multiple +/// CFI programs, it can be reused. +/// +/// ``` +/// use gimli::{UnwindContext, UnwindTable}; +/// +/// # fn foo<'a>(some_fde: gimli::FrameDescriptionEntry<gimli::EndianSlice<'a, gimli::LittleEndian>>) +/// # -> gimli::Result<()> { +/// # let eh_frame: gimli::EhFrame<_> = unreachable!(); +/// # let bases = unimplemented!(); +/// // An uninitialized context. +/// let mut ctx = Box::new(UnwindContext::new()); +/// +/// // Initialize the context by evaluating the CIE's initial instruction program, +/// // and generate the unwind table. +/// let mut table = some_fde.rows(&eh_frame, &bases, &mut ctx)?; +/// while let Some(row) = table.next_row()? { +/// // Do stuff with each row... +/// # let _ = row; +/// } +/// # unreachable!() +/// # } +/// ``` +#[derive(Clone, PartialEq, Eq)] +pub struct UnwindContext<R: Reader, A: UnwindContextStorage<R> = StoreOnHeap> { + // Stack of rows. The last row is the row currently being built by the + // program. There is always at least one row. The vast majority of CFI + // programs will only ever have one row on the stack. + stack: ArrayVec<A::Stack>, + + // If we are evaluating an FDE's instructions, then `is_initialized` will be + // `true`. If `initial_rule` is `Some`, then the initial register rules are either + // all default rules or have just 1 non-default rule, stored in `initial_rule`. + // If it's `None`, `stack[0]` will contain the initial register rules + // described by the CIE's initial instructions. These rules are used by + // `DW_CFA_restore`. Otherwise, when we are currently evaluating a CIE's + // initial instructions, `is_initialized` will be `false` and initial rules + // cannot be read. + initial_rule: Option<(Register, RegisterRule<R>)>, + + is_initialized: bool, +} + +impl<R: Reader, S: UnwindContextStorage<R>> Debug for UnwindContext<R, S> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("UnwindContext") + .field("stack", &self.stack) + .field("initial_rule", &self.initial_rule) + .field("is_initialized", &self.is_initialized) + .finish() + } +} + +impl<R: Reader, A: UnwindContextStorage<R>> Default for UnwindContext<R, A> { + fn default() -> Self { + Self::new_in() + } +} + +#[cfg(feature = "read")] +impl<R: Reader> UnwindContext<R> { + /// Construct a new call frame unwinding context. + pub fn new() -> Self { + Self::new_in() + } +} + +/// # Signal Safe Methods +/// +/// These methods are guaranteed not to allocate, acquire locks, or perform any +/// other signal-unsafe operations, if an non-allocating storage is used. +impl<R: Reader, A: UnwindContextStorage<R>> UnwindContext<R, A> { + /// Construct a new call frame unwinding context. + pub fn new_in() -> Self { + let mut ctx = UnwindContext { + stack: Default::default(), + initial_rule: None, + is_initialized: false, + }; + ctx.reset(); + ctx + } + + /// Run the CIE's initial instructions and initialize this `UnwindContext`. + fn initialize<Section: UnwindSection<R>>( + &mut self, + section: &Section, + bases: &BaseAddresses, + cie: &CommonInformationEntry<R>, + ) -> Result<()> { + // Always reset because previous initialization failure may leave dirty state. + self.reset(); + + let mut table = UnwindTable::new_for_cie(section, bases, self, cie); + while table.next_row()?.is_some() {} + + self.save_initial_rules()?; + Ok(()) + } + + fn reset(&mut self) { + self.stack.clear(); + self.stack.try_push(UnwindTableRow::default()).unwrap(); + debug_assert!(self.stack[0].is_default()); + self.initial_rule = None; + self.is_initialized = false; + } + + fn row(&self) -> &UnwindTableRow<R, A> { + self.stack.last().unwrap() + } + + fn row_mut(&mut self) -> &mut UnwindTableRow<R, A> { + self.stack.last_mut().unwrap() + } + + fn save_initial_rules(&mut self) -> Result<()> { + debug_assert!(!self.is_initialized); + self.initial_rule = match *self.stack.last().unwrap().registers.rules { + // All rules are default (undefined). In this case just synthesize + // an undefined rule. + [] => Some((Register(0), RegisterRule::Undefined)), + [ref rule] => Some(rule.clone()), + _ => { + let rules = self.stack.last().unwrap().clone(); + self.stack + .try_insert(0, rules) + .map_err(|_| Error::StackFull)?; + None + } + }; + self.is_initialized = true; + Ok(()) + } + + fn start_address(&self) -> u64 { + self.row().start_address + } + + fn set_start_address(&mut self, start_address: u64) { + let row = self.row_mut(); + row.start_address = start_address; + } + + fn set_register_rule(&mut self, register: Register, rule: RegisterRule<R>) -> Result<()> { + let row = self.row_mut(); + row.registers.set(register, rule) + } + + /// Returns `None` if we have not completed evaluation of a CIE's initial + /// instructions. + fn get_initial_rule(&self, register: Register) -> Option<RegisterRule<R>> { + if !self.is_initialized { + return None; + } + Some(match self.initial_rule { + None => self.stack[0].registers.get(register), + Some((r, ref rule)) if r == register => rule.clone(), + _ => RegisterRule::Undefined, + }) + } + + fn set_cfa(&mut self, cfa: CfaRule<R>) { + self.row_mut().cfa = cfa; + } + + fn cfa_mut(&mut self) -> &mut CfaRule<R> { + &mut self.row_mut().cfa + } + + fn push_row(&mut self) -> Result<()> { + let new_row = self.row().clone(); + self.stack.try_push(new_row).map_err(|_| Error::StackFull) + } + + fn pop_row(&mut self) -> Result<()> { + let min_size = if self.is_initialized && self.initial_rule.is_none() { + 2 + } else { + 1 + }; + if self.stack.len() <= min_size { + return Err(Error::PopWithEmptyStack); + } + self.stack.pop().unwrap(); + Ok(()) + } +} + +/// The `UnwindTable` iteratively evaluates a `FrameDescriptionEntry`'s +/// `CallFrameInstruction` program, yielding the each row one at a time. +/// +/// > 6.4.1 Structure of Call Frame Information +/// > +/// > DWARF supports virtual unwinding by defining an architecture independent +/// > basis for recording how procedures save and restore registers during their +/// > lifetimes. This basis must be augmented on some machines with specific +/// > information that is defined by an architecture specific ABI authoring +/// > committee, a hardware vendor, or a compiler producer. The body defining a +/// > specific augmentation is referred to below as the “augmenter.” +/// > +/// > Abstractly, this mechanism describes a very large table that has the +/// > following structure: +/// > +/// > <table> +/// > <tr> +/// > <th>LOC</th><th>CFA</th><th>R0</th><th>R1</th><td>...</td><th>RN</th> +/// > </tr> +/// > <tr> +/// > <th>L0</th> <td></td> <td></td> <td></td> <td></td> <td></td> +/// > </tr> +/// > <tr> +/// > <th>L1</th> <td></td> <td></td> <td></td> <td></td> <td></td> +/// > </tr> +/// > <tr> +/// > <td>...</td><td></td> <td></td> <td></td> <td></td> <td></td> +/// > </tr> +/// > <tr> +/// > <th>LN</th> <td></td> <td></td> <td></td> <td></td> <td></td> +/// > </tr> +/// > </table> +/// > +/// > The first column indicates an address for every location that contains code +/// > in a program. (In shared objects, this is an object-relative offset.) The +/// > remaining columns contain virtual unwinding rules that are associated with +/// > the indicated location. +/// > +/// > The CFA column defines the rule which computes the Canonical Frame Address +/// > value; it may be either a register and a signed offset that are added +/// > together, or a DWARF expression that is evaluated. +/// > +/// > The remaining columns are labeled by register number. This includes some +/// > registers that have special designation on some architectures such as the PC +/// > and the stack pointer register. (The actual mapping of registers for a +/// > particular architecture is defined by the augmenter.) The register columns +/// > contain rules that describe whether a given register has been saved and the +/// > rule to find the value for the register in the previous frame. +/// > +/// > ... +/// > +/// > This table would be extremely large if actually constructed as +/// > described. Most of the entries at any point in the table are identical to +/// > the ones above them. The whole table can be represented quite compactly by +/// > recording just the differences starting at the beginning address of each +/// > subroutine in the program. +#[derive(Debug)] +pub struct UnwindTable<'a, 'ctx, R: Reader, A: UnwindContextStorage<R> = StoreOnHeap> { + code_alignment_factor: Wrapping<u64>, + data_alignment_factor: Wrapping<i64>, + next_start_address: u64, + last_end_address: u64, + returned_last_row: bool, + current_row_valid: bool, + instructions: CallFrameInstructionIter<'a, R>, + ctx: &'ctx mut UnwindContext<R, A>, +} + +/// # Signal Safe Methods +/// +/// These methods are guaranteed not to allocate, acquire locks, or perform any +/// other signal-unsafe operations. +impl<'a, 'ctx, R: Reader, A: UnwindContextStorage<R>> UnwindTable<'a, 'ctx, R, A> { + /// Construct a new `UnwindTable` for the given + /// `FrameDescriptionEntry`'s CFI unwinding program. + pub fn new<Section: UnwindSection<R>>( + section: &'a Section, + bases: &'a BaseAddresses, + ctx: &'ctx mut UnwindContext<R, A>, + fde: &FrameDescriptionEntry<R>, + ) -> Result<Self> { + ctx.initialize(section, bases, fde.cie())?; + Ok(Self::new_for_fde(section, bases, ctx, fde)) + } + + fn new_for_fde<Section: UnwindSection<R>>( + section: &'a Section, + bases: &'a BaseAddresses, + ctx: &'ctx mut UnwindContext<R, A>, + fde: &FrameDescriptionEntry<R>, + ) -> Self { + assert!(ctx.stack.len() >= 1); + UnwindTable { + code_alignment_factor: Wrapping(fde.cie().code_alignment_factor()), + data_alignment_factor: Wrapping(fde.cie().data_alignment_factor()), + next_start_address: fde.initial_address(), + last_end_address: fde.initial_address().wrapping_add(fde.len()), + returned_last_row: false, + current_row_valid: false, + instructions: fde.instructions(section, bases), + ctx, + } + } + + fn new_for_cie<Section: UnwindSection<R>>( + section: &'a Section, + bases: &'a BaseAddresses, + ctx: &'ctx mut UnwindContext<R, A>, + cie: &CommonInformationEntry<R>, + ) -> Self { + assert!(ctx.stack.len() >= 1); + UnwindTable { + code_alignment_factor: Wrapping(cie.code_alignment_factor()), + data_alignment_factor: Wrapping(cie.data_alignment_factor()), + next_start_address: 0, + last_end_address: 0, + returned_last_row: false, + current_row_valid: false, + instructions: cie.instructions(section, bases), + ctx, + } + } + + /// Evaluate call frame instructions until the next row of the table is + /// completed, and return it. + /// + /// Unfortunately, this cannot be used with `FallibleIterator` because of + /// the restricted lifetime of the yielded item. + pub fn next_row(&mut self) -> Result<Option<&UnwindTableRow<R, A>>> { + assert!(self.ctx.stack.len() >= 1); + self.ctx.set_start_address(self.next_start_address); + self.current_row_valid = false; + + loop { + match self.instructions.next() { + Err(e) => return Err(e), + + Ok(None) => { + if self.returned_last_row { + return Ok(None); + } + + let row = self.ctx.row_mut(); + row.end_address = self.last_end_address; + + self.returned_last_row = true; + self.current_row_valid = true; + return Ok(Some(row)); + } + + Ok(Some(instruction)) => { + if self.evaluate(instruction)? { + self.current_row_valid = true; + return Ok(Some(self.ctx.row())); + } + } + }; + } + } + + /// Returns the current row with the lifetime of the context. + pub fn into_current_row(self) -> Option<&'ctx UnwindTableRow<R, A>> { + if self.current_row_valid { + Some(self.ctx.row()) + } else { + None + } + } + + /// Evaluate one call frame instruction. Return `Ok(true)` if the row is + /// complete, `Ok(false)` otherwise. + fn evaluate(&mut self, instruction: CallFrameInstruction<R>) -> Result<bool> { + use crate::CallFrameInstruction::*; + + match instruction { + // Instructions that complete the current row and advance the + // address for the next row. + SetLoc { address } => { + if address < self.ctx.start_address() { + return Err(Error::InvalidAddressRange); + } + + self.next_start_address = address; + self.ctx.row_mut().end_address = self.next_start_address; + return Ok(true); + } + AdvanceLoc { delta } => { + let delta = Wrapping(u64::from(delta)) * self.code_alignment_factor; + self.next_start_address = (Wrapping(self.ctx.start_address()) + delta).0; + self.ctx.row_mut().end_address = self.next_start_address; + return Ok(true); + } + + // Instructions that modify the CFA. + DefCfa { register, offset } => { + self.ctx.set_cfa(CfaRule::RegisterAndOffset { + register, + offset: offset as i64, + }); + } + DefCfaSf { + register, + factored_offset, + } => { + let data_align = self.data_alignment_factor; + self.ctx.set_cfa(CfaRule::RegisterAndOffset { + register, + offset: (Wrapping(factored_offset) * data_align).0, + }); + } + DefCfaRegister { register } => { + if let CfaRule::RegisterAndOffset { + register: ref mut reg, + .. + } = *self.ctx.cfa_mut() + { + *reg = register; + } else { + return Err(Error::CfiInstructionInInvalidContext); + } + } + DefCfaOffset { offset } => { + if let CfaRule::RegisterAndOffset { + offset: ref mut off, + .. + } = *self.ctx.cfa_mut() + { + *off = offset as i64; + } else { + return Err(Error::CfiInstructionInInvalidContext); + } + } + DefCfaOffsetSf { factored_offset } => { + if let CfaRule::RegisterAndOffset { + offset: ref mut off, + .. + } = *self.ctx.cfa_mut() + { + let data_align = self.data_alignment_factor; + *off = (Wrapping(factored_offset) * data_align).0; + } else { + return Err(Error::CfiInstructionInInvalidContext); + } + } + DefCfaExpression { expression } => { + self.ctx.set_cfa(CfaRule::Expression(expression)); + } + + // Instructions that define register rules. + Undefined { register } => { + self.ctx + .set_register_rule(register, RegisterRule::Undefined)?; + } + SameValue { register } => { + self.ctx + .set_register_rule(register, RegisterRule::SameValue)?; + } + Offset { + register, + factored_offset, + } => { + let offset = Wrapping(factored_offset as i64) * self.data_alignment_factor; + self.ctx + .set_register_rule(register, RegisterRule::Offset(offset.0))?; + } + OffsetExtendedSf { + register, + factored_offset, + } => { + let offset = Wrapping(factored_offset) * self.data_alignment_factor; + self.ctx + .set_register_rule(register, RegisterRule::Offset(offset.0))?; + } + ValOffset { + register, + factored_offset, + } => { + let offset = Wrapping(factored_offset as i64) * self.data_alignment_factor; + self.ctx + .set_register_rule(register, RegisterRule::ValOffset(offset.0))?; + } + ValOffsetSf { + register, + factored_offset, + } => { + let offset = Wrapping(factored_offset) * self.data_alignment_factor; + self.ctx + .set_register_rule(register, RegisterRule::ValOffset(offset.0))?; + } + Register { + dest_register, + src_register, + } => { + self.ctx + .set_register_rule(dest_register, RegisterRule::Register(src_register))?; + } + Expression { + register, + expression, + } => { + let expression = RegisterRule::Expression(expression); + self.ctx.set_register_rule(register, expression)?; + } + ValExpression { + register, + expression, + } => { + let expression = RegisterRule::ValExpression(expression); + self.ctx.set_register_rule(register, expression)?; + } + Restore { register } => { + let initial_rule = if let Some(rule) = self.ctx.get_initial_rule(register) { + rule + } else { + // Can't restore the initial rule when we are + // evaluating the initial rules! + return Err(Error::CfiInstructionInInvalidContext); + }; + + self.ctx.set_register_rule(register, initial_rule)?; + } + + // Row push and pop instructions. + RememberState => { + self.ctx.push_row()?; + } + RestoreState => { + // Pop state while preserving current location. + let start_address = self.ctx.start_address(); + self.ctx.pop_row()?; + self.ctx.set_start_address(start_address); + } + + // GNU Extension. Save the size somewhere so the unwinder can use + // it when restoring IP + ArgsSize { size } => { + self.ctx.row_mut().saved_args_size = size; + } + + // AArch64 extension. + NegateRaState => { + let register = crate::AArch64::RA_SIGN_STATE; + let value = match self.ctx.row().register(register) { + RegisterRule::Undefined => 0, + RegisterRule::Constant(value) => value, + _ => return Err(Error::CfiInstructionInInvalidContext), + }; + self.ctx + .set_register_rule(register, RegisterRule::Constant(value ^ 1))?; + } + + // No operation. + Nop => {} + }; + + Ok(false) + } +} + +// We tend to have very few register rules: usually only a couple. Even if we +// have a rule for every register, on x86-64 with SSE and everything we're +// talking about ~100 rules. So rather than keeping the rules in a hash map, or +// a vector indexed by register number (which would lead to filling lots of +// empty entries), we store them as a vec of (register number, register rule) +// pairs. +// +// Additionally, because every register's default rule is implicitly +// `RegisterRule::Undefined`, we never store a register's rule in this vec if it +// is undefined and save a little bit more space and do a little fewer +// comparisons that way. +// +// The maximum number of rules preallocated by libunwind is 97 for AArch64, 128 +// for ARM, and even 188 for MIPS. It is extremely unlikely to encounter this +// many register rules in practice. +// +// See: +// - https://github.com/libunwind/libunwind/blob/11fd461095ea98f4b3e3a361f5a8a558519363fa/include/tdep-x86_64/dwarf-config.h#L36 +// - https://github.com/libunwind/libunwind/blob/11fd461095ea98f4b3e3a361f5a8a558519363fa/include/tdep-aarch64/dwarf-config.h#L32 +// - https://github.com/libunwind/libunwind/blob/11fd461095ea98f4b3e3a361f5a8a558519363fa/include/tdep-arm/dwarf-config.h#L31 +// - https://github.com/libunwind/libunwind/blob/11fd461095ea98f4b3e3a361f5a8a558519363fa/include/tdep-mips/dwarf-config.h#L31 +struct RegisterRuleMap<R: Reader, S: UnwindContextStorage<R> = StoreOnHeap> { + rules: ArrayVec<S::Rules>, +} + +impl<R: Reader, S: UnwindContextStorage<R>> Debug for RegisterRuleMap<R, S> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("RegisterRuleMap") + .field("rules", &self.rules) + .finish() + } +} + +impl<R: Reader, S: UnwindContextStorage<R>> Clone for RegisterRuleMap<R, S> { + fn clone(&self) -> Self { + Self { + rules: self.rules.clone(), + } + } +} + +impl<R: Reader, S: UnwindContextStorage<R>> Default for RegisterRuleMap<R, S> { + fn default() -> Self { + RegisterRuleMap { + rules: Default::default(), + } + } +} + +/// # Signal Safe Methods +/// +/// These methods are guaranteed not to allocate, acquire locks, or perform any +/// other signal-unsafe operations. +impl<R: Reader, S: UnwindContextStorage<R>> RegisterRuleMap<R, S> { + fn is_default(&self) -> bool { + self.rules.is_empty() + } + + fn get(&self, register: Register) -> RegisterRule<R> { + self.rules + .iter() + .find(|rule| rule.0 == register) + .map(|r| { + debug_assert!(r.1.is_defined()); + r.1.clone() + }) + .unwrap_or(RegisterRule::Undefined) + } + + fn set(&mut self, register: Register, rule: RegisterRule<R>) -> Result<()> { + if !rule.is_defined() { + let idx = self + .rules + .iter() + .enumerate() + .find(|&(_, r)| r.0 == register) + .map(|(i, _)| i); + if let Some(idx) = idx { + self.rules.swap_remove(idx); + } + return Ok(()); + } + + for &mut (reg, ref mut old_rule) in &mut *self.rules { + debug_assert!(old_rule.is_defined()); + if reg == register { + *old_rule = rule; + return Ok(()); + } + } + + self.rules + .try_push((register, rule)) + .map_err(|_| Error::TooManyRegisterRules) + } + + fn iter(&self) -> RegisterRuleIter<R> { + RegisterRuleIter(self.rules.iter()) + } +} + +impl<'a, R, S: UnwindContextStorage<R>> FromIterator<&'a (Register, RegisterRule<R>)> + for RegisterRuleMap<R, S> +where + R: 'a + Reader, +{ + fn from_iter<T>(iter: T) -> Self + where + T: IntoIterator<Item = &'a (Register, RegisterRule<R>)>, + { + let iter = iter.into_iter(); + let mut rules = RegisterRuleMap::default(); + for &(reg, ref rule) in iter.filter(|r| r.1.is_defined()) { + rules.set(reg, rule.clone()).expect( + "This is only used in tests, impl isn't exposed publicly. + If you trip this, fix your test", + ); + } + rules + } +} + +impl<R, S: UnwindContextStorage<R>> PartialEq for RegisterRuleMap<R, S> +where + R: Reader + PartialEq, +{ + fn eq(&self, rhs: &Self) -> bool { + for &(reg, ref rule) in &*self.rules { + debug_assert!(rule.is_defined()); + if *rule != rhs.get(reg) { + return false; + } + } + + for &(reg, ref rhs_rule) in &*rhs.rules { + debug_assert!(rhs_rule.is_defined()); + if *rhs_rule != self.get(reg) { + return false; + } + } + + true + } +} + +impl<R, S: UnwindContextStorage<R>> Eq for RegisterRuleMap<R, S> where R: Reader + Eq {} + +/// An unordered iterator for register rules. +#[derive(Debug, Clone)] +pub struct RegisterRuleIter<'iter, R>(::core::slice::Iter<'iter, (Register, RegisterRule<R>)>) +where + R: Reader; + +impl<'iter, R: Reader> Iterator for RegisterRuleIter<'iter, R> { + type Item = &'iter (Register, RegisterRule<R>); + + fn next(&mut self) -> Option<Self::Item> { + self.0.next() + } +} + +/// A row in the virtual unwind table that describes how to find the values of +/// the registers in the *previous* frame for a range of PC addresses. +#[derive(PartialEq, Eq)] +pub struct UnwindTableRow<R: Reader, S: UnwindContextStorage<R> = StoreOnHeap> { + start_address: u64, + end_address: u64, + saved_args_size: u64, + cfa: CfaRule<R>, + registers: RegisterRuleMap<R, S>, +} + +impl<R: Reader, S: UnwindContextStorage<R>> Debug for UnwindTableRow<R, S> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("UnwindTableRow") + .field("start_address", &self.start_address) + .field("end_address", &self.end_address) + .field("saved_args_size", &self.saved_args_size) + .field("cfa", &self.cfa) + .field("registers", &self.registers) + .finish() + } +} + +impl<R: Reader, S: UnwindContextStorage<R>> Clone for UnwindTableRow<R, S> { + fn clone(&self) -> Self { + Self { + start_address: self.start_address, + end_address: self.end_address, + saved_args_size: self.saved_args_size, + cfa: self.cfa.clone(), + registers: self.registers.clone(), + } + } +} + +impl<R: Reader, S: UnwindContextStorage<R>> Default for UnwindTableRow<R, S> { + fn default() -> Self { + UnwindTableRow { + start_address: 0, + end_address: 0, + saved_args_size: 0, + cfa: Default::default(), + registers: Default::default(), + } + } +} + +impl<R: Reader, S: UnwindContextStorage<R>> UnwindTableRow<R, S> { + fn is_default(&self) -> bool { + self.start_address == 0 + && self.end_address == 0 + && self.cfa.is_default() + && self.registers.is_default() + } + + /// Get the starting PC address that this row applies to. + pub fn start_address(&self) -> u64 { + self.start_address + } + + /// Get the end PC address where this row's register rules become + /// unapplicable. + /// + /// In other words, this row describes how to recover the last frame's + /// registers for all PCs where `row.start_address() <= PC < + /// row.end_address()`. This row does NOT describe how to recover registers + /// when `PC == row.end_address()`. + pub fn end_address(&self) -> u64 { + self.end_address + } + + /// Return `true` if the given `address` is within this row's address range, + /// `false` otherwise. + pub fn contains(&self, address: u64) -> bool { + self.start_address <= address && address < self.end_address + } + + /// Returns the amount of args currently on the stack. + /// + /// When unwinding, if the personality function requested a change in IP, + /// the SP needs to be adjusted by saved_args_size. + pub fn saved_args_size(&self) -> u64 { + self.saved_args_size + } + + /// Get the canonical frame address (CFA) recovery rule for this row. + pub fn cfa(&self) -> &CfaRule<R> { + &self.cfa + } + + /// Get the register recovery rule for the given register number. + /// + /// The register number mapping is architecture dependent. For example, in + /// the x86-64 ABI the register number mapping is defined in Figure 3.36: + /// + /// > Figure 3.36: DWARF Register Number Mapping + /// > + /// > <table> + /// > <tr><th>Register Name</th> <th>Number</th> <th>Abbreviation</th></tr> + /// > <tr><td>General Purpose Register RAX</td> <td>0</td> <td>%rax</td></tr> + /// > <tr><td>General Purpose Register RDX</td> <td>1</td> <td>%rdx</td></tr> + /// > <tr><td>General Purpose Register RCX</td> <td>2</td> <td>%rcx</td></tr> + /// > <tr><td>General Purpose Register RBX</td> <td>3</td> <td>%rbx</td></tr> + /// > <tr><td>General Purpose Register RSI</td> <td>4</td> <td>%rsi</td></tr> + /// > <tr><td>General Purpose Register RDI</td> <td>5</td> <td>%rdi</td></tr> + /// > <tr><td>General Purpose Register RBP</td> <td>6</td> <td>%rbp</td></tr> + /// > <tr><td>Stack Pointer Register RSP</td> <td>7</td> <td>%rsp</td></tr> + /// > <tr><td>Extended Integer Registers 8-15</td> <td>8-15</td> <td>%r8-%r15</td></tr> + /// > <tr><td>Return Address RA</td> <td>16</td> <td></td></tr> + /// > <tr><td>Vector Registers 0–7</td> <td>17-24</td> <td>%xmm0–%xmm7</td></tr> + /// > <tr><td>Extended Vector Registers 8–15</td> <td>25-32</td> <td>%xmm8–%xmm15</td></tr> + /// > <tr><td>Floating Point Registers 0–7</td> <td>33-40</td> <td>%st0–%st7</td></tr> + /// > <tr><td>MMX Registers 0–7</td> <td>41-48</td> <td>%mm0–%mm7</td></tr> + /// > <tr><td>Flag Register</td> <td>49</td> <td>%rFLAGS</td></tr> + /// > <tr><td>Segment Register ES</td> <td>50</td> <td>%es</td></tr> + /// > <tr><td>Segment Register CS</td> <td>51</td> <td>%cs</td></tr> + /// > <tr><td>Segment Register SS</td> <td>52</td> <td>%ss</td></tr> + /// > <tr><td>Segment Register DS</td> <td>53</td> <td>%ds</td></tr> + /// > <tr><td>Segment Register FS</td> <td>54</td> <td>%fs</td></tr> + /// > <tr><td>Segment Register GS</td> <td>55</td> <td>%gs</td></tr> + /// > <tr><td>Reserved</td> <td>56-57</td> <td></td></tr> + /// > <tr><td>FS Base address</td> <td>58</td> <td>%fs.base</td></tr> + /// > <tr><td>GS Base address</td> <td>59</td> <td>%gs.base</td></tr> + /// > <tr><td>Reserved</td> <td>60-61</td> <td></td></tr> + /// > <tr><td>Task Register</td> <td>62</td> <td>%tr</td></tr> + /// > <tr><td>LDT Register</td> <td>63</td> <td>%ldtr</td></tr> + /// > <tr><td>128-bit Media Control and Status</td> <td>64</td> <td>%mxcsr</td></tr> + /// > <tr><td>x87 Control Word</td> <td>65</td> <td>%fcw</td></tr> + /// > <tr><td>x87 Status Word</td> <td>66</td> <td>%fsw</td></tr> + /// > <tr><td>Upper Vector Registers 16–31</td> <td>67-82</td> <td>%xmm16–%xmm31</td></tr> + /// > <tr><td>Reserved</td> <td>83-117</td> <td></td></tr> + /// > <tr><td>Vector Mask Registers 0–7</td> <td>118-125</td> <td>%k0–%k7</td></tr> + /// > <tr><td>Reserved</td> <td>126-129</td> <td></td></tr> + /// > </table> + pub fn register(&self, register: Register) -> RegisterRule<R> { + self.registers.get(register) + } + + /// Iterate over all defined register `(number, rule)` pairs. + /// + /// The rules are not iterated in any guaranteed order. Any register that + /// does not make an appearance in the iterator implicitly has the rule + /// `RegisterRule::Undefined`. + /// + /// ``` + /// # use gimli::{EndianSlice, LittleEndian, UnwindTableRow}; + /// # fn foo<'input>(unwind_table_row: UnwindTableRow<EndianSlice<'input, LittleEndian>>) { + /// for &(register, ref rule) in unwind_table_row.registers() { + /// // ... + /// # drop(register); drop(rule); + /// } + /// # } + /// ``` + pub fn registers(&self) -> RegisterRuleIter<R> { + self.registers.iter() + } +} + +/// The canonical frame address (CFA) recovery rules. +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum CfaRule<R: Reader> { + /// The CFA is given offset from the given register's value. + RegisterAndOffset { + /// The register containing the base value. + register: Register, + /// The offset from the register's base value. + offset: i64, + }, + /// The CFA is obtained by evaluating this `Reader` as a DWARF expression + /// program. + Expression(Expression<R>), +} + +impl<R: Reader> Default for CfaRule<R> { + fn default() -> Self { + CfaRule::RegisterAndOffset { + register: Register(0), + offset: 0, + } + } +} + +impl<R: Reader> CfaRule<R> { + fn is_default(&self) -> bool { + match *self { + CfaRule::RegisterAndOffset { register, offset } => { + register == Register(0) && offset == 0 + } + _ => false, + } + } +} + +/// An entry in the abstract CFI table that describes how to find the value of a +/// register. +/// +/// "The register columns contain rules that describe whether a given register +/// has been saved and the rule to find the value for the register in the +/// previous frame." +#[derive(Clone, Debug, PartialEq, Eq)] +#[non_exhaustive] +pub enum RegisterRule<R: Reader> { + /// > A register that has this rule has no recoverable value in the previous + /// > frame. (By convention, it is not preserved by a callee.) + Undefined, + + /// > This register has not been modified from the previous frame. (By + /// > convention, it is preserved by the callee, but the callee has not + /// > modified it.) + SameValue, + + /// "The previous value of this register is saved at the address CFA+N where + /// CFA is the current CFA value and N is a signed offset." + Offset(i64), + + /// "The previous value of this register is the value CFA+N where CFA is the + /// current CFA value and N is a signed offset." + ValOffset(i64), + + /// "The previous value of this register is stored in another register + /// numbered R." + Register(Register), + + /// "The previous value of this register is located at the address produced + /// by executing the DWARF expression." + Expression(Expression<R>), + + /// "The previous value of this register is the value produced by executing + /// the DWARF expression." + ValExpression(Expression<R>), + + /// "The rule is defined externally to this specification by the augmenter." + Architectural, + + /// This is a pseudo-register with a constant value. + Constant(u64), +} + +impl<R: Reader> RegisterRule<R> { + fn is_defined(&self) -> bool { + !matches!(*self, RegisterRule::Undefined) + } +} + +/// A parsed call frame instruction. +#[derive(Clone, Debug, PartialEq, Eq)] +#[non_exhaustive] +pub enum CallFrameInstruction<R: Reader> { + // 6.4.2.1 Row Creation Methods + /// > 1. DW_CFA_set_loc + /// > + /// > The DW_CFA_set_loc instruction takes a single operand that represents + /// > a target address. The required action is to create a new table row + /// > using the specified address as the location. All other values in the + /// > new row are initially identical to the current row. The new location + /// > value is always greater than the current one. If the segment_size + /// > field of this FDE's CIE is non- zero, the initial location is preceded + /// > by a segment selector of the given length. + SetLoc { + /// The target address. + address: u64, + }, + + /// The `AdvanceLoc` instruction is used for all of `DW_CFA_advance_loc` and + /// `DW_CFA_advance_loc{1,2,4}`. + /// + /// > 2. DW_CFA_advance_loc + /// > + /// > The DW_CFA_advance instruction takes a single operand (encoded with + /// > the opcode) that represents a constant delta. The required action is + /// > to create a new table row with a location value that is computed by + /// > taking the current entry’s location value and adding the value of + /// > delta * code_alignment_factor. All other values in the new row are + /// > initially identical to the current row. + AdvanceLoc { + /// The delta to be added to the current address. + delta: u32, + }, + + // 6.4.2.2 CFA Definition Methods + /// > 1. DW_CFA_def_cfa + /// > + /// > The DW_CFA_def_cfa instruction takes two unsigned LEB128 operands + /// > representing a register number and a (non-factored) offset. The + /// > required action is to define the current CFA rule to use the provided + /// > register and offset. + DefCfa { + /// The target register's number. + register: Register, + /// The non-factored offset. + offset: u64, + }, + + /// > 2. DW_CFA_def_cfa_sf + /// > + /// > The DW_CFA_def_cfa_sf instruction takes two operands: an unsigned + /// > LEB128 value representing a register number and a signed LEB128 + /// > factored offset. This instruction is identical to DW_CFA_def_cfa + /// > except that the second operand is signed and factored. The resulting + /// > offset is factored_offset * data_alignment_factor. + DefCfaSf { + /// The target register's number. + register: Register, + /// The factored offset. + factored_offset: i64, + }, + + /// > 3. DW_CFA_def_cfa_register + /// > + /// > The DW_CFA_def_cfa_register instruction takes a single unsigned LEB128 + /// > operand representing a register number. The required action is to + /// > define the current CFA rule to use the provided register (but to keep + /// > the old offset). This operation is valid only if the current CFA rule + /// > is defined to use a register and offset. + DefCfaRegister { + /// The target register's number. + register: Register, + }, + + /// > 4. DW_CFA_def_cfa_offset + /// > + /// > The DW_CFA_def_cfa_offset instruction takes a single unsigned LEB128 + /// > operand representing a (non-factored) offset. The required action is + /// > to define the current CFA rule to use the provided offset (but to keep + /// > the old register). This operation is valid only if the current CFA + /// > rule is defined to use a register and offset. + DefCfaOffset { + /// The non-factored offset. + offset: u64, + }, + + /// > 5. DW_CFA_def_cfa_offset_sf + /// > + /// > The DW_CFA_def_cfa_offset_sf instruction takes a signed LEB128 operand + /// > representing a factored offset. This instruction is identical to + /// > DW_CFA_def_cfa_offset except that the operand is signed and + /// > factored. The resulting offset is factored_offset * + /// > data_alignment_factor. This operation is valid only if the current CFA + /// > rule is defined to use a register and offset. + DefCfaOffsetSf { + /// The factored offset. + factored_offset: i64, + }, + + /// > 6. DW_CFA_def_cfa_expression + /// > + /// > The DW_CFA_def_cfa_expression instruction takes a single operand + /// > encoded as a DW_FORM_exprloc value representing a DWARF + /// > expression. The required action is to establish that expression as the + /// > means by which the current CFA is computed. + DefCfaExpression { + /// The DWARF expression. + expression: Expression<R>, + }, + + // 6.4.2.3 Register Rule Instructions + /// > 1. DW_CFA_undefined + /// > + /// > The DW_CFA_undefined instruction takes a single unsigned LEB128 + /// > operand that represents a register number. The required action is to + /// > set the rule for the specified register to “undefined.” + Undefined { + /// The target register's number. + register: Register, + }, + + /// > 2. DW_CFA_same_value + /// > + /// > The DW_CFA_same_value instruction takes a single unsigned LEB128 + /// > operand that represents a register number. The required action is to + /// > set the rule for the specified register to “same value.” + SameValue { + /// The target register's number. + register: Register, + }, + + /// The `Offset` instruction represents both `DW_CFA_offset` and + /// `DW_CFA_offset_extended`. + /// + /// > 3. DW_CFA_offset + /// > + /// > The DW_CFA_offset instruction takes two operands: a register number + /// > (encoded with the opcode) and an unsigned LEB128 constant representing + /// > a factored offset. The required action is to change the rule for the + /// > register indicated by the register number to be an offset(N) rule + /// > where the value of N is factored offset * data_alignment_factor. + Offset { + /// The target register's number. + register: Register, + /// The factored offset. + factored_offset: u64, + }, + + /// > 5. DW_CFA_offset_extended_sf + /// > + /// > The DW_CFA_offset_extended_sf instruction takes two operands: an + /// > unsigned LEB128 value representing a register number and a signed + /// > LEB128 factored offset. This instruction is identical to + /// > DW_CFA_offset_extended except that the second operand is signed and + /// > factored. The resulting offset is factored_offset * + /// > data_alignment_factor. + OffsetExtendedSf { + /// The target register's number. + register: Register, + /// The factored offset. + factored_offset: i64, + }, + + /// > 6. DW_CFA_val_offset + /// > + /// > The DW_CFA_val_offset instruction takes two unsigned LEB128 operands + /// > representing a register number and a factored offset. The required + /// > action is to change the rule for the register indicated by the + /// > register number to be a val_offset(N) rule where the value of N is + /// > factored_offset * data_alignment_factor. + ValOffset { + /// The target register's number. + register: Register, + /// The factored offset. + factored_offset: u64, + }, + + /// > 7. DW_CFA_val_offset_sf + /// > + /// > The DW_CFA_val_offset_sf instruction takes two operands: an unsigned + /// > LEB128 value representing a register number and a signed LEB128 + /// > factored offset. This instruction is identical to DW_CFA_val_offset + /// > except that the second operand is signed and factored. The resulting + /// > offset is factored_offset * data_alignment_factor. + ValOffsetSf { + /// The target register's number. + register: Register, + /// The factored offset. + factored_offset: i64, + }, + + /// > 8. DW_CFA_register + /// > + /// > The DW_CFA_register instruction takes two unsigned LEB128 operands + /// > representing register numbers. The required action is to set the rule + /// > for the first register to be register(R) where R is the second + /// > register. + Register { + /// The number of the register whose rule is being changed. + dest_register: Register, + /// The number of the register where the other register's value can be + /// found. + src_register: Register, + }, + + /// > 9. DW_CFA_expression + /// > + /// > The DW_CFA_expression instruction takes two operands: an unsigned + /// > LEB128 value representing a register number, and a DW_FORM_block value + /// > representing a DWARF expression. The required action is to change the + /// > rule for the register indicated by the register number to be an + /// > expression(E) rule where E is the DWARF expression. That is, the DWARF + /// > expression computes the address. The value of the CFA is pushed on the + /// > DWARF evaluation stack prior to execution of the DWARF expression. + Expression { + /// The target register's number. + register: Register, + /// The DWARF expression. + expression: Expression<R>, + }, + + /// > 10. DW_CFA_val_expression + /// > + /// > The DW_CFA_val_expression instruction takes two operands: an unsigned + /// > LEB128 value representing a register number, and a DW_FORM_block value + /// > representing a DWARF expression. The required action is to change the + /// > rule for the register indicated by the register number to be a + /// > val_expression(E) rule where E is the DWARF expression. That is, the + /// > DWARF expression computes the value of the given register. The value + /// > of the CFA is pushed on the DWARF evaluation stack prior to execution + /// > of the DWARF expression. + ValExpression { + /// The target register's number. + register: Register, + /// The DWARF expression. + expression: Expression<R>, + }, + + /// The `Restore` instruction represents both `DW_CFA_restore` and + /// `DW_CFA_restore_extended`. + /// + /// > 11. DW_CFA_restore + /// > + /// > The DW_CFA_restore instruction takes a single operand (encoded with + /// > the opcode) that represents a register number. The required action is + /// > to change the rule for the indicated register to the rule assigned it + /// > by the initial_instructions in the CIE. + Restore { + /// The register to be reset. + register: Register, + }, + + // 6.4.2.4 Row State Instructions + /// > 1. DW_CFA_remember_state + /// > + /// > The DW_CFA_remember_state instruction takes no operands. The required + /// > action is to push the set of rules for every register onto an implicit + /// > stack. + RememberState, + + /// > 2. DW_CFA_restore_state + /// > + /// > The DW_CFA_restore_state instruction takes no operands. The required + /// > action is to pop the set of rules off the implicit stack and place + /// > them in the current row. + RestoreState, + + /// > DW_CFA_GNU_args_size + /// > + /// > GNU Extension + /// > + /// > The DW_CFA_GNU_args_size instruction takes an unsigned LEB128 operand + /// > representing an argument size. This instruction specifies the total of + /// > the size of the arguments which have been pushed onto the stack. + ArgsSize { + /// The size of the arguments which have been pushed onto the stack + size: u64, + }, + + /// > DW_CFA_AARCH64_negate_ra_state + /// > + /// > AArch64 Extension + /// > + /// > The DW_CFA_AARCH64_negate_ra_state operation negates bit 0 of the + /// > RA_SIGN_STATE pseudo-register. It does not take any operands. The + /// > DW_CFA_AARCH64_negate_ra_state must not be mixed with other DWARF Register + /// > Rule Instructions on the RA_SIGN_STATE pseudo-register in one Common + /// > Information Entry (CIE) and Frame Descriptor Entry (FDE) program sequence. + NegateRaState, + + // 6.4.2.5 Padding Instruction + /// > 1. DW_CFA_nop + /// > + /// > The DW_CFA_nop instruction has no operands and no required actions. It + /// > is used as padding to make a CIE or FDE an appropriate size. + Nop, +} + +const CFI_INSTRUCTION_HIGH_BITS_MASK: u8 = 0b1100_0000; +const CFI_INSTRUCTION_LOW_BITS_MASK: u8 = !CFI_INSTRUCTION_HIGH_BITS_MASK; + +impl<R: Reader> CallFrameInstruction<R> { + fn parse( + input: &mut R, + address_encoding: Option<DwEhPe>, + parameters: &PointerEncodingParameters<R>, + vendor: Vendor, + ) -> Result<CallFrameInstruction<R>> { + let instruction = input.read_u8()?; + let high_bits = instruction & CFI_INSTRUCTION_HIGH_BITS_MASK; + + if high_bits == constants::DW_CFA_advance_loc.0 { + let delta = instruction & CFI_INSTRUCTION_LOW_BITS_MASK; + return Ok(CallFrameInstruction::AdvanceLoc { + delta: u32::from(delta), + }); + } + + if high_bits == constants::DW_CFA_offset.0 { + let register = Register((instruction & CFI_INSTRUCTION_LOW_BITS_MASK).into()); + let offset = input.read_uleb128()?; + return Ok(CallFrameInstruction::Offset { + register, + factored_offset: offset, + }); + } + + if high_bits == constants::DW_CFA_restore.0 { + let register = Register((instruction & CFI_INSTRUCTION_LOW_BITS_MASK).into()); + return Ok(CallFrameInstruction::Restore { register }); + } + + debug_assert_eq!(high_bits, 0); + let instruction = constants::DwCfa(instruction); + + match instruction { + constants::DW_CFA_nop => Ok(CallFrameInstruction::Nop), + + constants::DW_CFA_set_loc => { + let address = if let Some(encoding) = address_encoding { + parse_encoded_pointer(encoding, parameters, input)?.direct()? + } else { + input.read_address(parameters.address_size)? + }; + Ok(CallFrameInstruction::SetLoc { address }) + } + + constants::DW_CFA_advance_loc1 => { + let delta = input.read_u8()?; + Ok(CallFrameInstruction::AdvanceLoc { + delta: u32::from(delta), + }) + } + + constants::DW_CFA_advance_loc2 => { + let delta = input.read_u16()?; + Ok(CallFrameInstruction::AdvanceLoc { + delta: u32::from(delta), + }) + } + + constants::DW_CFA_advance_loc4 => { + let delta = input.read_u32()?; + Ok(CallFrameInstruction::AdvanceLoc { delta }) + } + + constants::DW_CFA_offset_extended => { + let register = input.read_uleb128().and_then(Register::from_u64)?; + let offset = input.read_uleb128()?; + Ok(CallFrameInstruction::Offset { + register, + factored_offset: offset, + }) + } + + constants::DW_CFA_restore_extended => { + let register = input.read_uleb128().and_then(Register::from_u64)?; + Ok(CallFrameInstruction::Restore { register }) + } + + constants::DW_CFA_undefined => { + let register = input.read_uleb128().and_then(Register::from_u64)?; + Ok(CallFrameInstruction::Undefined { register }) + } + + constants::DW_CFA_same_value => { + let register = input.read_uleb128().and_then(Register::from_u64)?; + Ok(CallFrameInstruction::SameValue { register }) + } + + constants::DW_CFA_register => { + let dest = input.read_uleb128().and_then(Register::from_u64)?; + let src = input.read_uleb128().and_then(Register::from_u64)?; + Ok(CallFrameInstruction::Register { + dest_register: dest, + src_register: src, + }) + } + + constants::DW_CFA_remember_state => Ok(CallFrameInstruction::RememberState), + + constants::DW_CFA_restore_state => Ok(CallFrameInstruction::RestoreState), + + constants::DW_CFA_def_cfa => { + let register = input.read_uleb128().and_then(Register::from_u64)?; + let offset = input.read_uleb128()?; + Ok(CallFrameInstruction::DefCfa { register, offset }) + } + + constants::DW_CFA_def_cfa_register => { + let register = input.read_uleb128().and_then(Register::from_u64)?; + Ok(CallFrameInstruction::DefCfaRegister { register }) + } + + constants::DW_CFA_def_cfa_offset => { + let offset = input.read_uleb128()?; + Ok(CallFrameInstruction::DefCfaOffset { offset }) + } + + constants::DW_CFA_def_cfa_expression => { + let len = input.read_uleb128().and_then(R::Offset::from_u64)?; + let expression = input.split(len)?; + Ok(CallFrameInstruction::DefCfaExpression { + expression: Expression(expression), + }) + } + + constants::DW_CFA_expression => { + let register = input.read_uleb128().and_then(Register::from_u64)?; + let len = input.read_uleb128().and_then(R::Offset::from_u64)?; + let expression = input.split(len)?; + Ok(CallFrameInstruction::Expression { + register, + expression: Expression(expression), + }) + } + + constants::DW_CFA_offset_extended_sf => { + let register = input.read_uleb128().and_then(Register::from_u64)?; + let offset = input.read_sleb128()?; + Ok(CallFrameInstruction::OffsetExtendedSf { + register, + factored_offset: offset, + }) + } + + constants::DW_CFA_def_cfa_sf => { + let register = input.read_uleb128().and_then(Register::from_u64)?; + let offset = input.read_sleb128()?; + Ok(CallFrameInstruction::DefCfaSf { + register, + factored_offset: offset, + }) + } + + constants::DW_CFA_def_cfa_offset_sf => { + let offset = input.read_sleb128()?; + Ok(CallFrameInstruction::DefCfaOffsetSf { + factored_offset: offset, + }) + } + + constants::DW_CFA_val_offset => { + let register = input.read_uleb128().and_then(Register::from_u64)?; + let offset = input.read_uleb128()?; + Ok(CallFrameInstruction::ValOffset { + register, + factored_offset: offset, + }) + } + + constants::DW_CFA_val_offset_sf => { + let register = input.read_uleb128().and_then(Register::from_u64)?; + let offset = input.read_sleb128()?; + Ok(CallFrameInstruction::ValOffsetSf { + register, + factored_offset: offset, + }) + } + + constants::DW_CFA_val_expression => { + let register = input.read_uleb128().and_then(Register::from_u64)?; + let len = input.read_uleb128().and_then(R::Offset::from_u64)?; + let expression = input.split(len)?; + Ok(CallFrameInstruction::ValExpression { + register, + expression: Expression(expression), + }) + } + + constants::DW_CFA_GNU_args_size => { + let size = input.read_uleb128()?; + Ok(CallFrameInstruction::ArgsSize { size }) + } + + constants::DW_CFA_AARCH64_negate_ra_state if vendor == Vendor::AArch64 => { + Ok(CallFrameInstruction::NegateRaState) + } + + otherwise => Err(Error::UnknownCallFrameInstruction(otherwise)), + } + } +} + +/// A lazy iterator parsing call frame instructions. +/// +/// Can be [used with +/// `FallibleIterator`](./index.html#using-with-fallibleiterator). +#[derive(Clone, Debug)] +pub struct CallFrameInstructionIter<'a, R: Reader> { + input: R, + address_encoding: Option<constants::DwEhPe>, + parameters: PointerEncodingParameters<'a, R>, + vendor: Vendor, +} + +impl<'a, R: Reader> CallFrameInstructionIter<'a, R> { + /// Parse the next call frame instruction. + pub fn next(&mut self) -> Result<Option<CallFrameInstruction<R>>> { + if self.input.is_empty() { + return Ok(None); + } + + match CallFrameInstruction::parse( + &mut self.input, + self.address_encoding, + &self.parameters, + self.vendor, + ) { + Ok(instruction) => Ok(Some(instruction)), + Err(e) => { + self.input.empty(); + Err(e) + } + } + } +} + +#[cfg(feature = "fallible-iterator")] +impl<'a, R: Reader> fallible_iterator::FallibleIterator for CallFrameInstructionIter<'a, R> { + type Item = CallFrameInstruction<R>; + type Error = Error; + + fn next(&mut self) -> ::core::result::Result<Option<Self::Item>, Self::Error> { + CallFrameInstructionIter::next(self) + } +} + +/// Parse a `DW_EH_PE_*` pointer encoding. +#[doc(hidden)] +#[inline] +fn parse_pointer_encoding<R: Reader>(input: &mut R) -> Result<constants::DwEhPe> { + let eh_pe = input.read_u8()?; + let eh_pe = constants::DwEhPe(eh_pe); + + if eh_pe.is_valid_encoding() { + Ok(eh_pe) + } else { + Err(Error::UnknownPointerEncoding) + } +} + +/// A decoded pointer. +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum Pointer { + /// This value is the decoded pointer value. + Direct(u64), + + /// This value is *not* the pointer value, but points to the address of + /// where the real pointer value lives. In other words, deref this pointer + /// to get the real pointer value. + /// + /// Chase this pointer at your own risk: do you trust the DWARF data it came + /// from? + Indirect(u64), +} + +impl Default for Pointer { + #[inline] + fn default() -> Self { + Pointer::Direct(0) + } +} + +impl Pointer { + #[inline] + fn new(encoding: constants::DwEhPe, address: u64) -> Pointer { + if encoding.is_indirect() { + Pointer::Indirect(address) + } else { + Pointer::Direct(address) + } + } + + /// Return the direct pointer value. + #[inline] + pub fn direct(self) -> Result<u64> { + match self { + Pointer::Direct(p) => Ok(p), + Pointer::Indirect(_) => Err(Error::UnsupportedPointerEncoding), + } + } + + /// Return the pointer value, discarding indirectness information. + #[inline] + pub fn pointer(self) -> u64 { + match self { + Pointer::Direct(p) | Pointer::Indirect(p) => p, + } + } +} + +#[derive(Clone, Debug)] +struct PointerEncodingParameters<'a, R: Reader> { + bases: &'a SectionBaseAddresses, + func_base: Option<u64>, + address_size: u8, + section: &'a R, +} + +fn parse_encoded_pointer<R: Reader>( + encoding: constants::DwEhPe, + parameters: &PointerEncodingParameters<R>, + input: &mut R, +) -> Result<Pointer> { + // TODO: check this once only in parse_pointer_encoding + if !encoding.is_valid_encoding() { + return Err(Error::UnknownPointerEncoding); + } + + if encoding == constants::DW_EH_PE_omit { + return Err(Error::CannotParseOmitPointerEncoding); + } + + let base = match encoding.application() { + constants::DW_EH_PE_absptr => 0, + constants::DW_EH_PE_pcrel => { + if let Some(section_base) = parameters.bases.section { + let offset_from_section = input.offset_from(parameters.section); + section_base.wrapping_add(offset_from_section.into_u64()) + } else { + return Err(Error::PcRelativePointerButSectionBaseIsUndefined); + } + } + constants::DW_EH_PE_textrel => { + if let Some(text) = parameters.bases.text { + text + } else { + return Err(Error::TextRelativePointerButTextBaseIsUndefined); + } + } + constants::DW_EH_PE_datarel => { + if let Some(data) = parameters.bases.data { + data + } else { + return Err(Error::DataRelativePointerButDataBaseIsUndefined); + } + } + constants::DW_EH_PE_funcrel => { + if let Some(func) = parameters.func_base { + func + } else { + return Err(Error::FuncRelativePointerInBadContext); + } + } + constants::DW_EH_PE_aligned => return Err(Error::UnsupportedPointerEncoding), + _ => unreachable!(), + }; + + let offset = match encoding.format() { + // Unsigned variants. + constants::DW_EH_PE_absptr => input.read_address(parameters.address_size), + constants::DW_EH_PE_uleb128 => input.read_uleb128(), + constants::DW_EH_PE_udata2 => input.read_u16().map(u64::from), + constants::DW_EH_PE_udata4 => input.read_u32().map(u64::from), + constants::DW_EH_PE_udata8 => input.read_u64(), + + // Signed variants. Here we sign extend the values (happens by + // default when casting a signed integer to a larger range integer + // in Rust), return them as u64, and rely on wrapping addition to do + // the right thing when adding these offsets to their bases. + constants::DW_EH_PE_sleb128 => input.read_sleb128().map(|a| a as u64), + constants::DW_EH_PE_sdata2 => input.read_i16().map(|a| a as u64), + constants::DW_EH_PE_sdata4 => input.read_i32().map(|a| a as u64), + constants::DW_EH_PE_sdata8 => input.read_i64().map(|a| a as u64), + + // That was all of the valid encoding formats. + _ => unreachable!(), + }?; + + Ok(Pointer::new(encoding, base.wrapping_add(offset))) +} + +#[cfg(test)] +mod tests { + use super::*; + use super::{parse_cfi_entry, AugmentationData, RegisterRuleMap, UnwindContext}; + use crate::common::Format; + use crate::constants; + use crate::endianity::{BigEndian, Endianity, LittleEndian, NativeEndian}; + use crate::read::{ + EndianSlice, Error, Expression, Pointer, ReaderOffsetId, Result, Section as ReadSection, + }; + use crate::test_util::GimliSectionMethods; + use alloc::boxed::Box; + use alloc::vec::Vec; + use core::marker::PhantomData; + use core::mem; + use core::u64; + use test_assembler::{Endian, Label, LabelMaker, LabelOrNum, Section, ToLabelOrNum}; + + // Ensure each test tries to read the same section kind that it wrote. + #[derive(Clone, Copy)] + struct SectionKind<Section>(PhantomData<Section>); + + impl<T> SectionKind<T> { + fn endian<'input, E>(self) -> Endian + where + E: Endianity, + T: UnwindSection<EndianSlice<'input, E>>, + T::Offset: UnwindOffset<usize>, + { + if E::default().is_big_endian() { + Endian::Big + } else { + Endian::Little + } + } + + fn section<'input, E>(self, contents: &'input [u8]) -> T + where + E: Endianity, + T: UnwindSection<EndianSlice<'input, E>> + ReadSection<EndianSlice<'input, E>>, + T::Offset: UnwindOffset<usize>, + { + EndianSlice::new(contents, E::default()).into() + } + } + + fn debug_frame_le<'a>() -> SectionKind<DebugFrame<EndianSlice<'a, LittleEndian>>> { + SectionKind(PhantomData) + } + + fn debug_frame_be<'a>() -> SectionKind<DebugFrame<EndianSlice<'a, BigEndian>>> { + SectionKind(PhantomData) + } + + fn eh_frame_le<'a>() -> SectionKind<EhFrame<EndianSlice<'a, LittleEndian>>> { + SectionKind(PhantomData) + } + + fn parse_fde<Section, O, F, R>( + section: Section, + input: &mut R, + get_cie: F, + ) -> Result<FrameDescriptionEntry<R>> + where + R: Reader, + Section: UnwindSection<R, Offset = O>, + O: UnwindOffset<R::Offset>, + F: FnMut(&Section, &BaseAddresses, O) -> Result<CommonInformationEntry<R>>, + { + let bases = Default::default(); + match parse_cfi_entry(&bases, §ion, input) { + Ok(Some(CieOrFde::Fde(partial))) => partial.parse(get_cie), + Ok(_) => Err(Error::NoEntryAtGivenOffset), + Err(e) => Err(e), + } + } + + // Mixin methods for `Section` to help define binary test data. + + trait CfiSectionMethods: GimliSectionMethods { + fn cie<'aug, 'input, E, T>( + self, + _kind: SectionKind<T>, + augmentation: Option<&'aug str>, + cie: &mut CommonInformationEntry<EndianSlice<'input, E>>, + ) -> Self + where + E: Endianity, + T: UnwindSection<EndianSlice<'input, E>>, + T::Offset: UnwindOffset; + fn fde<'a, 'input, E, T, L>( + self, + _kind: SectionKind<T>, + cie_offset: L, + fde: &mut FrameDescriptionEntry<EndianSlice<'input, E>>, + ) -> Self + where + E: Endianity, + T: UnwindSection<EndianSlice<'input, E>>, + T::Offset: UnwindOffset, + L: ToLabelOrNum<'a, u64>; + } + + impl CfiSectionMethods for Section { + fn cie<'aug, 'input, E, T>( + self, + _kind: SectionKind<T>, + augmentation: Option<&'aug str>, + cie: &mut CommonInformationEntry<EndianSlice<'input, E>>, + ) -> Self + where + E: Endianity, + T: UnwindSection<EndianSlice<'input, E>>, + T::Offset: UnwindOffset, + { + cie.offset = self.size() as _; + let length = Label::new(); + let start = Label::new(); + let end = Label::new(); + + let section = match cie.format { + Format::Dwarf32 => self.D32(&length).mark(&start).D32(0xffff_ffff), + Format::Dwarf64 => { + let section = self.D32(0xffff_ffff); + section.D64(&length).mark(&start).D64(0xffff_ffff_ffff_ffff) + } + }; + + let mut section = section.D8(cie.version); + + if let Some(augmentation) = augmentation { + section = section.append_bytes(augmentation.as_bytes()); + } + + // Null terminator for augmentation string. + let section = section.D8(0); + + let section = if T::has_address_and_segment_sizes(cie.version) { + section.D8(cie.address_size).D8(cie.segment_size) + } else { + section + }; + + let section = section + .uleb(cie.code_alignment_factor) + .sleb(cie.data_alignment_factor) + .uleb(cie.return_address_register.0.into()) + .append_bytes(cie.initial_instructions.slice()) + .mark(&end); + + cie.length = (&end - &start) as usize; + length.set_const(cie.length as u64); + + section + } + + fn fde<'a, 'input, E, T, L>( + self, + _kind: SectionKind<T>, + cie_offset: L, + fde: &mut FrameDescriptionEntry<EndianSlice<'input, E>>, + ) -> Self + where + E: Endianity, + T: UnwindSection<EndianSlice<'input, E>>, + T::Offset: UnwindOffset, + L: ToLabelOrNum<'a, u64>, + { + fde.offset = self.size() as _; + let length = Label::new(); + let start = Label::new(); + let end = Label::new(); + + assert_eq!(fde.format, fde.cie.format); + + let section = match T::cie_offset_encoding(fde.format) { + CieOffsetEncoding::U32 => { + let section = self.D32(&length).mark(&start); + match cie_offset.to_labelornum() { + LabelOrNum::Label(ref l) => section.D32(l), + LabelOrNum::Num(o) => section.D32(o as u32), + } + } + CieOffsetEncoding::U64 => { + let section = self.D32(0xffff_ffff); + section.D64(&length).mark(&start).D64(cie_offset) + } + }; + + let section = match fde.cie.segment_size { + 0 => section, + 4 => section.D32(fde.initial_segment as u32), + 8 => section.D64(fde.initial_segment), + x => panic!("Unsupported test segment size: {}", x), + }; + + let section = match fde.cie.address_size { + 4 => section + .D32(fde.initial_address() as u32) + .D32(fde.len() as u32), + 8 => section.D64(fde.initial_address()).D64(fde.len()), + x => panic!("Unsupported address size: {}", x), + }; + + let section = if let Some(ref augmentation) = fde.augmentation { + let cie_aug = fde + .cie + .augmentation + .expect("FDE has augmentation, but CIE doesn't"); + + if let Some(lsda) = augmentation.lsda { + // We only support writing `DW_EH_PE_absptr` here. + assert_eq!( + cie_aug + .lsda + .expect("FDE has lsda, but CIE doesn't") + .format(), + constants::DW_EH_PE_absptr + ); + + // Augmentation data length + let section = section.uleb(u64::from(fde.cie.address_size)); + match fde.cie.address_size { + 4 => section.D32({ + let x: u64 = lsda.pointer(); + x as u32 + }), + 8 => section.D64({ + let x: u64 = lsda.pointer(); + x + }), + x => panic!("Unsupported address size: {}", x), + } + } else { + // Even if we don't have any augmentation data, if there is + // an augmentation defined, we need to put the length in. + section.uleb(0) + } + } else { + section + }; + + let section = section.append_bytes(fde.instructions.slice()).mark(&end); + + fde.length = (&end - &start) as usize; + length.set_const(fde.length as u64); + + section + } + } + + trait ResultExt { + fn map_eof(self, input: &[u8]) -> Self; + } + + impl<T> ResultExt for Result<T> { + fn map_eof(self, input: &[u8]) -> Self { + match self { + Err(Error::UnexpectedEof(id)) => { + let id = ReaderOffsetId(id.0 - input.as_ptr() as u64); + Err(Error::UnexpectedEof(id)) + } + r => r, + } + } + } + + fn assert_parse_cie<'input, E>( + kind: SectionKind<DebugFrame<EndianSlice<'input, E>>>, + section: Section, + address_size: u8, + expected: Result<( + EndianSlice<'input, E>, + CommonInformationEntry<EndianSlice<'input, E>>, + )>, + ) where + E: Endianity, + { + let section = section.get_contents().unwrap(); + let mut debug_frame = kind.section(§ion); + debug_frame.set_address_size(address_size); + let input = &mut EndianSlice::new(§ion, E::default()); + let bases = Default::default(); + let result = CommonInformationEntry::parse(&bases, &debug_frame, input); + let result = result.map(|cie| (*input, cie)).map_eof(§ion); + assert_eq!(result, expected); + } + + #[test] + fn test_parse_cie_incomplete_length_32() { + let kind = debug_frame_le(); + let section = Section::with_endian(kind.endian()).L16(5); + assert_parse_cie( + kind, + section, + 8, + Err(Error::UnexpectedEof(ReaderOffsetId(0))), + ); + } + + #[test] + fn test_parse_cie_incomplete_length_64() { + let kind = debug_frame_le(); + let section = Section::with_endian(kind.endian()) + .L32(0xffff_ffff) + .L32(12345); + assert_parse_cie( + kind, + section, + 8, + Err(Error::UnexpectedEof(ReaderOffsetId(4))), + ); + } + + #[test] + fn test_parse_cie_incomplete_id_32() { + let kind = debug_frame_be(); + let section = Section::with_endian(kind.endian()) + // The length is not large enough to contain the ID. + .B32(3) + .B32(0xffff_ffff); + assert_parse_cie( + kind, + section, + 8, + Err(Error::UnexpectedEof(ReaderOffsetId(4))), + ); + } + + #[test] + fn test_parse_cie_bad_id_32() { + let kind = debug_frame_be(); + let section = Section::with_endian(kind.endian()) + // Initial length + .B32(4) + // Not the CIE Id. + .B32(0xbad1_bad2); + assert_parse_cie(kind, section, 8, Err(Error::NotCieId)); + } + + #[test] + fn test_parse_cie_32_bad_version() { + let mut cie = CommonInformationEntry { + offset: 0, + length: 0, + format: Format::Dwarf32, + version: 99, + augmentation: None, + address_size: 4, + segment_size: 0, + code_alignment_factor: 1, + data_alignment_factor: 2, + return_address_register: Register(3), + initial_instructions: EndianSlice::new(&[], LittleEndian), + }; + + let kind = debug_frame_le(); + let section = Section::with_endian(kind.endian()).cie(kind, None, &mut cie); + assert_parse_cie(kind, section, 4, Err(Error::UnknownVersion(99))); + } + + #[test] + fn test_parse_cie_unknown_augmentation() { + let length = Label::new(); + let start = Label::new(); + let end = Label::new(); + + let augmentation = Some("replicant"); + let expected_rest = [1, 2, 3]; + + let kind = debug_frame_le(); + let section = Section::with_endian(kind.endian()) + // Initial length + .L32(&length) + .mark(&start) + // CIE Id + .L32(0xffff_ffff) + // Version + .D8(4) + // Augmentation + .append_bytes(augmentation.unwrap().as_bytes()) + // Null terminator + .D8(0) + // Extra augmented data that we can't understand. + .L32(1) + .L32(2) + .L32(3) + .L32(4) + .L32(5) + .L32(6) + .mark(&end) + .append_bytes(&expected_rest); + + let expected_length = (&end - &start) as u64; + length.set_const(expected_length); + + assert_parse_cie(kind, section, 8, Err(Error::UnknownAugmentation)); + } + + fn test_parse_cie(format: Format, version: u8, address_size: u8) { + let expected_rest = [1, 2, 3, 4, 5, 6, 7, 8, 9]; + let expected_instrs: Vec<_> = (0..4).map(|_| constants::DW_CFA_nop.0).collect(); + + let mut cie = CommonInformationEntry { + offset: 0, + length: 0, + format, + version, + augmentation: None, + address_size, + segment_size: 0, + code_alignment_factor: 16, + data_alignment_factor: 32, + return_address_register: Register(1), + initial_instructions: EndianSlice::new(&expected_instrs, LittleEndian), + }; + + let kind = debug_frame_le(); + let section = Section::with_endian(kind.endian()) + .cie(kind, None, &mut cie) + .append_bytes(&expected_rest); + + assert_parse_cie( + kind, + section, + address_size, + Ok((EndianSlice::new(&expected_rest, LittleEndian), cie)), + ); + } + + #[test] + fn test_parse_cie_32_ok() { + test_parse_cie(Format::Dwarf32, 1, 4); + test_parse_cie(Format::Dwarf32, 1, 8); + test_parse_cie(Format::Dwarf32, 4, 4); + test_parse_cie(Format::Dwarf32, 4, 8); + } + + #[test] + fn test_parse_cie_64_ok() { + test_parse_cie(Format::Dwarf64, 1, 4); + test_parse_cie(Format::Dwarf64, 1, 8); + test_parse_cie(Format::Dwarf64, 4, 4); + test_parse_cie(Format::Dwarf64, 4, 8); + } + + #[test] + fn test_parse_cie_length_too_big() { + let expected_instrs: Vec<_> = (0..13).map(|_| constants::DW_CFA_nop.0).collect(); + + let mut cie = CommonInformationEntry { + offset: 0, + length: 0, + format: Format::Dwarf32, + version: 4, + augmentation: None, + address_size: 4, + segment_size: 0, + code_alignment_factor: 0, + data_alignment_factor: 0, + return_address_register: Register(3), + initial_instructions: EndianSlice::new(&expected_instrs, LittleEndian), + }; + + let kind = debug_frame_le(); + let section = Section::with_endian(kind.endian()).cie(kind, None, &mut cie); + + let mut contents = section.get_contents().unwrap(); + + // Overwrite the length to be too big. + contents[0] = 0; + contents[1] = 0; + contents[2] = 0; + contents[3] = 255; + + let debug_frame = DebugFrame::new(&contents, LittleEndian); + let bases = Default::default(); + assert_eq!( + CommonInformationEntry::parse( + &bases, + &debug_frame, + &mut EndianSlice::new(&contents, LittleEndian) + ) + .map_eof(&contents), + Err(Error::UnexpectedEof(ReaderOffsetId(4))) + ); + } + + #[test] + fn test_parse_fde_incomplete_length_32() { + let kind = debug_frame_le(); + let section = Section::with_endian(kind.endian()).L16(5); + let section = section.get_contents().unwrap(); + let debug_frame = kind.section(§ion); + let rest = &mut EndianSlice::new(§ion, LittleEndian); + assert_eq!( + parse_fde(debug_frame, rest, UnwindSection::cie_from_offset).map_eof(§ion), + Err(Error::UnexpectedEof(ReaderOffsetId(0))) + ); + } + + #[test] + fn test_parse_fde_incomplete_length_64() { + let kind = debug_frame_le(); + let section = Section::with_endian(kind.endian()) + .L32(0xffff_ffff) + .L32(12345); + let section = section.get_contents().unwrap(); + let debug_frame = kind.section(§ion); + let rest = &mut EndianSlice::new(§ion, LittleEndian); + assert_eq!( + parse_fde(debug_frame, rest, UnwindSection::cie_from_offset).map_eof(§ion), + Err(Error::UnexpectedEof(ReaderOffsetId(4))) + ); + } + + #[test] + fn test_parse_fde_incomplete_cie_pointer_32() { + let kind = debug_frame_be(); + let section = Section::with_endian(kind.endian()) + // The length is not large enough to contain the CIE pointer. + .B32(3) + .B32(1994); + let section = section.get_contents().unwrap(); + let debug_frame = kind.section(§ion); + let rest = &mut EndianSlice::new(§ion, BigEndian); + assert_eq!( + parse_fde(debug_frame, rest, UnwindSection::cie_from_offset).map_eof(§ion), + Err(Error::UnexpectedEof(ReaderOffsetId(4))) + ); + } + + #[test] + fn test_parse_fde_32_ok() { + let expected_rest = [1, 2, 3, 4, 5, 6, 7, 8, 9]; + let cie_offset = 0xbad0_bad1; + let expected_instrs: Vec<_> = (0..7).map(|_| constants::DW_CFA_nop.0).collect(); + + let cie = CommonInformationEntry { + offset: 0, + length: 100, + format: Format::Dwarf32, + version: 4, + augmentation: None, + // DWARF32 with a 64 bit address size! Holy moly! + address_size: 8, + segment_size: 0, + code_alignment_factor: 3, + data_alignment_factor: 2, + return_address_register: Register(1), + initial_instructions: EndianSlice::new(&[], LittleEndian), + }; + + let mut fde = FrameDescriptionEntry { + offset: 0, + length: 0, + format: Format::Dwarf32, + cie: cie.clone(), + initial_segment: 0, + initial_address: 0xfeed_beef, + address_range: 39, + augmentation: None, + instructions: EndianSlice::new(&expected_instrs, LittleEndian), + }; + + let kind = debug_frame_le(); + let section = Section::with_endian(kind.endian()) + .fde(kind, cie_offset, &mut fde) + .append_bytes(&expected_rest); + + let section = section.get_contents().unwrap(); + let debug_frame = kind.section(§ion); + let rest = &mut EndianSlice::new(§ion, LittleEndian); + + let get_cie = |_: &_, _: &_, offset| { + assert_eq!(offset, DebugFrameOffset(cie_offset as usize)); + Ok(cie.clone()) + }; + + assert_eq!(parse_fde(debug_frame, rest, get_cie), Ok(fde)); + assert_eq!(*rest, EndianSlice::new(&expected_rest, LittleEndian)); + } + + #[test] + fn test_parse_fde_32_with_segment_ok() { + let expected_rest = [1, 2, 3, 4, 5, 6, 7, 8, 9]; + let cie_offset = 0xbad0_bad1; + let expected_instrs: Vec<_> = (0..92).map(|_| constants::DW_CFA_nop.0).collect(); + + let cie = CommonInformationEntry { + offset: 0, + length: 100, + format: Format::Dwarf32, + version: 4, + augmentation: None, + address_size: 4, + segment_size: 4, + code_alignment_factor: 3, + data_alignment_factor: 2, + return_address_register: Register(1), + initial_instructions: EndianSlice::new(&[], LittleEndian), + }; + + let mut fde = FrameDescriptionEntry { + offset: 0, + length: 0, + format: Format::Dwarf32, + cie: cie.clone(), + initial_segment: 0xbadb_ad11, + initial_address: 0xfeed_beef, + address_range: 999, + augmentation: None, + instructions: EndianSlice::new(&expected_instrs, LittleEndian), + }; + + let kind = debug_frame_le(); + let section = Section::with_endian(kind.endian()) + .fde(kind, cie_offset, &mut fde) + .append_bytes(&expected_rest); + + let section = section.get_contents().unwrap(); + let debug_frame = kind.section(§ion); + let rest = &mut EndianSlice::new(§ion, LittleEndian); + + let get_cie = |_: &_, _: &_, offset| { + assert_eq!(offset, DebugFrameOffset(cie_offset as usize)); + Ok(cie.clone()) + }; + + assert_eq!(parse_fde(debug_frame, rest, get_cie), Ok(fde)); + assert_eq!(*rest, EndianSlice::new(&expected_rest, LittleEndian)); + } + + #[test] + fn test_parse_fde_64_ok() { + let expected_rest = [1, 2, 3, 4, 5, 6, 7, 8, 9]; + let cie_offset = 0xbad0_bad1; + let expected_instrs: Vec<_> = (0..7).map(|_| constants::DW_CFA_nop.0).collect(); + + let cie = CommonInformationEntry { + offset: 0, + length: 100, + format: Format::Dwarf64, + version: 4, + augmentation: None, + address_size: 8, + segment_size: 0, + code_alignment_factor: 3, + data_alignment_factor: 2, + return_address_register: Register(1), + initial_instructions: EndianSlice::new(&[], LittleEndian), + }; + + let mut fde = FrameDescriptionEntry { + offset: 0, + length: 0, + format: Format::Dwarf64, + cie: cie.clone(), + initial_segment: 0, + initial_address: 0xfeed_beef, + address_range: 999, + augmentation: None, + instructions: EndianSlice::new(&expected_instrs, LittleEndian), + }; + + let kind = debug_frame_le(); + let section = Section::with_endian(kind.endian()) + .fde(kind, cie_offset, &mut fde) + .append_bytes(&expected_rest); + + let section = section.get_contents().unwrap(); + let debug_frame = kind.section(§ion); + let rest = &mut EndianSlice::new(§ion, LittleEndian); + + let get_cie = |_: &_, _: &_, offset| { + assert_eq!(offset, DebugFrameOffset(cie_offset as usize)); + Ok(cie.clone()) + }; + + assert_eq!(parse_fde(debug_frame, rest, get_cie), Ok(fde)); + assert_eq!(*rest, EndianSlice::new(&expected_rest, LittleEndian)); + } + + #[test] + fn test_parse_cfi_entry_on_cie_32_ok() { + let expected_rest = [1, 2, 3, 4, 5, 6, 7, 8, 9]; + let expected_instrs: Vec<_> = (0..4).map(|_| constants::DW_CFA_nop.0).collect(); + + let mut cie = CommonInformationEntry { + offset: 0, + length: 0, + format: Format::Dwarf32, + version: 4, + augmentation: None, + address_size: 4, + segment_size: 0, + code_alignment_factor: 16, + data_alignment_factor: 32, + return_address_register: Register(1), + initial_instructions: EndianSlice::new(&expected_instrs, BigEndian), + }; + + let kind = debug_frame_be(); + let section = Section::with_endian(kind.endian()) + .cie(kind, None, &mut cie) + .append_bytes(&expected_rest); + let section = section.get_contents().unwrap(); + let debug_frame = kind.section(§ion); + let rest = &mut EndianSlice::new(§ion, BigEndian); + + let bases = Default::default(); + assert_eq!( + parse_cfi_entry(&bases, &debug_frame, rest), + Ok(Some(CieOrFde::Cie(cie))) + ); + assert_eq!(*rest, EndianSlice::new(&expected_rest, BigEndian)); + } + + #[test] + fn test_parse_cfi_entry_on_fde_32_ok() { + let cie_offset = 0x1234_5678; + let expected_rest = [1, 2, 3, 4, 5, 6, 7, 8, 9]; + let expected_instrs: Vec<_> = (0..4).map(|_| constants::DW_CFA_nop.0).collect(); + + let cie = CommonInformationEntry { + offset: 0, + length: 0, + format: Format::Dwarf32, + version: 4, + augmentation: None, + address_size: 4, + segment_size: 0, + code_alignment_factor: 16, + data_alignment_factor: 32, + return_address_register: Register(1), + initial_instructions: EndianSlice::new(&[], BigEndian), + }; + + let mut fde = FrameDescriptionEntry { + offset: 0, + length: 0, + format: Format::Dwarf32, + cie: cie.clone(), + initial_segment: 0, + initial_address: 0xfeed_beef, + address_range: 39, + augmentation: None, + instructions: EndianSlice::new(&expected_instrs, BigEndian), + }; + + let kind = debug_frame_be(); + let section = Section::with_endian(kind.endian()) + .fde(kind, cie_offset, &mut fde) + .append_bytes(&expected_rest); + + let section = section.get_contents().unwrap(); + let debug_frame = kind.section(§ion); + let rest = &mut EndianSlice::new(§ion, BigEndian); + + let bases = Default::default(); + match parse_cfi_entry(&bases, &debug_frame, rest) { + Ok(Some(CieOrFde::Fde(partial))) => { + assert_eq!(*rest, EndianSlice::new(&expected_rest, BigEndian)); + + assert_eq!(partial.length, fde.length); + assert_eq!(partial.format, fde.format); + assert_eq!(partial.cie_offset, DebugFrameOffset(cie_offset as usize)); + + let get_cie = |_: &_, _: &_, offset| { + assert_eq!(offset, DebugFrameOffset(cie_offset as usize)); + Ok(cie.clone()) + }; + + assert_eq!(partial.parse(get_cie), Ok(fde)); + } + otherwise => panic!("Unexpected result: {:#?}", otherwise), + } + } + + #[test] + fn test_cfi_entries_iter() { + let expected_instrs1: Vec<_> = (0..4).map(|_| constants::DW_CFA_nop.0).collect(); + + let expected_instrs2: Vec<_> = (0..8).map(|_| constants::DW_CFA_nop.0).collect(); + + let expected_instrs3: Vec<_> = (0..12).map(|_| constants::DW_CFA_nop.0).collect(); + + let expected_instrs4: Vec<_> = (0..16).map(|_| constants::DW_CFA_nop.0).collect(); + + let mut cie1 = CommonInformationEntry { + offset: 0, + length: 0, + format: Format::Dwarf32, + version: 4, + augmentation: None, + address_size: 4, + segment_size: 0, + code_alignment_factor: 1, + data_alignment_factor: 2, + return_address_register: Register(3), + initial_instructions: EndianSlice::new(&expected_instrs1, BigEndian), + }; + + let mut cie2 = CommonInformationEntry { + offset: 0, + length: 0, + format: Format::Dwarf32, + version: 4, + augmentation: None, + address_size: 4, + segment_size: 0, + code_alignment_factor: 3, + data_alignment_factor: 2, + return_address_register: Register(1), + initial_instructions: EndianSlice::new(&expected_instrs2, BigEndian), + }; + + let cie1_location = Label::new(); + let cie2_location = Label::new(); + + // Write the CIEs first so that their length gets set before we clone + // them into the FDEs and our equality assertions down the line end up + // with all the CIEs always having he correct length. + let kind = debug_frame_be(); + let section = Section::with_endian(kind.endian()) + .mark(&cie1_location) + .cie(kind, None, &mut cie1) + .mark(&cie2_location) + .cie(kind, None, &mut cie2); + + let mut fde1 = FrameDescriptionEntry { + offset: 0, + length: 0, + format: Format::Dwarf32, + cie: cie1.clone(), + initial_segment: 0, + initial_address: 0xfeed_beef, + address_range: 39, + augmentation: None, + instructions: EndianSlice::new(&expected_instrs3, BigEndian), + }; + + let mut fde2 = FrameDescriptionEntry { + offset: 0, + length: 0, + format: Format::Dwarf32, + cie: cie2.clone(), + initial_segment: 0, + initial_address: 0xfeed_face, + address_range: 9000, + augmentation: None, + instructions: EndianSlice::new(&expected_instrs4, BigEndian), + }; + + let section = + section + .fde(kind, &cie1_location, &mut fde1) + .fde(kind, &cie2_location, &mut fde2); + + section.start().set_const(0); + + let cie1_offset = cie1_location.value().unwrap() as usize; + let cie2_offset = cie2_location.value().unwrap() as usize; + + let contents = section.get_contents().unwrap(); + let debug_frame = kind.section(&contents); + + let bases = Default::default(); + let mut entries = debug_frame.entries(&bases); + + assert_eq!(entries.next(), Ok(Some(CieOrFde::Cie(cie1.clone())))); + assert_eq!(entries.next(), Ok(Some(CieOrFde::Cie(cie2.clone())))); + + match entries.next() { + Ok(Some(CieOrFde::Fde(partial))) => { + assert_eq!(partial.length, fde1.length); + assert_eq!(partial.format, fde1.format); + assert_eq!(partial.cie_offset, DebugFrameOffset(cie1_offset)); + + let get_cie = |_: &_, _: &_, offset| { + assert_eq!(offset, DebugFrameOffset(cie1_offset)); + Ok(cie1.clone()) + }; + assert_eq!(partial.parse(get_cie), Ok(fde1)); + } + otherwise => panic!("Unexpected result: {:#?}", otherwise), + } + + match entries.next() { + Ok(Some(CieOrFde::Fde(partial))) => { + assert_eq!(partial.length, fde2.length); + assert_eq!(partial.format, fde2.format); + assert_eq!(partial.cie_offset, DebugFrameOffset(cie2_offset)); + + let get_cie = |_: &_, _: &_, offset| { + assert_eq!(offset, DebugFrameOffset(cie2_offset)); + Ok(cie2.clone()) + }; + assert_eq!(partial.parse(get_cie), Ok(fde2)); + } + otherwise => panic!("Unexpected result: {:#?}", otherwise), + } + + assert_eq!(entries.next(), Ok(None)); + } + + #[test] + fn test_parse_cie_from_offset() { + let filler = [1, 2, 3, 4, 5, 6, 7, 8, 9]; + let instrs: Vec<_> = (0..5).map(|_| constants::DW_CFA_nop.0).collect(); + + let mut cie = CommonInformationEntry { + offset: 0, + length: 0, + format: Format::Dwarf64, + version: 4, + augmentation: None, + address_size: 4, + segment_size: 0, + code_alignment_factor: 4, + data_alignment_factor: 8, + return_address_register: Register(12), + initial_instructions: EndianSlice::new(&instrs, LittleEndian), + }; + + let cie_location = Label::new(); + + let kind = debug_frame_le(); + let section = Section::with_endian(kind.endian()) + .append_bytes(&filler) + .mark(&cie_location) + .cie(kind, None, &mut cie) + .append_bytes(&filler); + + section.start().set_const(0); + + let cie_offset = DebugFrameOffset(cie_location.value().unwrap() as usize); + + let contents = section.get_contents().unwrap(); + let debug_frame = kind.section(&contents); + let bases = Default::default(); + + assert_eq!(debug_frame.cie_from_offset(&bases, cie_offset), Ok(cie)); + } + + fn parse_cfi_instruction<R: Reader + Default>( + input: &mut R, + address_size: u8, + ) -> Result<CallFrameInstruction<R>> { + let parameters = &PointerEncodingParameters { + bases: &SectionBaseAddresses::default(), + func_base: None, + address_size, + section: &R::default(), + }; + CallFrameInstruction::parse(input, None, parameters, Vendor::Default) + } + + #[test] + fn test_parse_cfi_instruction_advance_loc() { + let expected_rest = [1, 2, 3, 4]; + let expected_delta = 42; + let section = Section::with_endian(Endian::Little) + .D8(constants::DW_CFA_advance_loc.0 | expected_delta) + .append_bytes(&expected_rest); + let contents = section.get_contents().unwrap(); + let input = &mut EndianSlice::new(&contents, LittleEndian); + assert_eq!( + parse_cfi_instruction(input, 8), + Ok(CallFrameInstruction::AdvanceLoc { + delta: u32::from(expected_delta), + }) + ); + assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); + } + + #[test] + fn test_parse_cfi_instruction_offset() { + let expected_rest = [1, 2, 3, 4]; + let expected_reg = 3; + let expected_offset = 1997; + let section = Section::with_endian(Endian::Little) + .D8(constants::DW_CFA_offset.0 | expected_reg) + .uleb(expected_offset) + .append_bytes(&expected_rest); + let contents = section.get_contents().unwrap(); + let input = &mut EndianSlice::new(&contents, LittleEndian); + assert_eq!( + parse_cfi_instruction(input, 8), + Ok(CallFrameInstruction::Offset { + register: Register(expected_reg.into()), + factored_offset: expected_offset, + }) + ); + assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); + } + + #[test] + fn test_parse_cfi_instruction_restore() { + let expected_rest = [1, 2, 3, 4]; + let expected_reg = 3; + let section = Section::with_endian(Endian::Little) + .D8(constants::DW_CFA_restore.0 | expected_reg) + .append_bytes(&expected_rest); + let contents = section.get_contents().unwrap(); + let input = &mut EndianSlice::new(&contents, LittleEndian); + assert_eq!( + parse_cfi_instruction(input, 8), + Ok(CallFrameInstruction::Restore { + register: Register(expected_reg.into()), + }) + ); + assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); + } + + #[test] + fn test_parse_cfi_instruction_nop() { + let expected_rest = [1, 2, 3, 4]; + let section = Section::with_endian(Endian::Little) + .D8(constants::DW_CFA_nop.0) + .append_bytes(&expected_rest); + let contents = section.get_contents().unwrap(); + let input = &mut EndianSlice::new(&contents, LittleEndian); + assert_eq!( + parse_cfi_instruction(input, 8), + Ok(CallFrameInstruction::Nop) + ); + assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); + } + + #[test] + fn test_parse_cfi_instruction_set_loc() { + let expected_rest = [1, 2, 3, 4]; + let expected_addr = 0xdead_beef; + let section = Section::with_endian(Endian::Little) + .D8(constants::DW_CFA_set_loc.0) + .L64(expected_addr) + .append_bytes(&expected_rest); + let contents = section.get_contents().unwrap(); + let input = &mut EndianSlice::new(&contents, LittleEndian); + assert_eq!( + parse_cfi_instruction(input, 8), + Ok(CallFrameInstruction::SetLoc { + address: expected_addr, + }) + ); + assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); + } + + #[test] + fn test_parse_cfi_instruction_set_loc_encoding() { + let text_base = 0xfeed_face; + let addr_offset = 0xbeef; + let expected_addr = text_base + addr_offset; + let expected_rest = [1, 2, 3, 4]; + let section = Section::with_endian(Endian::Little) + .D8(constants::DW_CFA_set_loc.0) + .L64(addr_offset) + .append_bytes(&expected_rest); + let contents = section.get_contents().unwrap(); + let input = &mut EndianSlice::new(&contents, LittleEndian); + let parameters = &PointerEncodingParameters { + bases: &BaseAddresses::default().set_text(text_base).eh_frame, + func_base: None, + address_size: 8, + section: &EndianSlice::new(&[], LittleEndian), + }; + assert_eq!( + CallFrameInstruction::parse( + input, + Some(constants::DW_EH_PE_textrel), + parameters, + Vendor::Default + ), + Ok(CallFrameInstruction::SetLoc { + address: expected_addr, + }) + ); + assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); + } + + #[test] + fn test_parse_cfi_instruction_advance_loc1() { + let expected_rest = [1, 2, 3, 4]; + let expected_delta = 8; + let section = Section::with_endian(Endian::Little) + .D8(constants::DW_CFA_advance_loc1.0) + .D8(expected_delta) + .append_bytes(&expected_rest); + let contents = section.get_contents().unwrap(); + let input = &mut EndianSlice::new(&contents, LittleEndian); + assert_eq!( + parse_cfi_instruction(input, 8), + Ok(CallFrameInstruction::AdvanceLoc { + delta: u32::from(expected_delta), + }) + ); + assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); + } + + #[test] + fn test_parse_cfi_instruction_advance_loc2() { + let expected_rest = [1, 2, 3, 4]; + let expected_delta = 500; + let section = Section::with_endian(Endian::Little) + .D8(constants::DW_CFA_advance_loc2.0) + .L16(expected_delta) + .append_bytes(&expected_rest); + let contents = section.get_contents().unwrap(); + let input = &mut EndianSlice::new(&contents, LittleEndian); + assert_eq!( + parse_cfi_instruction(input, 8), + Ok(CallFrameInstruction::AdvanceLoc { + delta: u32::from(expected_delta), + }) + ); + assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); + } + + #[test] + fn test_parse_cfi_instruction_advance_loc4() { + let expected_rest = [1, 2, 3, 4]; + let expected_delta = 1 << 20; + let section = Section::with_endian(Endian::Little) + .D8(constants::DW_CFA_advance_loc4.0) + .L32(expected_delta) + .append_bytes(&expected_rest); + let contents = section.get_contents().unwrap(); + let input = &mut EndianSlice::new(&contents, LittleEndian); + assert_eq!( + parse_cfi_instruction(input, 8), + Ok(CallFrameInstruction::AdvanceLoc { + delta: expected_delta, + }) + ); + assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); + } + + #[test] + fn test_parse_cfi_instruction_offset_extended() { + let expected_rest = [1, 2, 3, 4]; + let expected_reg = 7; + let expected_offset = 33; + let section = Section::with_endian(Endian::Little) + .D8(constants::DW_CFA_offset_extended.0) + .uleb(expected_reg.into()) + .uleb(expected_offset) + .append_bytes(&expected_rest); + let contents = section.get_contents().unwrap(); + let input = &mut EndianSlice::new(&contents, LittleEndian); + assert_eq!( + parse_cfi_instruction(input, 8), + Ok(CallFrameInstruction::Offset { + register: Register(expected_reg), + factored_offset: expected_offset, + }) + ); + assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); + } + + #[test] + fn test_parse_cfi_instruction_restore_extended() { + let expected_rest = [1, 2, 3, 4]; + let expected_reg = 7; + let section = Section::with_endian(Endian::Little) + .D8(constants::DW_CFA_restore_extended.0) + .uleb(expected_reg.into()) + .append_bytes(&expected_rest); + let contents = section.get_contents().unwrap(); + let input = &mut EndianSlice::new(&contents, LittleEndian); + assert_eq!( + parse_cfi_instruction(input, 8), + Ok(CallFrameInstruction::Restore { + register: Register(expected_reg), + }) + ); + assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); + } + + #[test] + fn test_parse_cfi_instruction_undefined() { + let expected_rest = [1, 2, 3, 4]; + let expected_reg = 7; + let section = Section::with_endian(Endian::Little) + .D8(constants::DW_CFA_undefined.0) + .uleb(expected_reg.into()) + .append_bytes(&expected_rest); + let contents = section.get_contents().unwrap(); + let input = &mut EndianSlice::new(&contents, LittleEndian); + assert_eq!( + parse_cfi_instruction(input, 8), + Ok(CallFrameInstruction::Undefined { + register: Register(expected_reg), + }) + ); + assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); + } + + #[test] + fn test_parse_cfi_instruction_same_value() { + let expected_rest = [1, 2, 3, 4]; + let expected_reg = 7; + let section = Section::with_endian(Endian::Little) + .D8(constants::DW_CFA_same_value.0) + .uleb(expected_reg.into()) + .append_bytes(&expected_rest); + let contents = section.get_contents().unwrap(); + let input = &mut EndianSlice::new(&contents, LittleEndian); + assert_eq!( + parse_cfi_instruction(input, 8), + Ok(CallFrameInstruction::SameValue { + register: Register(expected_reg), + }) + ); + assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); + } + + #[test] + fn test_parse_cfi_instruction_register() { + let expected_rest = [1, 2, 3, 4]; + let expected_dest_reg = 7; + let expected_src_reg = 8; + let section = Section::with_endian(Endian::Little) + .D8(constants::DW_CFA_register.0) + .uleb(expected_dest_reg.into()) + .uleb(expected_src_reg.into()) + .append_bytes(&expected_rest); + let contents = section.get_contents().unwrap(); + let input = &mut EndianSlice::new(&contents, LittleEndian); + assert_eq!( + parse_cfi_instruction(input, 8), + Ok(CallFrameInstruction::Register { + dest_register: Register(expected_dest_reg), + src_register: Register(expected_src_reg), + }) + ); + assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); + } + + #[test] + fn test_parse_cfi_instruction_remember_state() { + let expected_rest = [1, 2, 3, 4]; + let section = Section::with_endian(Endian::Little) + .D8(constants::DW_CFA_remember_state.0) + .append_bytes(&expected_rest); + let contents = section.get_contents().unwrap(); + let input = &mut EndianSlice::new(&contents, LittleEndian); + assert_eq!( + parse_cfi_instruction(input, 8), + Ok(CallFrameInstruction::RememberState) + ); + assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); + } + + #[test] + fn test_parse_cfi_instruction_restore_state() { + let expected_rest = [1, 2, 3, 4]; + let section = Section::with_endian(Endian::Little) + .D8(constants::DW_CFA_restore_state.0) + .append_bytes(&expected_rest); + let contents = section.get_contents().unwrap(); + let input = &mut EndianSlice::new(&contents, LittleEndian); + assert_eq!( + parse_cfi_instruction(input, 8), + Ok(CallFrameInstruction::RestoreState) + ); + assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); + } + + #[test] + fn test_parse_cfi_instruction_def_cfa() { + let expected_rest = [1, 2, 3, 4]; + let expected_reg = 2; + let expected_offset = 0; + let section = Section::with_endian(Endian::Little) + .D8(constants::DW_CFA_def_cfa.0) + .uleb(expected_reg.into()) + .uleb(expected_offset) + .append_bytes(&expected_rest); + let contents = section.get_contents().unwrap(); + let input = &mut EndianSlice::new(&contents, LittleEndian); + assert_eq!( + parse_cfi_instruction(input, 8), + Ok(CallFrameInstruction::DefCfa { + register: Register(expected_reg), + offset: expected_offset, + }) + ); + assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); + } + + #[test] + fn test_parse_cfi_instruction_def_cfa_register() { + let expected_rest = [1, 2, 3, 4]; + let expected_reg = 2; + let section = Section::with_endian(Endian::Little) + .D8(constants::DW_CFA_def_cfa_register.0) + .uleb(expected_reg.into()) + .append_bytes(&expected_rest); + let contents = section.get_contents().unwrap(); + let input = &mut EndianSlice::new(&contents, LittleEndian); + assert_eq!( + parse_cfi_instruction(input, 8), + Ok(CallFrameInstruction::DefCfaRegister { + register: Register(expected_reg), + }) + ); + assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); + } + + #[test] + fn test_parse_cfi_instruction_def_cfa_offset() { + let expected_rest = [1, 2, 3, 4]; + let expected_offset = 23; + let section = Section::with_endian(Endian::Little) + .D8(constants::DW_CFA_def_cfa_offset.0) + .uleb(expected_offset) + .append_bytes(&expected_rest); + let contents = section.get_contents().unwrap(); + let input = &mut EndianSlice::new(&contents, LittleEndian); + assert_eq!( + parse_cfi_instruction(input, 8), + Ok(CallFrameInstruction::DefCfaOffset { + offset: expected_offset, + }) + ); + assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); + } + + #[test] + fn test_parse_cfi_instruction_def_cfa_expression() { + let expected_rest = [1, 2, 3, 4]; + let expected_expr = [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]; + + let length = Label::new(); + let start = Label::new(); + let end = Label::new(); + + let section = Section::with_endian(Endian::Little) + .D8(constants::DW_CFA_def_cfa_expression.0) + .D8(&length) + .mark(&start) + .append_bytes(&expected_expr) + .mark(&end) + .append_bytes(&expected_rest); + + length.set_const((&end - &start) as u64); + let contents = section.get_contents().unwrap(); + let input = &mut EndianSlice::new(&contents, LittleEndian); + + assert_eq!( + parse_cfi_instruction(input, 8), + Ok(CallFrameInstruction::DefCfaExpression { + expression: Expression(EndianSlice::new(&expected_expr, LittleEndian)), + }) + ); + assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); + } + + #[test] + fn test_parse_cfi_instruction_expression() { + let expected_rest = [1, 2, 3, 4]; + let expected_reg = 99; + let expected_expr = [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]; + + let length = Label::new(); + let start = Label::new(); + let end = Label::new(); + + let section = Section::with_endian(Endian::Little) + .D8(constants::DW_CFA_expression.0) + .uleb(expected_reg.into()) + .D8(&length) + .mark(&start) + .append_bytes(&expected_expr) + .mark(&end) + .append_bytes(&expected_rest); + + length.set_const((&end - &start) as u64); + let contents = section.get_contents().unwrap(); + let input = &mut EndianSlice::new(&contents, LittleEndian); + + assert_eq!( + parse_cfi_instruction(input, 8), + Ok(CallFrameInstruction::Expression { + register: Register(expected_reg), + expression: Expression(EndianSlice::new(&expected_expr, LittleEndian)), + }) + ); + assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); + } + + #[test] + fn test_parse_cfi_instruction_offset_extended_sf() { + let expected_rest = [1, 2, 3, 4]; + let expected_reg = 7; + let expected_offset = -33; + let section = Section::with_endian(Endian::Little) + .D8(constants::DW_CFA_offset_extended_sf.0) + .uleb(expected_reg.into()) + .sleb(expected_offset) + .append_bytes(&expected_rest); + let contents = section.get_contents().unwrap(); + let input = &mut EndianSlice::new(&contents, LittleEndian); + assert_eq!( + parse_cfi_instruction(input, 8), + Ok(CallFrameInstruction::OffsetExtendedSf { + register: Register(expected_reg), + factored_offset: expected_offset, + }) + ); + assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); + } + + #[test] + fn test_parse_cfi_instruction_def_cfa_sf() { + let expected_rest = [1, 2, 3, 4]; + let expected_reg = 2; + let expected_offset = -9999; + let section = Section::with_endian(Endian::Little) + .D8(constants::DW_CFA_def_cfa_sf.0) + .uleb(expected_reg.into()) + .sleb(expected_offset) + .append_bytes(&expected_rest); + let contents = section.get_contents().unwrap(); + let input = &mut EndianSlice::new(&contents, LittleEndian); + assert_eq!( + parse_cfi_instruction(input, 8), + Ok(CallFrameInstruction::DefCfaSf { + register: Register(expected_reg), + factored_offset: expected_offset, + }) + ); + assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); + } + + #[test] + fn test_parse_cfi_instruction_def_cfa_offset_sf() { + let expected_rest = [1, 2, 3, 4]; + let expected_offset = -123; + let section = Section::with_endian(Endian::Little) + .D8(constants::DW_CFA_def_cfa_offset_sf.0) + .sleb(expected_offset) + .append_bytes(&expected_rest); + let contents = section.get_contents().unwrap(); + let input = &mut EndianSlice::new(&contents, LittleEndian); + assert_eq!( + parse_cfi_instruction(input, 8), + Ok(CallFrameInstruction::DefCfaOffsetSf { + factored_offset: expected_offset, + }) + ); + assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); + } + + #[test] + fn test_parse_cfi_instruction_val_offset() { + let expected_rest = [1, 2, 3, 4]; + let expected_reg = 50; + let expected_offset = 23; + let section = Section::with_endian(Endian::Little) + .D8(constants::DW_CFA_val_offset.0) + .uleb(expected_reg.into()) + .uleb(expected_offset) + .append_bytes(&expected_rest); + let contents = section.get_contents().unwrap(); + let input = &mut EndianSlice::new(&contents, LittleEndian); + assert_eq!( + parse_cfi_instruction(input, 8), + Ok(CallFrameInstruction::ValOffset { + register: Register(expected_reg), + factored_offset: expected_offset, + }) + ); + assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); + } + + #[test] + fn test_parse_cfi_instruction_val_offset_sf() { + let expected_rest = [1, 2, 3, 4]; + let expected_reg = 50; + let expected_offset = -23; + let section = Section::with_endian(Endian::Little) + .D8(constants::DW_CFA_val_offset_sf.0) + .uleb(expected_reg.into()) + .sleb(expected_offset) + .append_bytes(&expected_rest); + let contents = section.get_contents().unwrap(); + let input = &mut EndianSlice::new(&contents, LittleEndian); + assert_eq!( + parse_cfi_instruction(input, 8), + Ok(CallFrameInstruction::ValOffsetSf { + register: Register(expected_reg), + factored_offset: expected_offset, + }) + ); + assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); + } + + #[test] + fn test_parse_cfi_instruction_val_expression() { + let expected_rest = [1, 2, 3, 4]; + let expected_reg = 50; + let expected_expr = [2, 2, 1, 1, 5, 5]; + + let length = Label::new(); + let start = Label::new(); + let end = Label::new(); + + let section = Section::with_endian(Endian::Little) + .D8(constants::DW_CFA_val_expression.0) + .uleb(expected_reg.into()) + .D8(&length) + .mark(&start) + .append_bytes(&expected_expr) + .mark(&end) + .append_bytes(&expected_rest); + + length.set_const((&end - &start) as u64); + let contents = section.get_contents().unwrap(); + let input = &mut EndianSlice::new(&contents, LittleEndian); + + assert_eq!( + parse_cfi_instruction(input, 8), + Ok(CallFrameInstruction::ValExpression { + register: Register(expected_reg), + expression: Expression(EndianSlice::new(&expected_expr, LittleEndian)), + }) + ); + assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); + } + + #[test] + fn test_parse_cfi_instruction_negate_ra_state() { + let expected_rest = [1, 2, 3, 4]; + let section = Section::with_endian(Endian::Little) + .D8(constants::DW_CFA_AARCH64_negate_ra_state.0) + .append_bytes(&expected_rest); + let contents = section.get_contents().unwrap(); + let input = &mut EndianSlice::new(&contents, LittleEndian); + let parameters = &PointerEncodingParameters { + bases: &SectionBaseAddresses::default(), + func_base: None, + address_size: 8, + section: &EndianSlice::default(), + }; + assert_eq!( + CallFrameInstruction::parse(input, None, parameters, Vendor::AArch64), + Ok(CallFrameInstruction::NegateRaState) + ); + assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); + } + + #[test] + fn test_parse_cfi_instruction_unknown_instruction() { + let expected_rest = [1, 2, 3, 4]; + let unknown_instr = constants::DwCfa(0b0011_1111); + let section = Section::with_endian(Endian::Little) + .D8(unknown_instr.0) + .append_bytes(&expected_rest); + let contents = section.get_contents().unwrap(); + let input = &mut EndianSlice::new(&contents, LittleEndian); + assert_eq!( + parse_cfi_instruction(input, 8), + Err(Error::UnknownCallFrameInstruction(unknown_instr)) + ); + } + + #[test] + fn test_call_frame_instruction_iter_ok() { + let expected_reg = 50; + let expected_expr = [2, 2, 1, 1, 5, 5]; + let expected_delta = 230; + + let length = Label::new(); + let start = Label::new(); + let end = Label::new(); + + let section = Section::with_endian(Endian::Big) + .D8(constants::DW_CFA_val_expression.0) + .uleb(expected_reg.into()) + .D8(&length) + .mark(&start) + .append_bytes(&expected_expr) + .mark(&end) + .D8(constants::DW_CFA_advance_loc1.0) + .D8(expected_delta); + + length.set_const((&end - &start) as u64); + let contents = section.get_contents().unwrap(); + let input = EndianSlice::new(&contents, BigEndian); + let parameters = PointerEncodingParameters { + bases: &SectionBaseAddresses::default(), + func_base: None, + address_size: 8, + section: &EndianSlice::default(), + }; + let mut iter = CallFrameInstructionIter { + input, + address_encoding: None, + parameters, + vendor: Vendor::Default, + }; + + assert_eq!( + iter.next(), + Ok(Some(CallFrameInstruction::ValExpression { + register: Register(expected_reg), + expression: Expression(EndianSlice::new(&expected_expr, BigEndian)), + })) + ); + + assert_eq!( + iter.next(), + Ok(Some(CallFrameInstruction::AdvanceLoc { + delta: u32::from(expected_delta), + })) + ); + + assert_eq!(iter.next(), Ok(None)); + } + + #[test] + fn test_call_frame_instruction_iter_err() { + // DW_CFA_advance_loc1 without an operand. + let section = Section::with_endian(Endian::Big).D8(constants::DW_CFA_advance_loc1.0); + + let contents = section.get_contents().unwrap(); + let input = EndianSlice::new(&contents, BigEndian); + let parameters = PointerEncodingParameters { + bases: &SectionBaseAddresses::default(), + func_base: None, + address_size: 8, + section: &EndianSlice::default(), + }; + let mut iter = CallFrameInstructionIter { + input, + address_encoding: None, + parameters, + vendor: Vendor::Default, + }; + + assert_eq!( + iter.next().map_eof(&contents), + Err(Error::UnexpectedEof(ReaderOffsetId(1))) + ); + assert_eq!(iter.next(), Ok(None)); + } + + fn assert_eval<'a, I>( + mut initial_ctx: UnwindContext<EndianSlice<'a, LittleEndian>>, + expected_ctx: UnwindContext<EndianSlice<'a, LittleEndian>>, + cie: CommonInformationEntry<EndianSlice<'a, LittleEndian>>, + fde: Option<FrameDescriptionEntry<EndianSlice<'a, LittleEndian>>>, + instructions: I, + ) where + I: AsRef< + [( + Result<bool>, + CallFrameInstruction<EndianSlice<'a, LittleEndian>>, + )], + >, + { + { + let section = &DebugFrame::from(EndianSlice::default()); + let bases = &BaseAddresses::default(); + let mut table = match fde { + Some(fde) => UnwindTable::new_for_fde(section, bases, &mut initial_ctx, &fde), + None => UnwindTable::new_for_cie(section, bases, &mut initial_ctx, &cie), + }; + for &(ref expected_result, ref instruction) in instructions.as_ref() { + assert_eq!(*expected_result, table.evaluate(instruction.clone())); + } + } + + assert_eq!(expected_ctx, initial_ctx); + } + + fn make_test_cie<'a>() -> CommonInformationEntry<EndianSlice<'a, LittleEndian>> { + CommonInformationEntry { + offset: 0, + format: Format::Dwarf64, + length: 0, + return_address_register: Register(0), + version: 4, + address_size: mem::size_of::<usize>() as u8, + initial_instructions: EndianSlice::new(&[], LittleEndian), + augmentation: None, + segment_size: 0, + data_alignment_factor: 2, + code_alignment_factor: 3, + } + } + + #[test] + fn test_eval_set_loc() { + let cie = make_test_cie(); + let ctx = UnwindContext::new(); + let mut expected = ctx.clone(); + expected.row_mut().end_address = 42; + let instructions = [(Ok(true), CallFrameInstruction::SetLoc { address: 42 })]; + assert_eval(ctx, expected, cie, None, instructions); + } + + #[test] + fn test_eval_set_loc_backwards() { + let cie = make_test_cie(); + let mut ctx = UnwindContext::new(); + ctx.row_mut().start_address = 999; + let expected = ctx.clone(); + let instructions = [( + Err(Error::InvalidAddressRange), + CallFrameInstruction::SetLoc { address: 42 }, + )]; + assert_eval(ctx, expected, cie, None, instructions); + } + + #[test] + fn test_eval_advance_loc() { + let cie = make_test_cie(); + let mut ctx = UnwindContext::new(); + ctx.row_mut().start_address = 3; + let mut expected = ctx.clone(); + expected.row_mut().end_address = 3 + 2 * cie.code_alignment_factor; + let instructions = [(Ok(true), CallFrameInstruction::AdvanceLoc { delta: 2 })]; + assert_eval(ctx, expected, cie, None, instructions); + } + + #[test] + fn test_eval_advance_loc_overflow() { + let cie = make_test_cie(); + let mut ctx = UnwindContext::new(); + ctx.row_mut().start_address = u64::MAX; + let mut expected = ctx.clone(); + expected.row_mut().end_address = 42 * cie.code_alignment_factor - 1; + let instructions = [(Ok(true), CallFrameInstruction::AdvanceLoc { delta: 42 })]; + assert_eval(ctx, expected, cie, None, instructions); + } + + #[test] + fn test_eval_def_cfa() { + let cie = make_test_cie(); + let ctx = UnwindContext::new(); + let mut expected = ctx.clone(); + expected.set_cfa(CfaRule::RegisterAndOffset { + register: Register(42), + offset: 36, + }); + let instructions = [( + Ok(false), + CallFrameInstruction::DefCfa { + register: Register(42), + offset: 36, + }, + )]; + assert_eval(ctx, expected, cie, None, instructions); + } + + #[test] + fn test_eval_def_cfa_sf() { + let cie = make_test_cie(); + let ctx = UnwindContext::new(); + let mut expected = ctx.clone(); + expected.set_cfa(CfaRule::RegisterAndOffset { + register: Register(42), + offset: 36 * cie.data_alignment_factor as i64, + }); + let instructions = [( + Ok(false), + CallFrameInstruction::DefCfaSf { + register: Register(42), + factored_offset: 36, + }, + )]; + assert_eval(ctx, expected, cie, None, instructions); + } + + #[test] + fn test_eval_def_cfa_register() { + let cie = make_test_cie(); + let mut ctx = UnwindContext::new(); + ctx.set_cfa(CfaRule::RegisterAndOffset { + register: Register(3), + offset: 8, + }); + let mut expected = ctx.clone(); + expected.set_cfa(CfaRule::RegisterAndOffset { + register: Register(42), + offset: 8, + }); + let instructions = [( + Ok(false), + CallFrameInstruction::DefCfaRegister { + register: Register(42), + }, + )]; + assert_eval(ctx, expected, cie, None, instructions); + } + + #[test] + fn test_eval_def_cfa_register_invalid_context() { + let cie = make_test_cie(); + let mut ctx = UnwindContext::new(); + ctx.set_cfa(CfaRule::Expression(Expression(EndianSlice::new( + &[], + LittleEndian, + )))); + let expected = ctx.clone(); + let instructions = [( + Err(Error::CfiInstructionInInvalidContext), + CallFrameInstruction::DefCfaRegister { + register: Register(42), + }, + )]; + assert_eval(ctx, expected, cie, None, instructions); + } + + #[test] + fn test_eval_def_cfa_offset() { + let cie = make_test_cie(); + let mut ctx = UnwindContext::new(); + ctx.set_cfa(CfaRule::RegisterAndOffset { + register: Register(3), + offset: 8, + }); + let mut expected = ctx.clone(); + expected.set_cfa(CfaRule::RegisterAndOffset { + register: Register(3), + offset: 42, + }); + let instructions = [(Ok(false), CallFrameInstruction::DefCfaOffset { offset: 42 })]; + assert_eval(ctx, expected, cie, None, instructions); + } + + #[test] + fn test_eval_def_cfa_offset_invalid_context() { + let cie = make_test_cie(); + let mut ctx = UnwindContext::new(); + ctx.set_cfa(CfaRule::Expression(Expression(EndianSlice::new( + &[], + LittleEndian, + )))); + let expected = ctx.clone(); + let instructions = [( + Err(Error::CfiInstructionInInvalidContext), + CallFrameInstruction::DefCfaOffset { offset: 1993 }, + )]; + assert_eval(ctx, expected, cie, None, instructions); + } + + #[test] + fn test_eval_def_cfa_expression() { + let expr = [1, 2, 3, 4]; + let cie = make_test_cie(); + let ctx = UnwindContext::new(); + let mut expected = ctx.clone(); + expected.set_cfa(CfaRule::Expression(Expression(EndianSlice::new( + &expr, + LittleEndian, + )))); + let instructions = [( + Ok(false), + CallFrameInstruction::DefCfaExpression { + expression: Expression(EndianSlice::new(&expr, LittleEndian)), + }, + )]; + assert_eval(ctx, expected, cie, None, instructions); + } + + #[test] + fn test_eval_undefined() { + let cie = make_test_cie(); + let ctx = UnwindContext::new(); + let mut expected = ctx.clone(); + expected + .set_register_rule(Register(5), RegisterRule::Undefined) + .unwrap(); + let instructions = [( + Ok(false), + CallFrameInstruction::Undefined { + register: Register(5), + }, + )]; + assert_eval(ctx, expected, cie, None, instructions); + } + + #[test] + fn test_eval_same_value() { + let cie = make_test_cie(); + let ctx = UnwindContext::new(); + let mut expected = ctx.clone(); + expected + .set_register_rule(Register(0), RegisterRule::SameValue) + .unwrap(); + let instructions = [( + Ok(false), + CallFrameInstruction::SameValue { + register: Register(0), + }, + )]; + assert_eval(ctx, expected, cie, None, instructions); + } + + #[test] + fn test_eval_offset() { + let cie = make_test_cie(); + let ctx = UnwindContext::new(); + let mut expected = ctx.clone(); + expected + .set_register_rule( + Register(2), + RegisterRule::Offset(3 * cie.data_alignment_factor), + ) + .unwrap(); + let instructions = [( + Ok(false), + CallFrameInstruction::Offset { + register: Register(2), + factored_offset: 3, + }, + )]; + assert_eval(ctx, expected, cie, None, instructions); + } + + #[test] + fn test_eval_offset_extended_sf() { + let cie = make_test_cie(); + let ctx = UnwindContext::new(); + let mut expected = ctx.clone(); + expected + .set_register_rule( + Register(4), + RegisterRule::Offset(-3 * cie.data_alignment_factor), + ) + .unwrap(); + let instructions = [( + Ok(false), + CallFrameInstruction::OffsetExtendedSf { + register: Register(4), + factored_offset: -3, + }, + )]; + assert_eval(ctx, expected, cie, None, instructions); + } + + #[test] + fn test_eval_val_offset() { + let cie = make_test_cie(); + let ctx = UnwindContext::new(); + let mut expected = ctx.clone(); + expected + .set_register_rule( + Register(5), + RegisterRule::ValOffset(7 * cie.data_alignment_factor), + ) + .unwrap(); + let instructions = [( + Ok(false), + CallFrameInstruction::ValOffset { + register: Register(5), + factored_offset: 7, + }, + )]; + assert_eval(ctx, expected, cie, None, instructions); + } + + #[test] + fn test_eval_val_offset_sf() { + let cie = make_test_cie(); + let ctx = UnwindContext::new(); + let mut expected = ctx.clone(); + expected + .set_register_rule( + Register(5), + RegisterRule::ValOffset(-7 * cie.data_alignment_factor), + ) + .unwrap(); + let instructions = [( + Ok(false), + CallFrameInstruction::ValOffsetSf { + register: Register(5), + factored_offset: -7, + }, + )]; + assert_eval(ctx, expected, cie, None, instructions); + } + + #[test] + fn test_eval_expression() { + let expr = [1, 2, 3, 4]; + let cie = make_test_cie(); + let ctx = UnwindContext::new(); + let mut expected = ctx.clone(); + expected + .set_register_rule( + Register(9), + RegisterRule::Expression(Expression(EndianSlice::new(&expr, LittleEndian))), + ) + .unwrap(); + let instructions = [( + Ok(false), + CallFrameInstruction::Expression { + register: Register(9), + expression: Expression(EndianSlice::new(&expr, LittleEndian)), + }, + )]; + assert_eval(ctx, expected, cie, None, instructions); + } + + #[test] + fn test_eval_val_expression() { + let expr = [1, 2, 3, 4]; + let cie = make_test_cie(); + let ctx = UnwindContext::new(); + let mut expected = ctx.clone(); + expected + .set_register_rule( + Register(9), + RegisterRule::ValExpression(Expression(EndianSlice::new(&expr, LittleEndian))), + ) + .unwrap(); + let instructions = [( + Ok(false), + CallFrameInstruction::ValExpression { + register: Register(9), + expression: Expression(EndianSlice::new(&expr, LittleEndian)), + }, + )]; + assert_eval(ctx, expected, cie, None, instructions); + } + + #[test] + fn test_eval_restore() { + let cie = make_test_cie(); + let fde = FrameDescriptionEntry { + offset: 0, + format: Format::Dwarf64, + length: 0, + address_range: 0, + augmentation: None, + initial_address: 0, + initial_segment: 0, + cie: cie.clone(), + instructions: EndianSlice::new(&[], LittleEndian), + }; + + let mut ctx = UnwindContext::new(); + ctx.set_register_rule(Register(0), RegisterRule::Offset(1)) + .unwrap(); + ctx.save_initial_rules().unwrap(); + let expected = ctx.clone(); + ctx.set_register_rule(Register(0), RegisterRule::Offset(2)) + .unwrap(); + + let instructions = [( + Ok(false), + CallFrameInstruction::Restore { + register: Register(0), + }, + )]; + assert_eval(ctx, expected, cie, Some(fde), instructions); + } + + #[test] + fn test_eval_restore_havent_saved_initial_context() { + let cie = make_test_cie(); + let ctx = UnwindContext::new(); + let expected = ctx.clone(); + let instructions = [( + Err(Error::CfiInstructionInInvalidContext), + CallFrameInstruction::Restore { + register: Register(0), + }, + )]; + assert_eval(ctx, expected, cie, None, instructions); + } + + #[test] + fn test_eval_remember_state() { + let cie = make_test_cie(); + let ctx = UnwindContext::new(); + let mut expected = ctx.clone(); + expected.push_row().unwrap(); + let instructions = [(Ok(false), CallFrameInstruction::RememberState)]; + assert_eval(ctx, expected, cie, None, instructions); + } + + #[test] + fn test_eval_restore_state() { + let cie = make_test_cie(); + + let mut ctx = UnwindContext::new(); + ctx.set_start_address(1); + ctx.set_register_rule(Register(0), RegisterRule::SameValue) + .unwrap(); + let mut expected = ctx.clone(); + ctx.push_row().unwrap(); + ctx.set_start_address(2); + ctx.set_register_rule(Register(0), RegisterRule::Offset(16)) + .unwrap(); + + // Restore state should preserve current location. + expected.set_start_address(2); + + let instructions = [ + // First one pops just fine. + (Ok(false), CallFrameInstruction::RestoreState), + // Second pop would try to pop out of bounds. + ( + Err(Error::PopWithEmptyStack), + CallFrameInstruction::RestoreState, + ), + ]; + + assert_eval(ctx, expected, cie, None, instructions); + } + + #[test] + fn test_eval_negate_ra_state() { + let cie = make_test_cie(); + let ctx = UnwindContext::new(); + let mut expected = ctx.clone(); + expected + .set_register_rule(crate::AArch64::RA_SIGN_STATE, RegisterRule::Constant(1)) + .unwrap(); + let instructions = [(Ok(false), CallFrameInstruction::NegateRaState)]; + assert_eval(ctx, expected, cie, None, instructions); + + let cie = make_test_cie(); + let ctx = UnwindContext::new(); + let mut expected = ctx.clone(); + expected + .set_register_rule(crate::AArch64::RA_SIGN_STATE, RegisterRule::Constant(0)) + .unwrap(); + let instructions = [ + (Ok(false), CallFrameInstruction::NegateRaState), + (Ok(false), CallFrameInstruction::NegateRaState), + ]; + assert_eval(ctx, expected, cie, None, instructions); + + // NegateRaState can't be used with other instructions. + let cie = make_test_cie(); + let ctx = UnwindContext::new(); + let mut expected = ctx.clone(); + expected + .set_register_rule( + crate::AArch64::RA_SIGN_STATE, + RegisterRule::Offset(cie.data_alignment_factor as i64), + ) + .unwrap(); + let instructions = [ + ( + Ok(false), + CallFrameInstruction::Offset { + register: crate::AArch64::RA_SIGN_STATE, + factored_offset: 1, + }, + ), + ( + Err(Error::CfiInstructionInInvalidContext), + CallFrameInstruction::NegateRaState, + ), + ]; + assert_eval(ctx, expected, cie, None, instructions); + } + + #[test] + fn test_eval_nop() { + let cie = make_test_cie(); + let ctx = UnwindContext::new(); + let expected = ctx.clone(); + let instructions = [(Ok(false), CallFrameInstruction::Nop)]; + assert_eval(ctx, expected, cie, None, instructions); + } + + #[test] + fn test_unwind_table_cie_no_rule() { + let initial_instructions = Section::with_endian(Endian::Little) + // The CFA is -12 from register 4. + .D8(constants::DW_CFA_def_cfa_sf.0) + .uleb(4) + .sleb(-12) + .append_repeated(constants::DW_CFA_nop.0, 4); + let initial_instructions = initial_instructions.get_contents().unwrap(); + + let cie = CommonInformationEntry { + offset: 0, + length: 0, + format: Format::Dwarf32, + version: 4, + augmentation: None, + address_size: 8, + segment_size: 0, + code_alignment_factor: 1, + data_alignment_factor: 1, + return_address_register: Register(3), + initial_instructions: EndianSlice::new(&initial_instructions, LittleEndian), + }; + + let instructions = Section::with_endian(Endian::Little) + // A bunch of nop padding. + .append_repeated(constants::DW_CFA_nop.0, 8); + let instructions = instructions.get_contents().unwrap(); + + let fde = FrameDescriptionEntry { + offset: 0, + length: 0, + format: Format::Dwarf32, + cie: cie.clone(), + initial_segment: 0, + initial_address: 0, + address_range: 100, + augmentation: None, + instructions: EndianSlice::new(&instructions, LittleEndian), + }; + + let section = &DebugFrame::from(EndianSlice::default()); + let bases = &BaseAddresses::default(); + let mut ctx = Box::new(UnwindContext::new()); + + let mut table = fde + .rows(section, bases, &mut ctx) + .expect("Should run initial program OK"); + assert!(table.ctx.is_initialized); + let expected_initial_rule = (Register(0), RegisterRule::Undefined); + assert_eq!(table.ctx.initial_rule, Some(expected_initial_rule)); + + { + let row = table.next_row().expect("Should evaluate first row OK"); + let expected = UnwindTableRow { + start_address: 0, + end_address: 100, + saved_args_size: 0, + cfa: CfaRule::RegisterAndOffset { + register: Register(4), + offset: -12, + }, + registers: [].iter().collect(), + }; + assert_eq!(Some(&expected), row); + } + + // All done! + assert_eq!(Ok(None), table.next_row()); + assert_eq!(Ok(None), table.next_row()); + } + + #[test] + fn test_unwind_table_cie_single_rule() { + let initial_instructions = Section::with_endian(Endian::Little) + // The CFA is -12 from register 4. + .D8(constants::DW_CFA_def_cfa_sf.0) + .uleb(4) + .sleb(-12) + // Register 3 is 4 from the CFA. + .D8(constants::DW_CFA_offset.0 | 3) + .uleb(4) + .append_repeated(constants::DW_CFA_nop.0, 4); + let initial_instructions = initial_instructions.get_contents().unwrap(); + + let cie = CommonInformationEntry { + offset: 0, + length: 0, + format: Format::Dwarf32, + version: 4, + augmentation: None, + address_size: 8, + segment_size: 0, + code_alignment_factor: 1, + data_alignment_factor: 1, + return_address_register: Register(3), + initial_instructions: EndianSlice::new(&initial_instructions, LittleEndian), + }; + + let instructions = Section::with_endian(Endian::Little) + // A bunch of nop padding. + .append_repeated(constants::DW_CFA_nop.0, 8); + let instructions = instructions.get_contents().unwrap(); + + let fde = FrameDescriptionEntry { + offset: 0, + length: 0, + format: Format::Dwarf32, + cie: cie.clone(), + initial_segment: 0, + initial_address: 0, + address_range: 100, + augmentation: None, + instructions: EndianSlice::new(&instructions, LittleEndian), + }; + + let section = &DebugFrame::from(EndianSlice::default()); + let bases = &BaseAddresses::default(); + let mut ctx = Box::new(UnwindContext::new()); + + let mut table = fde + .rows(section, bases, &mut ctx) + .expect("Should run initial program OK"); + assert!(table.ctx.is_initialized); + let expected_initial_rule = (Register(3), RegisterRule::Offset(4)); + assert_eq!(table.ctx.initial_rule, Some(expected_initial_rule)); + + { + let row = table.next_row().expect("Should evaluate first row OK"); + let expected = UnwindTableRow { + start_address: 0, + end_address: 100, + saved_args_size: 0, + cfa: CfaRule::RegisterAndOffset { + register: Register(4), + offset: -12, + }, + registers: [(Register(3), RegisterRule::Offset(4))].iter().collect(), + }; + assert_eq!(Some(&expected), row); + } + + // All done! + assert_eq!(Ok(None), table.next_row()); + assert_eq!(Ok(None), table.next_row()); + } + + #[test] + fn test_unwind_table_cie_invalid_rule() { + let initial_instructions1 = Section::with_endian(Endian::Little) + // Test that stack length is reset. + .D8(constants::DW_CFA_remember_state.0) + // Test that stack value is reset (different register from that used later). + .D8(constants::DW_CFA_offset.0 | 4) + .uleb(8) + // Invalid due to missing operands. + .D8(constants::DW_CFA_offset.0); + let initial_instructions1 = initial_instructions1.get_contents().unwrap(); + + let cie1 = CommonInformationEntry { + offset: 0, + length: 0, + format: Format::Dwarf32, + version: 4, + augmentation: None, + address_size: 8, + segment_size: 0, + code_alignment_factor: 1, + data_alignment_factor: 1, + return_address_register: Register(3), + initial_instructions: EndianSlice::new(&initial_instructions1, LittleEndian), + }; + + let initial_instructions2 = Section::with_endian(Endian::Little) + // Register 3 is 4 from the CFA. + .D8(constants::DW_CFA_offset.0 | 3) + .uleb(4) + .append_repeated(constants::DW_CFA_nop.0, 4); + let initial_instructions2 = initial_instructions2.get_contents().unwrap(); + + let cie2 = CommonInformationEntry { + offset: 0, + length: 0, + format: Format::Dwarf32, + version: 4, + augmentation: None, + address_size: 8, + segment_size: 0, + code_alignment_factor: 1, + data_alignment_factor: 1, + return_address_register: Register(3), + initial_instructions: EndianSlice::new(&initial_instructions2, LittleEndian), + }; + + let fde1 = FrameDescriptionEntry { + offset: 0, + length: 0, + format: Format::Dwarf32, + cie: cie1.clone(), + initial_segment: 0, + initial_address: 0, + address_range: 100, + augmentation: None, + instructions: EndianSlice::new(&[], LittleEndian), + }; + + let fde2 = FrameDescriptionEntry { + offset: 0, + length: 0, + format: Format::Dwarf32, + cie: cie2.clone(), + initial_segment: 0, + initial_address: 0, + address_range: 100, + augmentation: None, + instructions: EndianSlice::new(&[], LittleEndian), + }; + + let section = &DebugFrame::from(EndianSlice::default()); + let bases = &BaseAddresses::default(); + let mut ctx = Box::new(UnwindContext::new()); + + let table = fde1 + .rows(section, bases, &mut ctx) + .map_eof(&initial_instructions1); + assert_eq!(table.err(), Some(Error::UnexpectedEof(ReaderOffsetId(4)))); + assert!(!ctx.is_initialized); + assert_eq!(ctx.stack.len(), 2); + assert_eq!(ctx.initial_rule, None); + + let _table = fde2 + .rows(section, bases, &mut ctx) + .expect("Should run initial program OK"); + assert!(ctx.is_initialized); + assert_eq!(ctx.stack.len(), 1); + let expected_initial_rule = (Register(3), RegisterRule::Offset(4)); + assert_eq!(ctx.initial_rule, Some(expected_initial_rule)); + } + + #[test] + fn test_unwind_table_next_row() { + let initial_instructions = Section::with_endian(Endian::Little) + // The CFA is -12 from register 4. + .D8(constants::DW_CFA_def_cfa_sf.0) + .uleb(4) + .sleb(-12) + // Register 0 is 8 from the CFA. + .D8(constants::DW_CFA_offset.0 | 0) + .uleb(8) + // Register 3 is 4 from the CFA. + .D8(constants::DW_CFA_offset.0 | 3) + .uleb(4) + .append_repeated(constants::DW_CFA_nop.0, 4); + let initial_instructions = initial_instructions.get_contents().unwrap(); + + let cie = CommonInformationEntry { + offset: 0, + length: 0, + format: Format::Dwarf32, + version: 4, + augmentation: None, + address_size: 8, + segment_size: 0, + code_alignment_factor: 1, + data_alignment_factor: 1, + return_address_register: Register(3), + initial_instructions: EndianSlice::new(&initial_instructions, LittleEndian), + }; + + let instructions = Section::with_endian(Endian::Little) + // Initial instructions form a row, advance the address by 1. + .D8(constants::DW_CFA_advance_loc1.0) + .D8(1) + // Register 0 is -16 from the CFA. + .D8(constants::DW_CFA_offset_extended_sf.0) + .uleb(0) + .sleb(-16) + // Finish this row, advance the address by 32. + .D8(constants::DW_CFA_advance_loc1.0) + .D8(32) + // Register 3 is -4 from the CFA. + .D8(constants::DW_CFA_offset_extended_sf.0) + .uleb(3) + .sleb(-4) + // Finish this row, advance the address by 64. + .D8(constants::DW_CFA_advance_loc1.0) + .D8(64) + // Register 5 is 4 from the CFA. + .D8(constants::DW_CFA_offset.0 | 5) + .uleb(4) + // A bunch of nop padding. + .append_repeated(constants::DW_CFA_nop.0, 8); + let instructions = instructions.get_contents().unwrap(); + + let fde = FrameDescriptionEntry { + offset: 0, + length: 0, + format: Format::Dwarf32, + cie: cie.clone(), + initial_segment: 0, + initial_address: 0, + address_range: 100, + augmentation: None, + instructions: EndianSlice::new(&instructions, LittleEndian), + }; + + let section = &DebugFrame::from(EndianSlice::default()); + let bases = &BaseAddresses::default(); + let mut ctx = Box::new(UnwindContext::new()); + + let mut table = fde + .rows(section, bases, &mut ctx) + .expect("Should run initial program OK"); + assert!(table.ctx.is_initialized); + assert!(table.ctx.initial_rule.is_none()); + let expected_initial_rules: RegisterRuleMap<_> = [ + (Register(0), RegisterRule::Offset(8)), + (Register(3), RegisterRule::Offset(4)), + ] + .iter() + .collect(); + assert_eq!(table.ctx.stack[0].registers, expected_initial_rules); + + { + let row = table.next_row().expect("Should evaluate first row OK"); + let expected = UnwindTableRow { + start_address: 0, + end_address: 1, + saved_args_size: 0, + cfa: CfaRule::RegisterAndOffset { + register: Register(4), + offset: -12, + }, + registers: [ + (Register(0), RegisterRule::Offset(8)), + (Register(3), RegisterRule::Offset(4)), + ] + .iter() + .collect(), + }; + assert_eq!(Some(&expected), row); + } + + { + let row = table.next_row().expect("Should evaluate second row OK"); + let expected = UnwindTableRow { + start_address: 1, + end_address: 33, + saved_args_size: 0, + cfa: CfaRule::RegisterAndOffset { + register: Register(4), + offset: -12, + }, + registers: [ + (Register(0), RegisterRule::Offset(-16)), + (Register(3), RegisterRule::Offset(4)), + ] + .iter() + .collect(), + }; + assert_eq!(Some(&expected), row); + } + + { + let row = table.next_row().expect("Should evaluate third row OK"); + let expected = UnwindTableRow { + start_address: 33, + end_address: 97, + saved_args_size: 0, + cfa: CfaRule::RegisterAndOffset { + register: Register(4), + offset: -12, + }, + registers: [ + (Register(0), RegisterRule::Offset(-16)), + (Register(3), RegisterRule::Offset(-4)), + ] + .iter() + .collect(), + }; + assert_eq!(Some(&expected), row); + } + + { + let row = table.next_row().expect("Should evaluate fourth row OK"); + let expected = UnwindTableRow { + start_address: 97, + end_address: 100, + saved_args_size: 0, + cfa: CfaRule::RegisterAndOffset { + register: Register(4), + offset: -12, + }, + registers: [ + (Register(0), RegisterRule::Offset(-16)), + (Register(3), RegisterRule::Offset(-4)), + (Register(5), RegisterRule::Offset(4)), + ] + .iter() + .collect(), + }; + assert_eq!(Some(&expected), row); + } + + // All done! + assert_eq!(Ok(None), table.next_row()); + assert_eq!(Ok(None), table.next_row()); + } + + #[test] + fn test_unwind_info_for_address_ok() { + let instrs1 = Section::with_endian(Endian::Big) + // The CFA is -12 from register 4. + .D8(constants::DW_CFA_def_cfa_sf.0) + .uleb(4) + .sleb(-12); + let instrs1 = instrs1.get_contents().unwrap(); + + let instrs2: Vec<_> = (0..8).map(|_| constants::DW_CFA_nop.0).collect(); + + let instrs3 = Section::with_endian(Endian::Big) + // Initial instructions form a row, advance the address by 100. + .D8(constants::DW_CFA_advance_loc1.0) + .D8(100) + // Register 0 is -16 from the CFA. + .D8(constants::DW_CFA_offset_extended_sf.0) + .uleb(0) + .sleb(-16); + let instrs3 = instrs3.get_contents().unwrap(); + + let instrs4: Vec<_> = (0..16).map(|_| constants::DW_CFA_nop.0).collect(); + + let mut cie1 = CommonInformationEntry { + offset: 0, + length: 0, + format: Format::Dwarf32, + version: 4, + augmentation: None, + address_size: 8, + segment_size: 0, + code_alignment_factor: 1, + data_alignment_factor: 1, + return_address_register: Register(3), + initial_instructions: EndianSlice::new(&instrs1, BigEndian), + }; + + let mut cie2 = CommonInformationEntry { + offset: 0, + length: 0, + format: Format::Dwarf32, + version: 4, + augmentation: None, + address_size: 4, + segment_size: 0, + code_alignment_factor: 1, + data_alignment_factor: 1, + return_address_register: Register(1), + initial_instructions: EndianSlice::new(&instrs2, BigEndian), + }; + + let cie1_location = Label::new(); + let cie2_location = Label::new(); + + // Write the CIEs first so that their length gets set before we clone + // them into the FDEs and our equality assertions down the line end up + // with all the CIEs always having he correct length. + let kind = debug_frame_be(); + let section = Section::with_endian(kind.endian()) + .mark(&cie1_location) + .cie(kind, None, &mut cie1) + .mark(&cie2_location) + .cie(kind, None, &mut cie2); + + let mut fde1 = FrameDescriptionEntry { + offset: 0, + length: 0, + format: Format::Dwarf32, + cie: cie1.clone(), + initial_segment: 0, + initial_address: 0xfeed_beef, + address_range: 200, + augmentation: None, + instructions: EndianSlice::new(&instrs3, BigEndian), + }; + + let mut fde2 = FrameDescriptionEntry { + offset: 0, + length: 0, + format: Format::Dwarf32, + cie: cie2.clone(), + initial_segment: 0, + initial_address: 0xfeed_face, + address_range: 9000, + augmentation: None, + instructions: EndianSlice::new(&instrs4, BigEndian), + }; + + let section = + section + .fde(kind, &cie1_location, &mut fde1) + .fde(kind, &cie2_location, &mut fde2); + section.start().set_const(0); + + let contents = section.get_contents().unwrap(); + let debug_frame = kind.section(&contents); + + // Get the second row of the unwind table in `instrs3`. + let bases = Default::default(); + let mut ctx = Box::new(UnwindContext::new()); + let result = debug_frame.unwind_info_for_address( + &bases, + &mut ctx, + 0xfeed_beef + 150, + DebugFrame::cie_from_offset, + ); + assert!(result.is_ok()); + let unwind_info = result.unwrap(); + + assert_eq!( + *unwind_info, + UnwindTableRow { + start_address: fde1.initial_address() + 100, + end_address: fde1.initial_address() + fde1.len(), + saved_args_size: 0, + cfa: CfaRule::RegisterAndOffset { + register: Register(4), + offset: -12, + }, + registers: [(Register(0), RegisterRule::Offset(-16))].iter().collect(), + } + ); + } + + #[test] + fn test_unwind_info_for_address_not_found() { + let debug_frame = DebugFrame::new(&[], NativeEndian); + let bases = Default::default(); + let mut ctx = Box::new(UnwindContext::new()); + let result = debug_frame.unwind_info_for_address( + &bases, + &mut ctx, + 0xbadb_ad99, + DebugFrame::cie_from_offset, + ); + assert!(result.is_err()); + assert_eq!(result.unwrap_err(), Error::NoUnwindInfoForAddress); + } + + #[test] + fn test_eh_frame_hdr_unknown_version() { + let bases = BaseAddresses::default(); + let buf = &[42]; + let result = EhFrameHdr::new(buf, NativeEndian).parse(&bases, 8); + assert!(result.is_err()); + assert_eq!(result.unwrap_err(), Error::UnknownVersion(42)); + } + + #[test] + fn test_eh_frame_hdr_omit_ehptr() { + let section = Section::with_endian(Endian::Little) + .L8(1) + .L8(0xff) + .L8(0x03) + .L8(0x0b) + .L32(2) + .L32(10) + .L32(1) + .L32(20) + .L32(2) + .L32(0); + let section = section.get_contents().unwrap(); + let bases = BaseAddresses::default(); + let result = EhFrameHdr::new(§ion, LittleEndian).parse(&bases, 8); + assert!(result.is_err()); + assert_eq!(result.unwrap_err(), Error::CannotParseOmitPointerEncoding); + } + + #[test] + fn test_eh_frame_hdr_omit_count() { + let section = Section::with_endian(Endian::Little) + .L8(1) + .L8(0x0b) + .L8(0xff) + .L8(0x0b) + .L32(0x12345); + let section = section.get_contents().unwrap(); + let bases = BaseAddresses::default(); + let result = EhFrameHdr::new(§ion, LittleEndian).parse(&bases, 8); + assert!(result.is_ok()); + let result = result.unwrap(); + assert_eq!(result.eh_frame_ptr(), Pointer::Direct(0x12345)); + assert!(result.table().is_none()); + } + + #[test] + fn test_eh_frame_hdr_omit_table() { + let section = Section::with_endian(Endian::Little) + .L8(1) + .L8(0x0b) + .L8(0x03) + .L8(0xff) + .L32(0x12345) + .L32(2); + let section = section.get_contents().unwrap(); + let bases = BaseAddresses::default(); + let result = EhFrameHdr::new(§ion, LittleEndian).parse(&bases, 8); + assert!(result.is_ok()); + let result = result.unwrap(); + assert_eq!(result.eh_frame_ptr(), Pointer::Direct(0x12345)); + assert!(result.table().is_none()); + } + + #[test] + fn test_eh_frame_hdr_varlen_table() { + let section = Section::with_endian(Endian::Little) + .L8(1) + .L8(0x0b) + .L8(0x03) + .L8(0x01) + .L32(0x12345) + .L32(2); + let section = section.get_contents().unwrap(); + let bases = BaseAddresses::default(); + let result = EhFrameHdr::new(§ion, LittleEndian).parse(&bases, 8); + assert!(result.is_ok()); + let result = result.unwrap(); + assert_eq!(result.eh_frame_ptr(), Pointer::Direct(0x12345)); + let table = result.table(); + assert!(table.is_some()); + let table = table.unwrap(); + assert_eq!( + table.lookup(0, &bases), + Err(Error::VariableLengthSearchTable) + ); + } + + #[test] + fn test_eh_frame_hdr_indirect_length() { + let section = Section::with_endian(Endian::Little) + .L8(1) + .L8(0x0b) + .L8(0x83) + .L8(0x0b) + .L32(0x12345) + .L32(2); + let section = section.get_contents().unwrap(); + let bases = BaseAddresses::default(); + let result = EhFrameHdr::new(§ion, LittleEndian).parse(&bases, 8); + assert!(result.is_err()); + assert_eq!(result.unwrap_err(), Error::UnsupportedPointerEncoding); + } + + #[test] + fn test_eh_frame_hdr_indirect_ptrs() { + let section = Section::with_endian(Endian::Little) + .L8(1) + .L8(0x8b) + .L8(0x03) + .L8(0x8b) + .L32(0x12345) + .L32(2) + .L32(10) + .L32(1) + .L32(20) + .L32(2); + let section = section.get_contents().unwrap(); + let bases = BaseAddresses::default(); + let result = EhFrameHdr::new(§ion, LittleEndian).parse(&bases, 8); + assert!(result.is_ok()); + let result = result.unwrap(); + assert_eq!(result.eh_frame_ptr(), Pointer::Indirect(0x12345)); + let table = result.table(); + assert!(table.is_some()); + let table = table.unwrap(); + assert_eq!( + table.lookup(0, &bases), + Err(Error::UnsupportedPointerEncoding) + ); + } + + #[test] + fn test_eh_frame_hdr_good() { + let section = Section::with_endian(Endian::Little) + .L8(1) + .L8(0x0b) + .L8(0x03) + .L8(0x0b) + .L32(0x12345) + .L32(2) + .L32(10) + .L32(1) + .L32(20) + .L32(2); + let section = section.get_contents().unwrap(); + let bases = BaseAddresses::default(); + let result = EhFrameHdr::new(§ion, LittleEndian).parse(&bases, 8); + assert!(result.is_ok()); + let result = result.unwrap(); + assert_eq!(result.eh_frame_ptr(), Pointer::Direct(0x12345)); + let table = result.table(); + assert!(table.is_some()); + let table = table.unwrap(); + assert_eq!(table.lookup(0, &bases), Ok(Pointer::Direct(1))); + assert_eq!(table.lookup(9, &bases), Ok(Pointer::Direct(1))); + assert_eq!(table.lookup(10, &bases), Ok(Pointer::Direct(1))); + assert_eq!(table.lookup(11, &bases), Ok(Pointer::Direct(1))); + assert_eq!(table.lookup(19, &bases), Ok(Pointer::Direct(1))); + assert_eq!(table.lookup(20, &bases), Ok(Pointer::Direct(2))); + assert_eq!(table.lookup(21, &bases), Ok(Pointer::Direct(2))); + assert_eq!(table.lookup(100_000, &bases), Ok(Pointer::Direct(2))); + } + + #[test] + fn test_eh_frame_fde_for_address_good() { + // First, setup eh_frame + // Write the CIE first so that its length gets set before we clone it + // into the FDE. + let mut cie = make_test_cie(); + cie.format = Format::Dwarf32; + cie.version = 1; + + let start_of_cie = Label::new(); + let end_of_cie = Label::new(); + + let kind = eh_frame_le(); + let section = Section::with_endian(kind.endian()) + .append_repeated(0, 16) + .mark(&start_of_cie) + .cie(kind, None, &mut cie) + .mark(&end_of_cie); + + let mut fde1 = FrameDescriptionEntry { + offset: 0, + length: 0, + format: Format::Dwarf32, + cie: cie.clone(), + initial_segment: 0, + initial_address: 9, + address_range: 4, + augmentation: None, + instructions: EndianSlice::new(&[], LittleEndian), + }; + let mut fde2 = FrameDescriptionEntry { + offset: 0, + length: 0, + format: Format::Dwarf32, + cie: cie.clone(), + initial_segment: 0, + initial_address: 20, + address_range: 8, + augmentation: None, + instructions: EndianSlice::new(&[], LittleEndian), + }; + + let start_of_fde1 = Label::new(); + let start_of_fde2 = Label::new(); + + let section = section + // +4 for the FDE length before the CIE offset. + .mark(&start_of_fde1) + .fde(kind, (&start_of_fde1 - &start_of_cie + 4) as u64, &mut fde1) + .mark(&start_of_fde2) + .fde(kind, (&start_of_fde2 - &start_of_cie + 4) as u64, &mut fde2); + + section.start().set_const(0); + let section = section.get_contents().unwrap(); + let eh_frame = kind.section(§ion); + + // Setup eh_frame_hdr + let section = Section::with_endian(kind.endian()) + .L8(1) + .L8(0x0b) + .L8(0x03) + .L8(0x0b) + .L32(0x12345) + .L32(2) + .L32(10) + .L32(0x12345 + start_of_fde1.value().unwrap() as u32) + .L32(20) + .L32(0x12345 + start_of_fde2.value().unwrap() as u32); + + let section = section.get_contents().unwrap(); + let bases = BaseAddresses::default(); + let eh_frame_hdr = EhFrameHdr::new(§ion, LittleEndian).parse(&bases, 8); + assert!(eh_frame_hdr.is_ok()); + let eh_frame_hdr = eh_frame_hdr.unwrap(); + + let table = eh_frame_hdr.table(); + assert!(table.is_some()); + let table = table.unwrap(); + + let bases = Default::default(); + let mut iter = table.iter(&bases); + assert_eq!( + iter.next(), + Ok(Some(( + Pointer::Direct(10), + Pointer::Direct(0x12345 + start_of_fde1.value().unwrap() as u64) + ))) + ); + assert_eq!( + iter.next(), + Ok(Some(( + Pointer::Direct(20), + Pointer::Direct(0x12345 + start_of_fde2.value().unwrap() as u64) + ))) + ); + assert_eq!(iter.next(), Ok(None)); + + assert_eq!( + table.iter(&bases).nth(0), + Ok(Some(( + Pointer::Direct(10), + Pointer::Direct(0x12345 + start_of_fde1.value().unwrap() as u64) + ))) + ); + + assert_eq!( + table.iter(&bases).nth(1), + Ok(Some(( + Pointer::Direct(20), + Pointer::Direct(0x12345 + start_of_fde2.value().unwrap() as u64) + ))) + ); + assert_eq!(table.iter(&bases).nth(2), Ok(None)); + + let f = |_: &_, _: &_, o: EhFrameOffset| { + assert_eq!(o, EhFrameOffset(start_of_cie.value().unwrap() as usize)); + Ok(cie.clone()) + }; + assert_eq!( + table.fde_for_address(&eh_frame, &bases, 9, f), + Ok(fde1.clone()) + ); + assert_eq!( + table.fde_for_address(&eh_frame, &bases, 10, f), + Ok(fde1.clone()) + ); + assert_eq!(table.fde_for_address(&eh_frame, &bases, 11, f), Ok(fde1)); + assert_eq!( + table.fde_for_address(&eh_frame, &bases, 19, f), + Err(Error::NoUnwindInfoForAddress) + ); + assert_eq!( + table.fde_for_address(&eh_frame, &bases, 20, f), + Ok(fde2.clone()) + ); + assert_eq!(table.fde_for_address(&eh_frame, &bases, 21, f), Ok(fde2)); + assert_eq!( + table.fde_for_address(&eh_frame, &bases, 100_000, f), + Err(Error::NoUnwindInfoForAddress) + ); + } + + #[test] + fn test_eh_frame_stops_at_zero_length() { + let section = Section::with_endian(Endian::Little).L32(0); + let section = section.get_contents().unwrap(); + let rest = &mut EndianSlice::new(§ion, LittleEndian); + let bases = Default::default(); + + assert_eq!( + parse_cfi_entry(&bases, &EhFrame::new(&*section, LittleEndian), rest), + Ok(None) + ); + + assert_eq!( + EhFrame::new(§ion, LittleEndian).cie_from_offset(&bases, EhFrameOffset(0)), + Err(Error::NoEntryAtGivenOffset) + ); + } + + fn resolve_cie_offset(buf: &[u8], cie_offset: usize) -> Result<usize> { + let mut fde = FrameDescriptionEntry { + offset: 0, + length: 0, + format: Format::Dwarf64, + cie: make_test_cie(), + initial_segment: 0, + initial_address: 0xfeed_beef, + address_range: 39, + augmentation: None, + instructions: EndianSlice::new(&[], LittleEndian), + }; + + let kind = eh_frame_le(); + let section = Section::with_endian(kind.endian()) + .append_bytes(&buf) + .fde(kind, cie_offset as u64, &mut fde) + .append_bytes(&buf); + + let section = section.get_contents().unwrap(); + let eh_frame = kind.section(§ion); + let input = &mut EndianSlice::new(§ion[buf.len()..], LittleEndian); + + let bases = Default::default(); + match parse_cfi_entry(&bases, &eh_frame, input) { + Ok(Some(CieOrFde::Fde(partial))) => Ok(partial.cie_offset.0), + Err(e) => Err(e), + otherwise => panic!("Unexpected result: {:#?}", otherwise), + } + } + + #[test] + fn test_eh_frame_resolve_cie_offset_ok() { + let buf = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + let cie_offset = 2; + // + 4 for size of length field + assert_eq!( + resolve_cie_offset(&buf, buf.len() + 4 - cie_offset), + Ok(cie_offset) + ); + } + + #[test] + fn test_eh_frame_resolve_cie_offset_out_of_bounds() { + let buf = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + assert_eq!( + resolve_cie_offset(&buf, buf.len() + 4 + 2), + Err(Error::OffsetOutOfBounds) + ); + } + + #[test] + fn test_eh_frame_resolve_cie_offset_underflow() { + let buf = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + assert_eq!( + resolve_cie_offset(&buf, ::core::usize::MAX), + Err(Error::OffsetOutOfBounds) + ); + } + + #[test] + fn test_eh_frame_fde_ok() { + let mut cie = make_test_cie(); + cie.format = Format::Dwarf32; + cie.version = 1; + + let start_of_cie = Label::new(); + let end_of_cie = Label::new(); + + // Write the CIE first so that its length gets set before we clone it + // into the FDE. + let kind = eh_frame_le(); + let section = Section::with_endian(kind.endian()) + .append_repeated(0, 16) + .mark(&start_of_cie) + .cie(kind, None, &mut cie) + .mark(&end_of_cie); + + let mut fde = FrameDescriptionEntry { + offset: 0, + length: 0, + format: Format::Dwarf32, + cie: cie.clone(), + initial_segment: 0, + initial_address: 0xfeed_beef, + address_range: 999, + augmentation: None, + instructions: EndianSlice::new(&[], LittleEndian), + }; + + let section = section + // +4 for the FDE length before the CIE offset. + .fde(kind, (&end_of_cie - &start_of_cie + 4) as u64, &mut fde); + + section.start().set_const(0); + let section = section.get_contents().unwrap(); + let eh_frame = kind.section(§ion); + let section = EndianSlice::new(§ion, LittleEndian); + + let mut offset = None; + match parse_fde( + eh_frame, + &mut section.range_from(end_of_cie.value().unwrap() as usize..), + |_, _, o| { + offset = Some(o); + assert_eq!(o, EhFrameOffset(start_of_cie.value().unwrap() as usize)); + Ok(cie.clone()) + }, + ) { + Ok(actual) => assert_eq!(actual, fde), + otherwise => panic!("Unexpected result {:?}", otherwise), + } + assert!(offset.is_some()); + } + + #[test] + fn test_eh_frame_fde_out_of_bounds() { + let mut cie = make_test_cie(); + cie.version = 1; + + let end_of_cie = Label::new(); + + let mut fde = FrameDescriptionEntry { + offset: 0, + length: 0, + format: Format::Dwarf64, + cie: cie.clone(), + initial_segment: 0, + initial_address: 0xfeed_beef, + address_range: 999, + augmentation: None, + instructions: EndianSlice::new(&[], LittleEndian), + }; + + let kind = eh_frame_le(); + let section = Section::with_endian(kind.endian()) + .cie(kind, None, &mut cie) + .mark(&end_of_cie) + .fde(kind, 99_999_999_999_999, &mut fde); + + section.start().set_const(0); + let section = section.get_contents().unwrap(); + let eh_frame = kind.section(§ion); + let section = EndianSlice::new(§ion, LittleEndian); + + let result = parse_fde( + eh_frame, + &mut section.range_from(end_of_cie.value().unwrap() as usize..), + UnwindSection::cie_from_offset, + ); + assert_eq!(result, Err(Error::OffsetOutOfBounds)); + } + + #[test] + fn test_augmentation_parse_not_z_augmentation() { + let augmentation = &mut EndianSlice::new(b"wtf", NativeEndian); + let bases = Default::default(); + let address_size = 8; + let section = EhFrame::new(&[], NativeEndian); + let input = &mut EndianSlice::new(&[], NativeEndian); + assert_eq!( + Augmentation::parse(augmentation, &bases, address_size, §ion, input), + Err(Error::UnknownAugmentation) + ); + } + + #[test] + fn test_augmentation_parse_just_signal_trampoline() { + let aug_str = &mut EndianSlice::new(b"S", LittleEndian); + let bases = Default::default(); + let address_size = 8; + let section = EhFrame::new(&[], LittleEndian); + let input = &mut EndianSlice::new(&[], LittleEndian); + + let mut augmentation = Augmentation::default(); + augmentation.is_signal_trampoline = true; + + assert_eq!( + Augmentation::parse(aug_str, &bases, address_size, §ion, input), + Ok(augmentation) + ); + } + + #[test] + fn test_augmentation_parse_unknown_part_of_z_augmentation() { + // The 'Z' character is not defined by the z-style augmentation. + let bases = Default::default(); + let address_size = 8; + let section = Section::with_endian(Endian::Little) + .uleb(4) + .append_repeated(4, 4) + .get_contents() + .unwrap(); + let section = EhFrame::new(§ion, LittleEndian); + let input = &mut section.section().clone(); + let augmentation = &mut EndianSlice::new(b"zZ", LittleEndian); + assert_eq!( + Augmentation::parse(augmentation, &bases, address_size, §ion, input), + Err(Error::UnknownAugmentation) + ); + } + + #[test] + #[allow(non_snake_case)] + fn test_augmentation_parse_L() { + let bases = Default::default(); + let address_size = 8; + let rest = [9, 8, 7, 6, 5, 4, 3, 2, 1]; + + let section = Section::with_endian(Endian::Little) + .uleb(1) + .D8(constants::DW_EH_PE_uleb128.0) + .append_bytes(&rest) + .get_contents() + .unwrap(); + let section = EhFrame::new(§ion, LittleEndian); + let input = &mut section.section().clone(); + let aug_str = &mut EndianSlice::new(b"zL", LittleEndian); + + let mut augmentation = Augmentation::default(); + augmentation.lsda = Some(constants::DW_EH_PE_uleb128); + + assert_eq!( + Augmentation::parse(aug_str, &bases, address_size, §ion, input), + Ok(augmentation) + ); + assert_eq!(*input, EndianSlice::new(&rest, LittleEndian)); + } + + #[test] + #[allow(non_snake_case)] + fn test_augmentation_parse_P() { + let bases = Default::default(); + let address_size = 8; + let rest = [9, 8, 7, 6, 5, 4, 3, 2, 1]; + + let section = Section::with_endian(Endian::Little) + .uleb(9) + .D8(constants::DW_EH_PE_udata8.0) + .L64(0xf00d_f00d) + .append_bytes(&rest) + .get_contents() + .unwrap(); + let section = EhFrame::new(§ion, LittleEndian); + let input = &mut section.section().clone(); + let aug_str = &mut EndianSlice::new(b"zP", LittleEndian); + + let mut augmentation = Augmentation::default(); + augmentation.personality = Some((constants::DW_EH_PE_udata8, Pointer::Direct(0xf00d_f00d))); + + assert_eq!( + Augmentation::parse(aug_str, &bases, address_size, §ion, input), + Ok(augmentation) + ); + assert_eq!(*input, EndianSlice::new(&rest, LittleEndian)); + } + + #[test] + #[allow(non_snake_case)] + fn test_augmentation_parse_R() { + let bases = Default::default(); + let address_size = 8; + let rest = [9, 8, 7, 6, 5, 4, 3, 2, 1]; + + let section = Section::with_endian(Endian::Little) + .uleb(1) + .D8(constants::DW_EH_PE_udata4.0) + .append_bytes(&rest) + .get_contents() + .unwrap(); + let section = EhFrame::new(§ion, LittleEndian); + let input = &mut section.section().clone(); + let aug_str = &mut EndianSlice::new(b"zR", LittleEndian); + + let mut augmentation = Augmentation::default(); + augmentation.fde_address_encoding = Some(constants::DW_EH_PE_udata4); + + assert_eq!( + Augmentation::parse(aug_str, &bases, address_size, §ion, input), + Ok(augmentation) + ); + assert_eq!(*input, EndianSlice::new(&rest, LittleEndian)); + } + + #[test] + #[allow(non_snake_case)] + fn test_augmentation_parse_S() { + let bases = Default::default(); + let address_size = 8; + let rest = [9, 8, 7, 6, 5, 4, 3, 2, 1]; + + let section = Section::with_endian(Endian::Little) + .uleb(0) + .append_bytes(&rest) + .get_contents() + .unwrap(); + let section = EhFrame::new(§ion, LittleEndian); + let input = &mut section.section().clone(); + let aug_str = &mut EndianSlice::new(b"zS", LittleEndian); + + let mut augmentation = Augmentation::default(); + augmentation.is_signal_trampoline = true; + + assert_eq!( + Augmentation::parse(aug_str, &bases, address_size, §ion, input), + Ok(augmentation) + ); + assert_eq!(*input, EndianSlice::new(&rest, LittleEndian)); + } + + #[test] + fn test_augmentation_parse_all() { + let bases = Default::default(); + let address_size = 8; + let rest = [9, 8, 7, 6, 5, 4, 3, 2, 1]; + + let section = Section::with_endian(Endian::Little) + .uleb(1 + 9 + 1) + // L + .D8(constants::DW_EH_PE_uleb128.0) + // P + .D8(constants::DW_EH_PE_udata8.0) + .L64(0x1bad_f00d) + // R + .D8(constants::DW_EH_PE_uleb128.0) + .append_bytes(&rest) + .get_contents() + .unwrap(); + let section = EhFrame::new(§ion, LittleEndian); + let input = &mut section.section().clone(); + let aug_str = &mut EndianSlice::new(b"zLPRS", LittleEndian); + + let augmentation = Augmentation { + lsda: Some(constants::DW_EH_PE_uleb128), + personality: Some((constants::DW_EH_PE_udata8, Pointer::Direct(0x1bad_f00d))), + fde_address_encoding: Some(constants::DW_EH_PE_uleb128), + is_signal_trampoline: true, + }; + + assert_eq!( + Augmentation::parse(aug_str, &bases, address_size, §ion, input), + Ok(augmentation) + ); + assert_eq!(*input, EndianSlice::new(&rest, LittleEndian)); + } + + #[test] + fn test_eh_frame_fde_no_augmentation() { + let instrs = [1, 2, 3, 4]; + let cie_offset = 1; + + let mut cie = make_test_cie(); + cie.format = Format::Dwarf32; + cie.version = 1; + + let mut fde = FrameDescriptionEntry { + offset: 0, + length: 0, + format: Format::Dwarf32, + cie: cie.clone(), + initial_segment: 0, + initial_address: 0xfeed_face, + address_range: 9000, + augmentation: None, + instructions: EndianSlice::new(&instrs, LittleEndian), + }; + + let rest = [1, 2, 3, 4]; + + let kind = eh_frame_le(); + let section = Section::with_endian(kind.endian()) + .fde(kind, cie_offset, &mut fde) + .append_bytes(&rest) + .get_contents() + .unwrap(); + let section = kind.section(§ion); + let input = &mut section.section().clone(); + + let result = parse_fde(section, input, |_, _, _| Ok(cie.clone())); + assert_eq!(result, Ok(fde)); + assert_eq!(*input, EndianSlice::new(&rest, LittleEndian)); + } + + #[test] + fn test_eh_frame_fde_empty_augmentation() { + let instrs = [1, 2, 3, 4]; + let cie_offset = 1; + + let mut cie = make_test_cie(); + cie.format = Format::Dwarf32; + cie.version = 1; + cie.augmentation = Some(Augmentation::default()); + + let mut fde = FrameDescriptionEntry { + offset: 0, + length: 0, + format: Format::Dwarf32, + cie: cie.clone(), + initial_segment: 0, + initial_address: 0xfeed_face, + address_range: 9000, + augmentation: Some(AugmentationData::default()), + instructions: EndianSlice::new(&instrs, LittleEndian), + }; + + let rest = [1, 2, 3, 4]; + + let kind = eh_frame_le(); + let section = Section::with_endian(kind.endian()) + .fde(kind, cie_offset, &mut fde) + .append_bytes(&rest) + .get_contents() + .unwrap(); + let section = kind.section(§ion); + let input = &mut section.section().clone(); + + let result = parse_fde(section, input, |_, _, _| Ok(cie.clone())); + assert_eq!(result, Ok(fde)); + assert_eq!(*input, EndianSlice::new(&rest, LittleEndian)); + } + + #[test] + fn test_eh_frame_fde_lsda_augmentation() { + let instrs = [1, 2, 3, 4]; + let cie_offset = 1; + + let mut cie = make_test_cie(); + cie.format = Format::Dwarf32; + cie.version = 1; + cie.augmentation = Some(Augmentation::default()); + cie.augmentation.as_mut().unwrap().lsda = Some(constants::DW_EH_PE_absptr); + + let mut fde = FrameDescriptionEntry { + offset: 0, + length: 0, + format: Format::Dwarf32, + cie: cie.clone(), + initial_segment: 0, + initial_address: 0xfeed_face, + address_range: 9000, + augmentation: Some(AugmentationData { + lsda: Some(Pointer::Direct(0x1122_3344)), + }), + instructions: EndianSlice::new(&instrs, LittleEndian), + }; + + let rest = [1, 2, 3, 4]; + + let kind = eh_frame_le(); + let section = Section::with_endian(kind.endian()) + .fde(kind, cie_offset, &mut fde) + .append_bytes(&rest) + .get_contents() + .unwrap(); + let section = kind.section(§ion); + let input = &mut section.section().clone(); + + let result = parse_fde(section, input, |_, _, _| Ok(cie.clone())); + assert_eq!(result, Ok(fde)); + assert_eq!(*input, EndianSlice::new(&rest, LittleEndian)); + } + + #[test] + fn test_eh_frame_fde_lsda_function_relative() { + let instrs = [1, 2, 3, 4]; + let cie_offset = 1; + + let mut cie = make_test_cie(); + cie.format = Format::Dwarf32; + cie.version = 1; + cie.augmentation = Some(Augmentation::default()); + cie.augmentation.as_mut().unwrap().lsda = Some(constants::DwEhPe( + constants::DW_EH_PE_funcrel.0 | constants::DW_EH_PE_absptr.0, + )); + + let mut fde = FrameDescriptionEntry { + offset: 0, + length: 0, + format: Format::Dwarf32, + cie: cie.clone(), + initial_segment: 0, + initial_address: 0xfeed_face, + address_range: 9000, + augmentation: Some(AugmentationData { + lsda: Some(Pointer::Direct(0xbeef)), + }), + instructions: EndianSlice::new(&instrs, LittleEndian), + }; + + let rest = [1, 2, 3, 4]; + + let kind = eh_frame_le(); + let section = Section::with_endian(kind.endian()) + .append_repeated(10, 10) + .fde(kind, cie_offset, &mut fde) + .append_bytes(&rest) + .get_contents() + .unwrap(); + let section = kind.section(§ion); + let input = &mut section.section().range_from(10..); + + // Adjust the FDE's augmentation to be relative to the function. + fde.augmentation.as_mut().unwrap().lsda = Some(Pointer::Direct(0xfeed_face + 0xbeef)); + + let result = parse_fde(section, input, |_, _, _| Ok(cie.clone())); + assert_eq!(result, Ok(fde)); + assert_eq!(*input, EndianSlice::new(&rest, LittleEndian)); + } + + #[test] + fn test_eh_frame_cie_personality_function_relative_bad_context() { + let instrs = [1, 2, 3, 4]; + + let length = Label::new(); + let start = Label::new(); + let end = Label::new(); + + let aug_len = Label::new(); + let aug_start = Label::new(); + let aug_end = Label::new(); + + let section = Section::with_endian(Endian::Little) + // Length + .L32(&length) + .mark(&start) + // CIE ID + .L32(0) + // Version + .D8(1) + // Augmentation + .append_bytes(b"zP\0") + // Code alignment factor + .uleb(1) + // Data alignment factor + .sleb(1) + // Return address register + .uleb(1) + // Augmentation data length. This is a uleb, be we rely on the value + // being less than 2^7 and therefore a valid uleb (can't use Label + // with uleb). + .D8(&aug_len) + .mark(&aug_start) + // Augmentation data. Personality encoding and then encoded pointer. + .D8(constants::DW_EH_PE_funcrel.0 | constants::DW_EH_PE_uleb128.0) + .uleb(1) + .mark(&aug_end) + // Initial instructions + .append_bytes(&instrs) + .mark(&end); + + length.set_const((&end - &start) as u64); + aug_len.set_const((&aug_end - &aug_start) as u64); + + let section = section.get_contents().unwrap(); + let section = EhFrame::new(§ion, LittleEndian); + + let bases = BaseAddresses::default(); + let mut iter = section.entries(&bases); + assert_eq!(iter.next(), Err(Error::FuncRelativePointerInBadContext)); + } + + #[test] + fn register_rule_map_eq() { + // Different order, but still equal. + let map1: RegisterRuleMap<EndianSlice<LittleEndian>> = [ + (Register(0), RegisterRule::SameValue), + (Register(3), RegisterRule::Offset(1)), + ] + .iter() + .collect(); + let map2: RegisterRuleMap<EndianSlice<LittleEndian>> = [ + (Register(3), RegisterRule::Offset(1)), + (Register(0), RegisterRule::SameValue), + ] + .iter() + .collect(); + assert_eq!(map1, map2); + assert_eq!(map2, map1); + + // Not equal. + let map3: RegisterRuleMap<EndianSlice<LittleEndian>> = [ + (Register(0), RegisterRule::SameValue), + (Register(2), RegisterRule::Offset(1)), + ] + .iter() + .collect(); + let map4: RegisterRuleMap<EndianSlice<LittleEndian>> = [ + (Register(3), RegisterRule::Offset(1)), + (Register(0), RegisterRule::SameValue), + ] + .iter() + .collect(); + assert!(map3 != map4); + assert!(map4 != map3); + + // One has undefined explicitly set, other implicitly has undefined. + let mut map5 = RegisterRuleMap::<EndianSlice<LittleEndian>>::default(); + map5.set(Register(0), RegisterRule::SameValue).unwrap(); + map5.set(Register(0), RegisterRule::Undefined).unwrap(); + let map6 = RegisterRuleMap::<EndianSlice<LittleEndian>>::default(); + assert_eq!(map5, map6); + assert_eq!(map6, map5); + } + + #[test] + fn iter_register_rules() { + let mut row = UnwindTableRow::<EndianSlice<LittleEndian>>::default(); + row.registers = [ + (Register(0), RegisterRule::SameValue), + (Register(1), RegisterRule::Offset(1)), + (Register(2), RegisterRule::ValOffset(2)), + ] + .iter() + .collect(); + + let mut found0 = false; + let mut found1 = false; + let mut found2 = false; + + for &(register, ref rule) in row.registers() { + match register.0 { + 0 => { + assert_eq!(found0, false); + found0 = true; + assert_eq!(*rule, RegisterRule::SameValue); + } + 1 => { + assert_eq!(found1, false); + found1 = true; + assert_eq!(*rule, RegisterRule::Offset(1)); + } + 2 => { + assert_eq!(found2, false); + found2 = true; + assert_eq!(*rule, RegisterRule::ValOffset(2)); + } + x => panic!("Unexpected register rule: ({}, {:?})", x, rule), + } + } + + assert_eq!(found0, true); + assert_eq!(found1, true); + assert_eq!(found2, true); + } + + #[test] + #[cfg(target_pointer_width = "64")] + fn size_of_unwind_ctx() { + use core::mem; + let size = mem::size_of::<UnwindContext<EndianSlice<NativeEndian>>>(); + let max_size = 30968; + if size > max_size { + assert_eq!(size, max_size); + } + } + + #[test] + #[cfg(target_pointer_width = "64")] + fn size_of_register_rule_map() { + use core::mem; + let size = mem::size_of::<RegisterRuleMap<EndianSlice<NativeEndian>>>(); + let max_size = 6152; + if size > max_size { + assert_eq!(size, max_size); + } + } + + #[test] + fn test_parse_pointer_encoding_ok() { + use crate::endianity::NativeEndian; + let expected = + constants::DwEhPe(constants::DW_EH_PE_uleb128.0 | constants::DW_EH_PE_pcrel.0); + let input = [expected.0, 1, 2, 3, 4]; + let input = &mut EndianSlice::new(&input, NativeEndian); + assert_eq!(parse_pointer_encoding(input), Ok(expected)); + assert_eq!(*input, EndianSlice::new(&[1, 2, 3, 4], NativeEndian)); + } + + #[test] + fn test_parse_pointer_encoding_bad_encoding() { + use crate::endianity::NativeEndian; + let expected = + constants::DwEhPe((constants::DW_EH_PE_sdata8.0 + 1) | constants::DW_EH_PE_pcrel.0); + let input = [expected.0, 1, 2, 3, 4]; + let input = &mut EndianSlice::new(&input, NativeEndian); + assert_eq!( + Err(Error::UnknownPointerEncoding), + parse_pointer_encoding(input) + ); + } + + #[test] + fn test_parse_encoded_pointer_absptr() { + let encoding = constants::DW_EH_PE_absptr; + let expected_rest = [1, 2, 3, 4]; + + let input = Section::with_endian(Endian::Little) + .L32(0xf00d_f00d) + .append_bytes(&expected_rest); + let input = input.get_contents().unwrap(); + let input = EndianSlice::new(&input, LittleEndian); + let mut rest = input; + + let parameters = PointerEncodingParameters { + bases: &SectionBaseAddresses::default(), + func_base: None, + address_size: 4, + section: &input, + }; + assert_eq!( + parse_encoded_pointer(encoding, ¶meters, &mut rest), + Ok(Pointer::Direct(0xf00d_f00d)) + ); + assert_eq!(rest, EndianSlice::new(&expected_rest, LittleEndian)); + } + + #[test] + fn test_parse_encoded_pointer_pcrel() { + let encoding = constants::DW_EH_PE_pcrel; + let expected_rest = [1, 2, 3, 4]; + + let input = Section::with_endian(Endian::Little) + .append_repeated(0, 0x10) + .L32(0x1) + .append_bytes(&expected_rest); + let input = input.get_contents().unwrap(); + let input = EndianSlice::new(&input, LittleEndian); + let mut rest = input.range_from(0x10..); + + let parameters = PointerEncodingParameters { + bases: &BaseAddresses::default().set_eh_frame(0x100).eh_frame, + func_base: None, + address_size: 4, + section: &input, + }; + assert_eq!( + parse_encoded_pointer(encoding, ¶meters, &mut rest), + Ok(Pointer::Direct(0x111)) + ); + assert_eq!(rest, EndianSlice::new(&expected_rest, LittleEndian)); + } + + #[test] + fn test_parse_encoded_pointer_pcrel_undefined() { + let encoding = constants::DW_EH_PE_pcrel; + + let input = Section::with_endian(Endian::Little).L32(0x1); + let input = input.get_contents().unwrap(); + let input = EndianSlice::new(&input, LittleEndian); + let mut rest = input; + + let parameters = PointerEncodingParameters { + bases: &SectionBaseAddresses::default(), + func_base: None, + address_size: 4, + section: &input, + }; + assert_eq!( + parse_encoded_pointer(encoding, ¶meters, &mut rest), + Err(Error::PcRelativePointerButSectionBaseIsUndefined) + ); + } + + #[test] + fn test_parse_encoded_pointer_textrel() { + let encoding = constants::DW_EH_PE_textrel; + let expected_rest = [1, 2, 3, 4]; + + let input = Section::with_endian(Endian::Little) + .L32(0x1) + .append_bytes(&expected_rest); + let input = input.get_contents().unwrap(); + let input = EndianSlice::new(&input, LittleEndian); + let mut rest = input; + + let parameters = PointerEncodingParameters { + bases: &BaseAddresses::default().set_text(0x10).eh_frame, + func_base: None, + address_size: 4, + section: &input, + }; + assert_eq!( + parse_encoded_pointer(encoding, ¶meters, &mut rest), + Ok(Pointer::Direct(0x11)) + ); + assert_eq!(rest, EndianSlice::new(&expected_rest, LittleEndian)); + } + + #[test] + fn test_parse_encoded_pointer_textrel_undefined() { + let encoding = constants::DW_EH_PE_textrel; + + let input = Section::with_endian(Endian::Little).L32(0x1); + let input = input.get_contents().unwrap(); + let input = EndianSlice::new(&input, LittleEndian); + let mut rest = input; + + let parameters = PointerEncodingParameters { + bases: &SectionBaseAddresses::default(), + func_base: None, + address_size: 4, + section: &input, + }; + assert_eq!( + parse_encoded_pointer(encoding, ¶meters, &mut rest), + Err(Error::TextRelativePointerButTextBaseIsUndefined) + ); + } + + #[test] + fn test_parse_encoded_pointer_datarel() { + let encoding = constants::DW_EH_PE_datarel; + let expected_rest = [1, 2, 3, 4]; + + let input = Section::with_endian(Endian::Little) + .L32(0x1) + .append_bytes(&expected_rest); + let input = input.get_contents().unwrap(); + let input = EndianSlice::new(&input, LittleEndian); + let mut rest = input; + + let parameters = PointerEncodingParameters { + bases: &BaseAddresses::default().set_got(0x10).eh_frame, + func_base: None, + address_size: 4, + section: &input, + }; + assert_eq!( + parse_encoded_pointer(encoding, ¶meters, &mut rest), + Ok(Pointer::Direct(0x11)) + ); + assert_eq!(rest, EndianSlice::new(&expected_rest, LittleEndian)); + } + + #[test] + fn test_parse_encoded_pointer_datarel_undefined() { + let encoding = constants::DW_EH_PE_datarel; + + let input = Section::with_endian(Endian::Little).L32(0x1); + let input = input.get_contents().unwrap(); + let input = EndianSlice::new(&input, LittleEndian); + let mut rest = input; + + let parameters = PointerEncodingParameters { + bases: &SectionBaseAddresses::default(), + func_base: None, + address_size: 4, + section: &input, + }; + assert_eq!( + parse_encoded_pointer(encoding, ¶meters, &mut rest), + Err(Error::DataRelativePointerButDataBaseIsUndefined) + ); + } + + #[test] + fn test_parse_encoded_pointer_funcrel() { + let encoding = constants::DW_EH_PE_funcrel; + let expected_rest = [1, 2, 3, 4]; + + let input = Section::with_endian(Endian::Little) + .L32(0x1) + .append_bytes(&expected_rest); + let input = input.get_contents().unwrap(); + let input = EndianSlice::new(&input, LittleEndian); + let mut rest = input; + + let parameters = PointerEncodingParameters { + bases: &SectionBaseAddresses::default(), + func_base: Some(0x10), + address_size: 4, + section: &input, + }; + assert_eq!( + parse_encoded_pointer(encoding, ¶meters, &mut rest), + Ok(Pointer::Direct(0x11)) + ); + assert_eq!(rest, EndianSlice::new(&expected_rest, LittleEndian)); + } + + #[test] + fn test_parse_encoded_pointer_funcrel_undefined() { + let encoding = constants::DW_EH_PE_funcrel; + + let input = Section::with_endian(Endian::Little).L32(0x1); + let input = input.get_contents().unwrap(); + let input = EndianSlice::new(&input, LittleEndian); + let mut rest = input; + + let parameters = PointerEncodingParameters { + bases: &SectionBaseAddresses::default(), + func_base: None, + address_size: 4, + section: &input, + }; + assert_eq!( + parse_encoded_pointer(encoding, ¶meters, &mut rest), + Err(Error::FuncRelativePointerInBadContext) + ); + } + + #[test] + fn test_parse_encoded_pointer_uleb128() { + let encoding = + constants::DwEhPe(constants::DW_EH_PE_absptr.0 | constants::DW_EH_PE_uleb128.0); + let expected_rest = [1, 2, 3, 4]; + + let input = Section::with_endian(Endian::Little) + .uleb(0x12_3456) + .append_bytes(&expected_rest); + let input = input.get_contents().unwrap(); + let input = EndianSlice::new(&input, LittleEndian); + let mut rest = input; + + let parameters = PointerEncodingParameters { + bases: &SectionBaseAddresses::default(), + func_base: None, + address_size: 4, + section: &input, + }; + assert_eq!( + parse_encoded_pointer(encoding, ¶meters, &mut rest), + Ok(Pointer::Direct(0x12_3456)) + ); + assert_eq!(rest, EndianSlice::new(&expected_rest, LittleEndian)); + } + + #[test] + fn test_parse_encoded_pointer_udata2() { + let encoding = + constants::DwEhPe(constants::DW_EH_PE_absptr.0 | constants::DW_EH_PE_udata2.0); + let expected_rest = [1, 2, 3, 4]; + + let input = Section::with_endian(Endian::Little) + .L16(0x1234) + .append_bytes(&expected_rest); + let input = input.get_contents().unwrap(); + let input = EndianSlice::new(&input, LittleEndian); + let mut rest = input; + + let parameters = PointerEncodingParameters { + bases: &SectionBaseAddresses::default(), + func_base: None, + address_size: 4, + section: &input, + }; + assert_eq!( + parse_encoded_pointer(encoding, ¶meters, &mut rest), + Ok(Pointer::Direct(0x1234)) + ); + assert_eq!(rest, EndianSlice::new(&expected_rest, LittleEndian)); + } + + #[test] + fn test_parse_encoded_pointer_udata4() { + let encoding = + constants::DwEhPe(constants::DW_EH_PE_absptr.0 | constants::DW_EH_PE_udata4.0); + let expected_rest = [1, 2, 3, 4]; + + let input = Section::with_endian(Endian::Little) + .L32(0x1234_5678) + .append_bytes(&expected_rest); + let input = input.get_contents().unwrap(); + let input = EndianSlice::new(&input, LittleEndian); + let mut rest = input; + + let parameters = PointerEncodingParameters { + bases: &SectionBaseAddresses::default(), + func_base: None, + address_size: 4, + section: &input, + }; + assert_eq!( + parse_encoded_pointer(encoding, ¶meters, &mut rest), + Ok(Pointer::Direct(0x1234_5678)) + ); + assert_eq!(rest, EndianSlice::new(&expected_rest, LittleEndian)); + } + + #[test] + fn test_parse_encoded_pointer_udata8() { + let encoding = + constants::DwEhPe(constants::DW_EH_PE_absptr.0 | constants::DW_EH_PE_udata8.0); + let expected_rest = [1, 2, 3, 4]; + + let input = Section::with_endian(Endian::Little) + .L64(0x1234_5678_1234_5678) + .append_bytes(&expected_rest); + let input = input.get_contents().unwrap(); + let input = EndianSlice::new(&input, LittleEndian); + let mut rest = input; + + let parameters = PointerEncodingParameters { + bases: &SectionBaseAddresses::default(), + func_base: None, + address_size: 4, + section: &input, + }; + assert_eq!( + parse_encoded_pointer(encoding, ¶meters, &mut rest), + Ok(Pointer::Direct(0x1234_5678_1234_5678)) + ); + assert_eq!(rest, EndianSlice::new(&expected_rest, LittleEndian)); + } + + #[test] + fn test_parse_encoded_pointer_sleb128() { + let encoding = + constants::DwEhPe(constants::DW_EH_PE_textrel.0 | constants::DW_EH_PE_sleb128.0); + let expected_rest = [1, 2, 3, 4]; + + let input = Section::with_endian(Endian::Little) + .sleb(-0x1111) + .append_bytes(&expected_rest); + let input = input.get_contents().unwrap(); + let input = EndianSlice::new(&input, LittleEndian); + let mut rest = input; + + let parameters = PointerEncodingParameters { + bases: &BaseAddresses::default().set_text(0x1111_1111).eh_frame, + func_base: None, + address_size: 4, + section: &input, + }; + assert_eq!( + parse_encoded_pointer(encoding, ¶meters, &mut rest), + Ok(Pointer::Direct(0x1111_0000)) + ); + assert_eq!(rest, EndianSlice::new(&expected_rest, LittleEndian)); + } + + #[test] + fn test_parse_encoded_pointer_sdata2() { + let encoding = + constants::DwEhPe(constants::DW_EH_PE_absptr.0 | constants::DW_EH_PE_sdata2.0); + let expected_rest = [1, 2, 3, 4]; + let expected = 0x111 as i16; + + let input = Section::with_endian(Endian::Little) + .L16(expected as u16) + .append_bytes(&expected_rest); + let input = input.get_contents().unwrap(); + let input = EndianSlice::new(&input, LittleEndian); + let mut rest = input; + + let parameters = PointerEncodingParameters { + bases: &SectionBaseAddresses::default(), + func_base: None, + address_size: 4, + section: &input, + }; + assert_eq!( + parse_encoded_pointer(encoding, ¶meters, &mut rest), + Ok(Pointer::Direct(expected as u64)) + ); + assert_eq!(rest, EndianSlice::new(&expected_rest, LittleEndian)); + } + + #[test] + fn test_parse_encoded_pointer_sdata4() { + let encoding = + constants::DwEhPe(constants::DW_EH_PE_absptr.0 | constants::DW_EH_PE_sdata4.0); + let expected_rest = [1, 2, 3, 4]; + let expected = 0x111_1111 as i32; + + let input = Section::with_endian(Endian::Little) + .L32(expected as u32) + .append_bytes(&expected_rest); + let input = input.get_contents().unwrap(); + let input = EndianSlice::new(&input, LittleEndian); + let mut rest = input; + + let parameters = PointerEncodingParameters { + bases: &SectionBaseAddresses::default(), + func_base: None, + address_size: 4, + section: &input, + }; + assert_eq!( + parse_encoded_pointer(encoding, ¶meters, &mut rest), + Ok(Pointer::Direct(expected as u64)) + ); + assert_eq!(rest, EndianSlice::new(&expected_rest, LittleEndian)); + } + + #[test] + fn test_parse_encoded_pointer_sdata8() { + let encoding = + constants::DwEhPe(constants::DW_EH_PE_absptr.0 | constants::DW_EH_PE_sdata8.0); + let expected_rest = [1, 2, 3, 4]; + let expected = -0x11_1111_1222_2222 as i64; + + let input = Section::with_endian(Endian::Little) + .L64(expected as u64) + .append_bytes(&expected_rest); + let input = input.get_contents().unwrap(); + let input = EndianSlice::new(&input, LittleEndian); + let mut rest = input; + + let parameters = PointerEncodingParameters { + bases: &SectionBaseAddresses::default(), + func_base: None, + address_size: 4, + section: &input, + }; + assert_eq!( + parse_encoded_pointer(encoding, ¶meters, &mut rest), + Ok(Pointer::Direct(expected as u64)) + ); + assert_eq!(rest, EndianSlice::new(&expected_rest, LittleEndian)); + } + + #[test] + fn test_parse_encoded_pointer_omit() { + let encoding = constants::DW_EH_PE_omit; + + let input = Section::with_endian(Endian::Little).L32(0x1); + let input = input.get_contents().unwrap(); + let input = EndianSlice::new(&input, LittleEndian); + let mut rest = input; + + let parameters = PointerEncodingParameters { + bases: &SectionBaseAddresses::default(), + func_base: None, + address_size: 4, + section: &input, + }; + assert_eq!( + parse_encoded_pointer(encoding, ¶meters, &mut rest), + Err(Error::CannotParseOmitPointerEncoding) + ); + assert_eq!(rest, input); + } + + #[test] + fn test_parse_encoded_pointer_bad_encoding() { + let encoding = constants::DwEhPe(constants::DW_EH_PE_sdata8.0 + 1); + + let input = Section::with_endian(Endian::Little).L32(0x1); + let input = input.get_contents().unwrap(); + let input = EndianSlice::new(&input, LittleEndian); + let mut rest = input; + + let parameters = PointerEncodingParameters { + bases: &SectionBaseAddresses::default(), + func_base: None, + address_size: 4, + section: &input, + }; + assert_eq!( + parse_encoded_pointer(encoding, ¶meters, &mut rest), + Err(Error::UnknownPointerEncoding) + ); + } + + #[test] + fn test_parse_encoded_pointer_aligned() { + // FIXME: support this encoding! + + let encoding = constants::DW_EH_PE_aligned; + + let input = Section::with_endian(Endian::Little).L32(0x1); + let input = input.get_contents().unwrap(); + let input = EndianSlice::new(&input, LittleEndian); + let mut rest = input; + + let parameters = PointerEncodingParameters { + bases: &SectionBaseAddresses::default(), + func_base: None, + address_size: 4, + section: &input, + }; + assert_eq!( + parse_encoded_pointer(encoding, ¶meters, &mut rest), + Err(Error::UnsupportedPointerEncoding) + ); + } + + #[test] + fn test_parse_encoded_pointer_indirect() { + let expected_rest = [1, 2, 3, 4]; + let encoding = constants::DW_EH_PE_indirect; + + let input = Section::with_endian(Endian::Little) + .L32(0x1234_5678) + .append_bytes(&expected_rest); + let input = input.get_contents().unwrap(); + let input = EndianSlice::new(&input, LittleEndian); + let mut rest = input; + + let parameters = PointerEncodingParameters { + bases: &SectionBaseAddresses::default(), + func_base: None, + address_size: 4, + section: &input, + }; + assert_eq!( + parse_encoded_pointer(encoding, ¶meters, &mut rest), + Ok(Pointer::Indirect(0x1234_5678)) + ); + assert_eq!(rest, EndianSlice::new(&expected_rest, LittleEndian)); + } +} diff --git a/vendor/gimli/src/read/dwarf.rs b/vendor/gimli/src/read/dwarf.rs new file mode 100644 index 0000000..2f8dcb3 --- /dev/null +++ b/vendor/gimli/src/read/dwarf.rs @@ -0,0 +1,1210 @@ +use alloc::string::String; +use alloc::sync::Arc; + +use crate::common::{ + DebugAddrBase, DebugAddrIndex, DebugInfoOffset, DebugLineStrOffset, DebugLocListsBase, + DebugLocListsIndex, DebugRngListsBase, DebugRngListsIndex, DebugStrOffset, DebugStrOffsetsBase, + DebugStrOffsetsIndex, DebugTypeSignature, DebugTypesOffset, DwarfFileType, DwoId, Encoding, + LocationListsOffset, RangeListsOffset, RawRangeListsOffset, SectionId, UnitSectionOffset, +}; +use crate::constants; +use crate::read::{ + Abbreviations, AbbreviationsCache, AbbreviationsCacheStrategy, AttributeValue, DebugAbbrev, + DebugAddr, DebugAranges, DebugCuIndex, DebugInfo, DebugInfoUnitHeadersIter, DebugLine, + DebugLineStr, DebugLoc, DebugLocLists, DebugRngLists, DebugStr, DebugStrOffsets, DebugTuIndex, + DebugTypes, DebugTypesUnitHeadersIter, DebuggingInformationEntry, EntriesCursor, EntriesRaw, + EntriesTree, Error, IncompleteLineProgram, LocListIter, LocationLists, Range, RangeLists, + RawLocListIter, RawRngListIter, Reader, ReaderOffset, ReaderOffsetId, Result, RngListIter, + Section, UnitHeader, UnitIndex, UnitIndexSectionIterator, UnitOffset, UnitType, +}; + +/// All of the commonly used DWARF sections, and other common information. +#[derive(Debug, Default)] +pub struct Dwarf<R> { + /// The `.debug_abbrev` section. + pub debug_abbrev: DebugAbbrev<R>, + + /// The `.debug_addr` section. + pub debug_addr: DebugAddr<R>, + + /// The `.debug_aranges` section. + pub debug_aranges: DebugAranges<R>, + + /// The `.debug_info` section. + pub debug_info: DebugInfo<R>, + + /// The `.debug_line` section. + pub debug_line: DebugLine<R>, + + /// The `.debug_line_str` section. + pub debug_line_str: DebugLineStr<R>, + + /// The `.debug_str` section. + pub debug_str: DebugStr<R>, + + /// The `.debug_str_offsets` section. + pub debug_str_offsets: DebugStrOffsets<R>, + + /// The `.debug_types` section. + pub debug_types: DebugTypes<R>, + + /// The location lists in the `.debug_loc` and `.debug_loclists` sections. + pub locations: LocationLists<R>, + + /// The range lists in the `.debug_ranges` and `.debug_rnglists` sections. + pub ranges: RangeLists<R>, + + /// The type of this file. + pub file_type: DwarfFileType, + + /// The DWARF sections for a supplementary object file. + pub sup: Option<Arc<Dwarf<R>>>, + + /// A cache of previously parsed abbreviations for units in this file. + pub abbreviations_cache: AbbreviationsCache, +} + +impl<T> Dwarf<T> { + /// Try to load the DWARF sections using the given loader function. + /// + /// `section` loads a DWARF section from the object file. + /// It should return an empty section if the section does not exist. + /// + /// `section` may either directly return a `Reader` instance (such as + /// `EndianSlice`), or it may return some other type and then convert + /// that type into a `Reader` using `Dwarf::borrow`. + /// + /// After loading, the user should set the `file_type` field and + /// call `load_sup` if required. + pub fn load<F, E>(mut section: F) -> core::result::Result<Self, E> + where + F: FnMut(SectionId) -> core::result::Result<T, E>, + { + // Section types are inferred. + let debug_loc = Section::load(&mut section)?; + let debug_loclists = Section::load(&mut section)?; + let debug_ranges = Section::load(&mut section)?; + let debug_rnglists = Section::load(&mut section)?; + Ok(Dwarf { + debug_abbrev: Section::load(&mut section)?, + debug_addr: Section::load(&mut section)?, + debug_aranges: Section::load(&mut section)?, + debug_info: Section::load(&mut section)?, + debug_line: Section::load(&mut section)?, + debug_line_str: Section::load(&mut section)?, + debug_str: Section::load(&mut section)?, + debug_str_offsets: Section::load(&mut section)?, + debug_types: Section::load(&mut section)?, + locations: LocationLists::new(debug_loc, debug_loclists), + ranges: RangeLists::new(debug_ranges, debug_rnglists), + file_type: DwarfFileType::Main, + sup: None, + abbreviations_cache: AbbreviationsCache::new(), + }) + } + + /// Load the DWARF sections from the supplementary object file. + /// + /// `section` operates the same as for `load`. + /// + /// Sets `self.sup`, replacing any previous value. + pub fn load_sup<F, E>(&mut self, section: F) -> core::result::Result<(), E> + where + F: FnMut(SectionId) -> core::result::Result<T, E>, + { + self.sup = Some(Arc::new(Self::load(section)?)); + Ok(()) + } + + /// Create a `Dwarf` structure that references the data in `self`. + /// + /// This is useful when `R` implements `Reader` but `T` does not. + /// + /// ## Example Usage + /// + /// It can be useful to load DWARF sections into owned data structures, + /// such as `Vec`. However, we do not implement the `Reader` trait + /// for `Vec`, because it would be very inefficient, but this trait + /// is required for all of the methods that parse the DWARF data. + /// So we first load the DWARF sections into `Vec`s, and then use + /// `borrow` to create `Reader`s that reference the data. + /// + /// ```rust,no_run + /// # fn example() -> Result<(), gimli::Error> { + /// # let loader = |name| -> Result<_, gimli::Error> { unimplemented!() }; + /// # let sup_loader = |name| -> Result<_, gimli::Error> { unimplemented!() }; + /// // Read the DWARF sections into `Vec`s with whatever object loader you're using. + /// let mut owned_dwarf: gimli::Dwarf<Vec<u8>> = gimli::Dwarf::load(loader)?; + /// owned_dwarf.load_sup(sup_loader)?; + /// // Create references to the DWARF sections. + /// let dwarf = owned_dwarf.borrow(|section| { + /// gimli::EndianSlice::new(§ion, gimli::LittleEndian) + /// }); + /// # unreachable!() + /// # } + /// ``` + pub fn borrow<'a, F, R>(&'a self, mut borrow: F) -> Dwarf<R> + where + F: FnMut(&'a T) -> R, + { + Dwarf { + debug_abbrev: self.debug_abbrev.borrow(&mut borrow), + debug_addr: self.debug_addr.borrow(&mut borrow), + debug_aranges: self.debug_aranges.borrow(&mut borrow), + debug_info: self.debug_info.borrow(&mut borrow), + debug_line: self.debug_line.borrow(&mut borrow), + debug_line_str: self.debug_line_str.borrow(&mut borrow), + debug_str: self.debug_str.borrow(&mut borrow), + debug_str_offsets: self.debug_str_offsets.borrow(&mut borrow), + debug_types: self.debug_types.borrow(&mut borrow), + locations: self.locations.borrow(&mut borrow), + ranges: self.ranges.borrow(&mut borrow), + file_type: self.file_type, + sup: self.sup().map(|sup| Arc::new(sup.borrow(borrow))), + abbreviations_cache: AbbreviationsCache::new(), + } + } + + /// Return a reference to the DWARF sections for supplementary object file. + pub fn sup(&self) -> Option<&Dwarf<T>> { + self.sup.as_ref().map(Arc::as_ref) + } +} + +impl<R: Reader> Dwarf<R> { + /// Parse abbreviations and store them in the cache. + /// + /// This will iterate over the units in `self.debug_info` to determine the + /// abbreviations offsets. + /// + /// Errors during parsing abbreviations are also stored in the cache. + /// Errors during iterating over the units are ignored. + pub fn populate_abbreviations_cache(&mut self, strategy: AbbreviationsCacheStrategy) { + self.abbreviations_cache + .populate(strategy, &self.debug_abbrev, self.debug_info.units()); + } + + /// Iterate the unit headers in the `.debug_info` section. + /// + /// Can be [used with + /// `FallibleIterator`](./index.html#using-with-fallibleiterator). + #[inline] + pub fn units(&self) -> DebugInfoUnitHeadersIter<R> { + self.debug_info.units() + } + + /// Construct a new `Unit` from the given unit header. + #[inline] + pub fn unit(&self, header: UnitHeader<R>) -> Result<Unit<R>> { + Unit::new(self, header) + } + + /// Iterate the type-unit headers in the `.debug_types` section. + /// + /// Can be [used with + /// `FallibleIterator`](./index.html#using-with-fallibleiterator). + #[inline] + pub fn type_units(&self) -> DebugTypesUnitHeadersIter<R> { + self.debug_types.units() + } + + /// Parse the abbreviations for a compilation unit. + #[inline] + pub fn abbreviations(&self, unit: &UnitHeader<R>) -> Result<Arc<Abbreviations>> { + self.abbreviations_cache + .get(&self.debug_abbrev, unit.debug_abbrev_offset()) + } + + /// Return the string offset at the given index. + #[inline] + pub fn string_offset( + &self, + unit: &Unit<R>, + index: DebugStrOffsetsIndex<R::Offset>, + ) -> Result<DebugStrOffset<R::Offset>> { + self.debug_str_offsets + .get_str_offset(unit.header.format(), unit.str_offsets_base, index) + } + + /// Return the string at the given offset in `.debug_str`. + #[inline] + pub fn string(&self, offset: DebugStrOffset<R::Offset>) -> Result<R> { + self.debug_str.get_str(offset) + } + + /// Return the string at the given offset in `.debug_line_str`. + #[inline] + pub fn line_string(&self, offset: DebugLineStrOffset<R::Offset>) -> Result<R> { + self.debug_line_str.get_str(offset) + } + + /// Return an attribute value as a string slice. + /// + /// If the attribute value is one of: + /// + /// - an inline `DW_FORM_string` string + /// - a `DW_FORM_strp` reference to an offset into the `.debug_str` section + /// - a `DW_FORM_strp_sup` reference to an offset into a supplementary + /// object file + /// - a `DW_FORM_line_strp` reference to an offset into the `.debug_line_str` + /// section + /// - a `DW_FORM_strx` index into the `.debug_str_offsets` entries for the unit + /// + /// then return the attribute's string value. Returns an error if the attribute + /// value does not have a string form, or if a string form has an invalid value. + pub fn attr_string(&self, unit: &Unit<R>, attr: AttributeValue<R>) -> Result<R> { + match attr { + AttributeValue::String(string) => Ok(string), + AttributeValue::DebugStrRef(offset) => self.debug_str.get_str(offset), + AttributeValue::DebugStrRefSup(offset) => { + if let Some(sup) = self.sup() { + sup.debug_str.get_str(offset) + } else { + Err(Error::ExpectedStringAttributeValue) + } + } + AttributeValue::DebugLineStrRef(offset) => self.debug_line_str.get_str(offset), + AttributeValue::DebugStrOffsetsIndex(index) => { + let offset = self.debug_str_offsets.get_str_offset( + unit.header.format(), + unit.str_offsets_base, + index, + )?; + self.debug_str.get_str(offset) + } + _ => Err(Error::ExpectedStringAttributeValue), + } + } + + /// Return the address at the given index. + pub fn address(&self, unit: &Unit<R>, index: DebugAddrIndex<R::Offset>) -> Result<u64> { + self.debug_addr + .get_address(unit.encoding().address_size, unit.addr_base, index) + } + + /// Try to return an attribute value as an address. + /// + /// If the attribute value is one of: + /// + /// - a `DW_FORM_addr` + /// - a `DW_FORM_addrx` index into the `.debug_addr` entries for the unit + /// + /// then return the address. + /// Returns `None` for other forms. + pub fn attr_address(&self, unit: &Unit<R>, attr: AttributeValue<R>) -> Result<Option<u64>> { + match attr { + AttributeValue::Addr(addr) => Ok(Some(addr)), + AttributeValue::DebugAddrIndex(index) => self.address(unit, index).map(Some), + _ => Ok(None), + } + } + + /// Return the range list offset for the given raw offset. + /// + /// This handles adding `DW_AT_GNU_ranges_base` if required. + pub fn ranges_offset_from_raw( + &self, + unit: &Unit<R>, + offset: RawRangeListsOffset<R::Offset>, + ) -> RangeListsOffset<R::Offset> { + if self.file_type == DwarfFileType::Dwo && unit.header.version() < 5 { + RangeListsOffset(offset.0.wrapping_add(unit.rnglists_base.0)) + } else { + RangeListsOffset(offset.0) + } + } + + /// Return the range list offset at the given index. + pub fn ranges_offset( + &self, + unit: &Unit<R>, + index: DebugRngListsIndex<R::Offset>, + ) -> Result<RangeListsOffset<R::Offset>> { + self.ranges + .get_offset(unit.encoding(), unit.rnglists_base, index) + } + + /// Iterate over the `RangeListEntry`s starting at the given offset. + pub fn ranges( + &self, + unit: &Unit<R>, + offset: RangeListsOffset<R::Offset>, + ) -> Result<RngListIter<R>> { + self.ranges.ranges( + offset, + unit.encoding(), + unit.low_pc, + &self.debug_addr, + unit.addr_base, + ) + } + + /// Iterate over the `RawRngListEntry`ies starting at the given offset. + pub fn raw_ranges( + &self, + unit: &Unit<R>, + offset: RangeListsOffset<R::Offset>, + ) -> Result<RawRngListIter<R>> { + self.ranges.raw_ranges(offset, unit.encoding()) + } + + /// Try to return an attribute value as a range list offset. + /// + /// If the attribute value is one of: + /// + /// - a `DW_FORM_sec_offset` reference to the `.debug_ranges` or `.debug_rnglists` sections + /// - a `DW_FORM_rnglistx` index into the `.debug_rnglists` entries for the unit + /// + /// then return the range list offset of the range list. + /// Returns `None` for other forms. + pub fn attr_ranges_offset( + &self, + unit: &Unit<R>, + attr: AttributeValue<R>, + ) -> Result<Option<RangeListsOffset<R::Offset>>> { + match attr { + AttributeValue::RangeListsRef(offset) => { + Ok(Some(self.ranges_offset_from_raw(unit, offset))) + } + AttributeValue::DebugRngListsIndex(index) => self.ranges_offset(unit, index).map(Some), + _ => Ok(None), + } + } + + /// Try to return an attribute value as a range list entry iterator. + /// + /// If the attribute value is one of: + /// + /// - a `DW_FORM_sec_offset` reference to the `.debug_ranges` or `.debug_rnglists` sections + /// - a `DW_FORM_rnglistx` index into the `.debug_rnglists` entries for the unit + /// + /// then return an iterator over the entries in the range list. + /// Returns `None` for other forms. + pub fn attr_ranges( + &self, + unit: &Unit<R>, + attr: AttributeValue<R>, + ) -> Result<Option<RngListIter<R>>> { + match self.attr_ranges_offset(unit, attr)? { + Some(offset) => Ok(Some(self.ranges(unit, offset)?)), + None => Ok(None), + } + } + + /// Return an iterator for the address ranges of a `DebuggingInformationEntry`. + /// + /// This uses `DW_AT_low_pc`, `DW_AT_high_pc` and `DW_AT_ranges`. + pub fn die_ranges( + &self, + unit: &Unit<R>, + entry: &DebuggingInformationEntry<R>, + ) -> Result<RangeIter<R>> { + let mut low_pc = None; + let mut high_pc = None; + let mut size = None; + let mut attrs = entry.attrs(); + while let Some(attr) = attrs.next()? { + match attr.name() { + constants::DW_AT_low_pc => { + low_pc = Some( + self.attr_address(unit, attr.value())? + .ok_or(Error::UnsupportedAttributeForm)?, + ); + } + constants::DW_AT_high_pc => match attr.value() { + AttributeValue::Udata(val) => size = Some(val), + attr => { + high_pc = Some( + self.attr_address(unit, attr)? + .ok_or(Error::UnsupportedAttributeForm)?, + ); + } + }, + constants::DW_AT_ranges => { + if let Some(list) = self.attr_ranges(unit, attr.value())? { + return Ok(RangeIter(RangeIterInner::List(list))); + } + } + _ => {} + } + } + let range = low_pc.and_then(|begin| { + let end = size.map(|size| begin + size).or(high_pc); + // TODO: perhaps return an error if `end` is `None` + end.map(|end| Range { begin, end }) + }); + Ok(RangeIter(RangeIterInner::Single(range))) + } + + /// Return an iterator for the address ranges of a `Unit`. + /// + /// This uses `DW_AT_low_pc`, `DW_AT_high_pc` and `DW_AT_ranges` of the + /// root `DebuggingInformationEntry`. + pub fn unit_ranges(&self, unit: &Unit<R>) -> Result<RangeIter<R>> { + let mut cursor = unit.header.entries(&unit.abbreviations); + cursor.next_dfs()?; + let root = cursor.current().ok_or(Error::MissingUnitDie)?; + self.die_ranges(unit, root) + } + + /// Return the location list offset at the given index. + pub fn locations_offset( + &self, + unit: &Unit<R>, + index: DebugLocListsIndex<R::Offset>, + ) -> Result<LocationListsOffset<R::Offset>> { + self.locations + .get_offset(unit.encoding(), unit.loclists_base, index) + } + + /// Iterate over the `LocationListEntry`s starting at the given offset. + pub fn locations( + &self, + unit: &Unit<R>, + offset: LocationListsOffset<R::Offset>, + ) -> Result<LocListIter<R>> { + match self.file_type { + DwarfFileType::Main => self.locations.locations( + offset, + unit.encoding(), + unit.low_pc, + &self.debug_addr, + unit.addr_base, + ), + DwarfFileType::Dwo => self.locations.locations_dwo( + offset, + unit.encoding(), + unit.low_pc, + &self.debug_addr, + unit.addr_base, + ), + } + } + + /// Iterate over the raw `LocationListEntry`s starting at the given offset. + pub fn raw_locations( + &self, + unit: &Unit<R>, + offset: LocationListsOffset<R::Offset>, + ) -> Result<RawLocListIter<R>> { + match self.file_type { + DwarfFileType::Main => self.locations.raw_locations(offset, unit.encoding()), + DwarfFileType::Dwo => self.locations.raw_locations_dwo(offset, unit.encoding()), + } + } + + /// Try to return an attribute value as a location list offset. + /// + /// If the attribute value is one of: + /// + /// - a `DW_FORM_sec_offset` reference to the `.debug_loc` or `.debug_loclists` sections + /// - a `DW_FORM_loclistx` index into the `.debug_loclists` entries for the unit + /// + /// then return the location list offset of the location list. + /// Returns `None` for other forms. + pub fn attr_locations_offset( + &self, + unit: &Unit<R>, + attr: AttributeValue<R>, + ) -> Result<Option<LocationListsOffset<R::Offset>>> { + match attr { + AttributeValue::LocationListsRef(offset) => Ok(Some(offset)), + AttributeValue::DebugLocListsIndex(index) => { + self.locations_offset(unit, index).map(Some) + } + _ => Ok(None), + } + } + + /// Try to return an attribute value as a location list entry iterator. + /// + /// If the attribute value is one of: + /// + /// - a `DW_FORM_sec_offset` reference to the `.debug_loc` or `.debug_loclists` sections + /// - a `DW_FORM_loclistx` index into the `.debug_loclists` entries for the unit + /// + /// then return an iterator over the entries in the location list. + /// Returns `None` for other forms. + pub fn attr_locations( + &self, + unit: &Unit<R>, + attr: AttributeValue<R>, + ) -> Result<Option<LocListIter<R>>> { + match self.attr_locations_offset(unit, attr)? { + Some(offset) => Ok(Some(self.locations(unit, offset)?)), + None => Ok(None), + } + } + + /// Call `Reader::lookup_offset_id` for each section, and return the first match. + /// + /// The first element of the tuple is `true` for supplementary sections. + pub fn lookup_offset_id(&self, id: ReaderOffsetId) -> Option<(bool, SectionId, R::Offset)> { + None.or_else(|| self.debug_abbrev.lookup_offset_id(id)) + .or_else(|| self.debug_addr.lookup_offset_id(id)) + .or_else(|| self.debug_aranges.lookup_offset_id(id)) + .or_else(|| self.debug_info.lookup_offset_id(id)) + .or_else(|| self.debug_line.lookup_offset_id(id)) + .or_else(|| self.debug_line_str.lookup_offset_id(id)) + .or_else(|| self.debug_str.lookup_offset_id(id)) + .or_else(|| self.debug_str_offsets.lookup_offset_id(id)) + .or_else(|| self.debug_types.lookup_offset_id(id)) + .or_else(|| self.locations.lookup_offset_id(id)) + .or_else(|| self.ranges.lookup_offset_id(id)) + .map(|(id, offset)| (false, id, offset)) + .or_else(|| { + self.sup() + .and_then(|sup| sup.lookup_offset_id(id)) + .map(|(_, id, offset)| (true, id, offset)) + }) + } + + /// Returns a string representation of the given error. + /// + /// This uses information from the DWARF sections to provide more information in some cases. + pub fn format_error(&self, err: Error) -> String { + #[allow(clippy::single_match)] + match err { + Error::UnexpectedEof(id) => match self.lookup_offset_id(id) { + Some((sup, section, offset)) => { + return format!( + "{} at {}{}+0x{:x}", + err, + section.name(), + if sup { "(sup)" } else { "" }, + offset.into_u64(), + ); + } + None => {} + }, + _ => {} + } + err.description().into() + } +} + +impl<R: Clone> Dwarf<R> { + /// Assuming `self` was loaded from a .dwo, take the appropriate + /// sections from `parent` (which contains the skeleton unit for this + /// dwo) such as `.debug_addr` and merge them into this `Dwarf`. + pub fn make_dwo(&mut self, parent: &Dwarf<R>) { + self.file_type = DwarfFileType::Dwo; + // These sections are always taken from the parent file and not the dwo. + self.debug_addr = parent.debug_addr.clone(); + // .debug_rnglists comes from the DWO, .debug_ranges comes from the + // parent file. + self.ranges + .set_debug_ranges(parent.ranges.debug_ranges().clone()); + self.sup = parent.sup.clone(); + } +} + +/// The sections from a `.dwp` file. +#[derive(Debug)] +pub struct DwarfPackage<R: Reader> { + /// The compilation unit index in the `.debug_cu_index` section. + pub cu_index: UnitIndex<R>, + + /// The type unit index in the `.debug_tu_index` section. + pub tu_index: UnitIndex<R>, + + /// The `.debug_abbrev.dwo` section. + pub debug_abbrev: DebugAbbrev<R>, + + /// The `.debug_info.dwo` section. + pub debug_info: DebugInfo<R>, + + /// The `.debug_line.dwo` section. + pub debug_line: DebugLine<R>, + + /// The `.debug_str.dwo` section. + pub debug_str: DebugStr<R>, + + /// The `.debug_str_offsets.dwo` section. + pub debug_str_offsets: DebugStrOffsets<R>, + + /// The `.debug_loc.dwo` section. + /// + /// Only present when using GNU split-dwarf extension to DWARF 4. + pub debug_loc: DebugLoc<R>, + + /// The `.debug_loclists.dwo` section. + pub debug_loclists: DebugLocLists<R>, + + /// The `.debug_rnglists.dwo` section. + pub debug_rnglists: DebugRngLists<R>, + + /// The `.debug_types.dwo` section. + /// + /// Only present when using GNU split-dwarf extension to DWARF 4. + pub debug_types: DebugTypes<R>, + + /// An empty section. + /// + /// Used when creating `Dwarf<R>`. + pub empty: R, +} + +impl<R: Reader> DwarfPackage<R> { + /// Try to load the `.dwp` sections using the given loader function. + /// + /// `section` loads a DWARF section from the object file. + /// It should return an empty section if the section does not exist. + pub fn load<F, E>(mut section: F, empty: R) -> core::result::Result<Self, E> + where + F: FnMut(SectionId) -> core::result::Result<R, E>, + E: From<Error>, + { + Ok(DwarfPackage { + cu_index: DebugCuIndex::load(&mut section)?.index()?, + tu_index: DebugTuIndex::load(&mut section)?.index()?, + // Section types are inferred. + debug_abbrev: Section::load(&mut section)?, + debug_info: Section::load(&mut section)?, + debug_line: Section::load(&mut section)?, + debug_str: Section::load(&mut section)?, + debug_str_offsets: Section::load(&mut section)?, + debug_loc: Section::load(&mut section)?, + debug_loclists: Section::load(&mut section)?, + debug_rnglists: Section::load(&mut section)?, + debug_types: Section::load(&mut section)?, + empty, + }) + } + + /// Find the compilation unit with the given DWO identifier and return its section + /// contributions. + pub fn find_cu(&self, id: DwoId, parent: &Dwarf<R>) -> Result<Option<Dwarf<R>>> { + let row = match self.cu_index.find(id.0) { + Some(row) => row, + None => return Ok(None), + }; + self.cu_sections(row, parent).map(Some) + } + + /// Find the type unit with the given type signature and return its section + /// contributions. + pub fn find_tu( + &self, + signature: DebugTypeSignature, + parent: &Dwarf<R>, + ) -> Result<Option<Dwarf<R>>> { + let row = match self.tu_index.find(signature.0) { + Some(row) => row, + None => return Ok(None), + }; + self.tu_sections(row, parent).map(Some) + } + + /// Return the section contributions of the compilation unit at the given index. + /// + /// The index must be in the range `1..cu_index.unit_count`. + /// + /// This function should only be needed by low level parsers. + pub fn cu_sections(&self, index: u32, parent: &Dwarf<R>) -> Result<Dwarf<R>> { + self.sections(self.cu_index.sections(index)?, parent) + } + + /// Return the section contributions of the compilation unit at the given index. + /// + /// The index must be in the range `1..tu_index.unit_count`. + /// + /// This function should only be needed by low level parsers. + pub fn tu_sections(&self, index: u32, parent: &Dwarf<R>) -> Result<Dwarf<R>> { + self.sections(self.tu_index.sections(index)?, parent) + } + + /// Return the section contributions of a unit. + /// + /// This function should only be needed by low level parsers. + pub fn sections( + &self, + sections: UnitIndexSectionIterator<R>, + parent: &Dwarf<R>, + ) -> Result<Dwarf<R>> { + let mut abbrev_offset = 0; + let mut abbrev_size = 0; + let mut info_offset = 0; + let mut info_size = 0; + let mut line_offset = 0; + let mut line_size = 0; + let mut loc_offset = 0; + let mut loc_size = 0; + let mut loclists_offset = 0; + let mut loclists_size = 0; + let mut str_offsets_offset = 0; + let mut str_offsets_size = 0; + let mut rnglists_offset = 0; + let mut rnglists_size = 0; + let mut types_offset = 0; + let mut types_size = 0; + for section in sections { + match section.section { + SectionId::DebugAbbrev => { + abbrev_offset = section.offset; + abbrev_size = section.size; + } + SectionId::DebugInfo => { + info_offset = section.offset; + info_size = section.size; + } + SectionId::DebugLine => { + line_offset = section.offset; + line_size = section.size; + } + SectionId::DebugLoc => { + loc_offset = section.offset; + loc_size = section.size; + } + SectionId::DebugLocLists => { + loclists_offset = section.offset; + loclists_size = section.size; + } + SectionId::DebugStrOffsets => { + str_offsets_offset = section.offset; + str_offsets_size = section.size; + } + SectionId::DebugRngLists => { + rnglists_offset = section.offset; + rnglists_size = section.size; + } + SectionId::DebugTypes => { + types_offset = section.offset; + types_size = section.size; + } + SectionId::DebugMacro | SectionId::DebugMacinfo => { + // These are valid but we can't parse these yet. + } + _ => return Err(Error::UnknownIndexSection), + } + } + + let debug_abbrev = self.debug_abbrev.dwp_range(abbrev_offset, abbrev_size)?; + let debug_info = self.debug_info.dwp_range(info_offset, info_size)?; + let debug_line = self.debug_line.dwp_range(line_offset, line_size)?; + let debug_loc = self.debug_loc.dwp_range(loc_offset, loc_size)?; + let debug_loclists = self + .debug_loclists + .dwp_range(loclists_offset, loclists_size)?; + let debug_str_offsets = self + .debug_str_offsets + .dwp_range(str_offsets_offset, str_offsets_size)?; + let debug_rnglists = self + .debug_rnglists + .dwp_range(rnglists_offset, rnglists_size)?; + let debug_types = self.debug_types.dwp_range(types_offset, types_size)?; + + let debug_str = self.debug_str.clone(); + + let debug_addr = parent.debug_addr.clone(); + let debug_ranges = parent.ranges.debug_ranges().clone(); + + let debug_aranges = self.empty.clone().into(); + let debug_line_str = self.empty.clone().into(); + + Ok(Dwarf { + debug_abbrev, + debug_addr, + debug_aranges, + debug_info, + debug_line, + debug_line_str, + debug_str, + debug_str_offsets, + debug_types, + locations: LocationLists::new(debug_loc, debug_loclists), + ranges: RangeLists::new(debug_ranges, debug_rnglists), + file_type: DwarfFileType::Dwo, + sup: parent.sup.clone(), + abbreviations_cache: AbbreviationsCache::new(), + }) + } +} + +/// All of the commonly used information for a unit in the `.debug_info` or `.debug_types` +/// sections. +#[derive(Debug)] +pub struct Unit<R, Offset = <R as Reader>::Offset> +where + R: Reader<Offset = Offset>, + Offset: ReaderOffset, +{ + /// The header of the unit. + pub header: UnitHeader<R, Offset>, + + /// The parsed abbreviations for the unit. + pub abbreviations: Arc<Abbreviations>, + + /// The `DW_AT_name` attribute of the unit. + pub name: Option<R>, + + /// The `DW_AT_comp_dir` attribute of the unit. + pub comp_dir: Option<R>, + + /// The `DW_AT_low_pc` attribute of the unit. Defaults to 0. + pub low_pc: u64, + + /// The `DW_AT_str_offsets_base` attribute of the unit. Defaults to 0. + pub str_offsets_base: DebugStrOffsetsBase<Offset>, + + /// The `DW_AT_addr_base` attribute of the unit. Defaults to 0. + pub addr_base: DebugAddrBase<Offset>, + + /// The `DW_AT_loclists_base` attribute of the unit. Defaults to 0. + pub loclists_base: DebugLocListsBase<Offset>, + + /// The `DW_AT_rnglists_base` attribute of the unit. Defaults to 0. + pub rnglists_base: DebugRngListsBase<Offset>, + + /// The line number program of the unit. + pub line_program: Option<IncompleteLineProgram<R, Offset>>, + + /// The DWO ID of a skeleton unit or split compilation unit. + pub dwo_id: Option<DwoId>, +} + +impl<R: Reader> Unit<R> { + /// Construct a new `Unit` from the given unit header. + #[inline] + pub fn new(dwarf: &Dwarf<R>, header: UnitHeader<R>) -> Result<Self> { + let abbreviations = dwarf.abbreviations(&header)?; + Self::new_with_abbreviations(dwarf, header, abbreviations) + } + + /// Construct a new `Unit` from the given unit header and abbreviations. + /// + /// The abbreviations for this call can be obtained using `dwarf.abbreviations(&header)`. + /// The caller may implement caching to reuse the `Abbreviations` across units with the + /// same `header.debug_abbrev_offset()` value. + #[inline] + pub fn new_with_abbreviations( + dwarf: &Dwarf<R>, + header: UnitHeader<R>, + abbreviations: Arc<Abbreviations>, + ) -> Result<Self> { + let mut unit = Unit { + abbreviations, + name: None, + comp_dir: None, + low_pc: 0, + str_offsets_base: DebugStrOffsetsBase::default_for_encoding_and_file( + header.encoding(), + dwarf.file_type, + ), + // NB: Because the .debug_addr section never lives in a .dwo, we can assume its base is always 0 or provided. + addr_base: DebugAddrBase(R::Offset::from_u8(0)), + loclists_base: DebugLocListsBase::default_for_encoding_and_file( + header.encoding(), + dwarf.file_type, + ), + rnglists_base: DebugRngListsBase::default_for_encoding_and_file( + header.encoding(), + dwarf.file_type, + ), + line_program: None, + dwo_id: match header.type_() { + UnitType::Skeleton(dwo_id) | UnitType::SplitCompilation(dwo_id) => Some(dwo_id), + _ => None, + }, + header, + }; + let mut name = None; + let mut comp_dir = None; + let mut line_program_offset = None; + let mut low_pc_attr = None; + + { + let mut cursor = unit.header.entries(&unit.abbreviations); + cursor.next_dfs()?; + let root = cursor.current().ok_or(Error::MissingUnitDie)?; + let mut attrs = root.attrs(); + while let Some(attr) = attrs.next()? { + match attr.name() { + constants::DW_AT_name => { + name = Some(attr.value()); + } + constants::DW_AT_comp_dir => { + comp_dir = Some(attr.value()); + } + constants::DW_AT_low_pc => { + low_pc_attr = Some(attr.value()); + } + constants::DW_AT_stmt_list => { + if let AttributeValue::DebugLineRef(offset) = attr.value() { + line_program_offset = Some(offset); + } + } + constants::DW_AT_str_offsets_base => { + if let AttributeValue::DebugStrOffsetsBase(base) = attr.value() { + unit.str_offsets_base = base; + } + } + constants::DW_AT_addr_base | constants::DW_AT_GNU_addr_base => { + if let AttributeValue::DebugAddrBase(base) = attr.value() { + unit.addr_base = base; + } + } + constants::DW_AT_loclists_base => { + if let AttributeValue::DebugLocListsBase(base) = attr.value() { + unit.loclists_base = base; + } + } + constants::DW_AT_rnglists_base | constants::DW_AT_GNU_ranges_base => { + if let AttributeValue::DebugRngListsBase(base) = attr.value() { + unit.rnglists_base = base; + } + } + constants::DW_AT_GNU_dwo_id => { + if unit.dwo_id.is_none() { + if let AttributeValue::DwoId(dwo_id) = attr.value() { + unit.dwo_id = Some(dwo_id); + } + } + } + _ => {} + } + } + } + + unit.name = match name { + Some(val) => dwarf.attr_string(&unit, val).ok(), + None => None, + }; + unit.comp_dir = match comp_dir { + Some(val) => dwarf.attr_string(&unit, val).ok(), + None => None, + }; + unit.line_program = match line_program_offset { + Some(offset) => Some(dwarf.debug_line.program( + offset, + unit.header.address_size(), + unit.comp_dir.clone(), + unit.name.clone(), + )?), + None => None, + }; + if let Some(low_pc_attr) = low_pc_attr { + if let Some(addr) = dwarf.attr_address(&unit, low_pc_attr)? { + unit.low_pc = addr; + } + } + Ok(unit) + } + + /// Return the encoding parameters for this unit. + #[inline] + pub fn encoding(&self) -> Encoding { + self.header.encoding() + } + + /// Read the `DebuggingInformationEntry` at the given offset. + pub fn entry(&self, offset: UnitOffset<R::Offset>) -> Result<DebuggingInformationEntry<R>> { + self.header.entry(&self.abbreviations, offset) + } + + /// Navigate this unit's `DebuggingInformationEntry`s. + #[inline] + pub fn entries(&self) -> EntriesCursor<R> { + self.header.entries(&self.abbreviations) + } + + /// Navigate this unit's `DebuggingInformationEntry`s + /// starting at the given offset. + #[inline] + pub fn entries_at_offset(&self, offset: UnitOffset<R::Offset>) -> Result<EntriesCursor<R>> { + self.header.entries_at_offset(&self.abbreviations, offset) + } + + /// Navigate this unit's `DebuggingInformationEntry`s as a tree + /// starting at the given offset. + #[inline] + pub fn entries_tree(&self, offset: Option<UnitOffset<R::Offset>>) -> Result<EntriesTree<R>> { + self.header.entries_tree(&self.abbreviations, offset) + } + + /// Read the raw data that defines the Debugging Information Entries. + #[inline] + pub fn entries_raw(&self, offset: Option<UnitOffset<R::Offset>>) -> Result<EntriesRaw<R>> { + self.header.entries_raw(&self.abbreviations, offset) + } + + /// Copy attributes that are subject to relocation from another unit. This is intended + /// to be used to copy attributes from a skeleton compilation unit to the corresponding + /// split compilation unit. + pub fn copy_relocated_attributes(&mut self, other: &Unit<R>) { + self.low_pc = other.low_pc; + self.addr_base = other.addr_base; + if self.header.version() < 5 { + self.rnglists_base = other.rnglists_base; + } + } + + /// Find the dwo name (if any) for this unit, automatically handling the differences + /// between the standardized DWARF 5 split DWARF format and the pre-DWARF 5 GNU + /// extension. + /// + /// The returned value is relative to this unit's `comp_dir`. + pub fn dwo_name(&self) -> Result<Option<AttributeValue<R>>> { + let mut entries = self.entries(); + if let None = entries.next_entry()? { + return Ok(None); + } + + let entry = entries.current().unwrap(); + if self.header.version() < 5 { + entry.attr_value(constants::DW_AT_GNU_dwo_name) + } else { + entry.attr_value(constants::DW_AT_dwo_name) + } + } +} + +impl<T: ReaderOffset> UnitSectionOffset<T> { + /// Convert an offset to be relative to the start of the given unit, + /// instead of relative to the start of the section. + /// Returns `None` if the offset is not within the unit entries. + pub fn to_unit_offset<R>(&self, unit: &Unit<R>) -> Option<UnitOffset<T>> + where + R: Reader<Offset = T>, + { + let (offset, unit_offset) = match (self, unit.header.offset()) { + ( + UnitSectionOffset::DebugInfoOffset(offset), + UnitSectionOffset::DebugInfoOffset(unit_offset), + ) => (offset.0, unit_offset.0), + ( + UnitSectionOffset::DebugTypesOffset(offset), + UnitSectionOffset::DebugTypesOffset(unit_offset), + ) => (offset.0, unit_offset.0), + _ => return None, + }; + let offset = match offset.checked_sub(unit_offset) { + Some(offset) => UnitOffset(offset), + None => return None, + }; + if !unit.header.is_valid_offset(offset) { + return None; + } + Some(offset) + } +} + +impl<T: ReaderOffset> UnitOffset<T> { + /// Convert an offset to be relative to the start of the .debug_info section, + /// instead of relative to the start of the given compilation unit. + /// + /// Does not check that the offset is valid. + pub fn to_unit_section_offset<R>(&self, unit: &Unit<R>) -> UnitSectionOffset<T> + where + R: Reader<Offset = T>, + { + match unit.header.offset() { + UnitSectionOffset::DebugInfoOffset(unit_offset) => { + DebugInfoOffset(unit_offset.0 + self.0).into() + } + UnitSectionOffset::DebugTypesOffset(unit_offset) => { + DebugTypesOffset(unit_offset.0 + self.0).into() + } + } + } +} + +/// An iterator for the address ranges of a `DebuggingInformationEntry`. +/// +/// Returned by `Dwarf::die_ranges` and `Dwarf::unit_ranges`. +#[derive(Debug)] +pub struct RangeIter<R: Reader>(RangeIterInner<R>); + +#[derive(Debug)] +enum RangeIterInner<R: Reader> { + Single(Option<Range>), + List(RngListIter<R>), +} + +impl<R: Reader> Default for RangeIter<R> { + fn default() -> Self { + RangeIter(RangeIterInner::Single(None)) + } +} + +impl<R: Reader> RangeIter<R> { + /// Advance the iterator to the next range. + pub fn next(&mut self) -> Result<Option<Range>> { + match self.0 { + RangeIterInner::Single(ref mut range) => Ok(range.take()), + RangeIterInner::List(ref mut list) => list.next(), + } + } +} + +#[cfg(feature = "fallible-iterator")] +impl<R: Reader> fallible_iterator::FallibleIterator for RangeIter<R> { + type Item = Range; + type Error = Error; + + #[inline] + fn next(&mut self) -> ::core::result::Result<Option<Self::Item>, Self::Error> { + RangeIter::next(self) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::read::EndianSlice; + use crate::{Endianity, LittleEndian}; + + /// Ensure that `Dwarf<R>` is covariant wrt R. + #[test] + fn test_dwarf_variance() { + /// This only needs to compile. + fn _f<'a: 'b, 'b, E: Endianity>(x: Dwarf<EndianSlice<'a, E>>) -> Dwarf<EndianSlice<'b, E>> { + x + } + } + + /// Ensure that `Unit<R>` is covariant wrt R. + #[test] + fn test_dwarf_unit_variance() { + /// This only needs to compile. + fn _f<'a: 'b, 'b, E: Endianity>(x: Unit<EndianSlice<'a, E>>) -> Unit<EndianSlice<'b, E>> { + x + } + } + + #[test] + fn test_send() { + fn assert_is_send<T: Send>() {} + assert_is_send::<Dwarf<EndianSlice<LittleEndian>>>(); + assert_is_send::<Unit<EndianSlice<LittleEndian>>>(); + } + + #[test] + fn test_format_error() { + let mut owned_dwarf = Dwarf::load(|_| -> Result<_> { Ok(vec![1, 2]) }).unwrap(); + owned_dwarf + .load_sup(|_| -> Result<_> { Ok(vec![1, 2]) }) + .unwrap(); + let dwarf = owned_dwarf.borrow(|section| EndianSlice::new(§ion, LittleEndian)); + + match dwarf.debug_str.get_str(DebugStrOffset(1)) { + Ok(r) => panic!("Unexpected str {:?}", r), + Err(e) => { + assert_eq!( + dwarf.format_error(e), + "Hit the end of input before it was expected at .debug_str+0x1" + ); + } + } + match dwarf.sup().unwrap().debug_str.get_str(DebugStrOffset(1)) { + Ok(r) => panic!("Unexpected str {:?}", r), + Err(e) => { + assert_eq!( + dwarf.format_error(e), + "Hit the end of input before it was expected at .debug_str(sup)+0x1" + ); + } + } + assert_eq!(dwarf.format_error(Error::Io), Error::Io.description()); + } +} diff --git a/vendor/gimli/src/read/endian_reader.rs b/vendor/gimli/src/read/endian_reader.rs new file mode 100644 index 0000000..8852b38 --- /dev/null +++ b/vendor/gimli/src/read/endian_reader.rs @@ -0,0 +1,639 @@ +//! Defining custom `Reader`s quickly. + +use alloc::borrow::Cow; +use alloc::rc::Rc; +use alloc::string::String; +use alloc::sync::Arc; +use core::fmt::Debug; +use core::ops::{Deref, Index, Range, RangeFrom, RangeTo}; +use core::slice; +use core::str; +use stable_deref_trait::CloneStableDeref; + +use crate::endianity::Endianity; +use crate::read::{Error, Reader, ReaderOffsetId, Result}; + +/// A reference counted, non-thread-safe slice of bytes and associated +/// endianity. +/// +/// ``` +/// # #[cfg(feature = "std")] { +/// use std::rc::Rc; +/// +/// let buf = Rc::from(&[1, 2, 3, 4][..]); +/// let reader = gimli::EndianRcSlice::new(buf, gimli::NativeEndian); +/// # let _ = reader; +/// # } +/// ``` +pub type EndianRcSlice<Endian> = EndianReader<Endian, Rc<[u8]>>; + +/// An atomically reference counted, thread-safe slice of bytes and associated +/// endianity. +/// +/// ``` +/// # #[cfg(feature = "std")] { +/// use std::sync::Arc; +/// +/// let buf = Arc::from(&[1, 2, 3, 4][..]); +/// let reader = gimli::EndianArcSlice::new(buf, gimli::NativeEndian); +/// # let _ = reader; +/// # } +/// ``` +pub type EndianArcSlice<Endian> = EndianReader<Endian, Arc<[u8]>>; + +/// An easy way to define a custom `Reader` implementation with a reference to a +/// generic buffer of bytes and an associated endianity. +/// +/// Note that the whole original buffer is kept alive in memory even if there is +/// only one reader that references only a handful of bytes from that original +/// buffer. That is, `EndianReader` will not do any copying, moving, or +/// compacting in order to free up unused regions of the original buffer. If you +/// require this kind of behavior, it is up to you to implement `Reader` +/// directly by-hand. +/// +/// # Example +/// +/// Say you have an `mmap`ed file that you want to serve as a `gimli::Reader`. +/// You can wrap that `mmap`ed file up in a `MmapFile` type and use +/// `EndianReader<Rc<MmapFile>>` or `EndianReader<Arc<MmapFile>>` as readers as +/// long as `MmapFile` dereferences to the underlying `[u8]` data. +/// +/// ``` +/// use std::io; +/// use std::ops::Deref; +/// use std::path::Path; +/// use std::slice; +/// use std::sync::Arc; +/// +/// /// A type that represents an `mmap`ed file. +/// #[derive(Debug)] +/// pub struct MmapFile { +/// ptr: *const u8, +/// len: usize, +/// } +/// +/// impl MmapFile { +/// pub fn new(path: &Path) -> io::Result<MmapFile> { +/// // Call `mmap` and check for errors and all that... +/// # unimplemented!() +/// } +/// } +/// +/// impl Drop for MmapFile { +/// fn drop(&mut self) { +/// // Call `munmap` to clean up after ourselves... +/// # unimplemented!() +/// } +/// } +/// +/// // And `MmapFile` can deref to a slice of the `mmap`ed region of memory. +/// impl Deref for MmapFile { +/// type Target = [u8]; +/// fn deref(&self) -> &[u8] { +/// unsafe { +/// slice::from_raw_parts(self.ptr, self.len) +/// } +/// } +/// } +/// +/// /// A type that represents a shared `mmap`ed file. +/// #[derive(Debug, Clone)] +/// pub struct ArcMmapFile(Arc<MmapFile>); +/// +/// // And `ArcMmapFile` can deref to a slice of the `mmap`ed region of memory. +/// impl Deref for ArcMmapFile { +/// type Target = [u8]; +/// fn deref(&self) -> &[u8] { +/// &self.0 +/// } +/// } +/// +/// // These are both valid for any `Rc` or `Arc`. +/// unsafe impl gimli::StableDeref for ArcMmapFile {} +/// unsafe impl gimli::CloneStableDeref for ArcMmapFile {} +/// +/// /// A `gimli::Reader` that is backed by an `mmap`ed file! +/// pub type MmapFileReader<Endian> = gimli::EndianReader<Endian, ArcMmapFile>; +/// # fn test(_: &MmapFileReader<gimli::NativeEndian>) { } +/// ``` +#[derive(Debug, Clone, Copy, Hash)] +pub struct EndianReader<Endian, T> +where + Endian: Endianity, + T: CloneStableDeref<Target = [u8]> + Debug, +{ + range: SubRange<T>, + endian: Endian, +} + +impl<Endian, T1, T2> PartialEq<EndianReader<Endian, T2>> for EndianReader<Endian, T1> +where + Endian: Endianity, + T1: CloneStableDeref<Target = [u8]> + Debug, + T2: CloneStableDeref<Target = [u8]> + Debug, +{ + fn eq(&self, rhs: &EndianReader<Endian, T2>) -> bool { + self.bytes() == rhs.bytes() + } +} + +impl<Endian, T> Eq for EndianReader<Endian, T> +where + Endian: Endianity, + T: CloneStableDeref<Target = [u8]> + Debug, +{ +} + +// This is separated out from `EndianReader` so that we can avoid running afoul +// of borrowck. We need to `read_slice(&mut self, ...) -> &[u8]` and then call +// `self.endian.read_whatever` on the result. The problem is that the returned +// slice keeps the `&mut self` borrow active, so we wouldn't be able to access +// `self.endian`. Splitting the sub-range out from the endian lets us work +// around this, making it so that only the `self.range` borrow is held active, +// not all of `self`. +// +// This also serves to encapsulate the unsafe code concerning `CloneStableDeref`. +// The `bytes` member is held so that the bytes live long enough, and the +// `CloneStableDeref` ensures these bytes never move. The `ptr` and `len` +// members point inside `bytes`, and are updated during read operations. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +struct SubRange<T> +where + T: CloneStableDeref<Target = [u8]> + Debug, +{ + bytes: T, + ptr: *const u8, + len: usize, +} + +unsafe impl<T> Send for SubRange<T> where T: CloneStableDeref<Target = [u8]> + Debug + Send {} + +unsafe impl<T> Sync for SubRange<T> where T: CloneStableDeref<Target = [u8]> + Debug + Sync {} + +impl<T> SubRange<T> +where + T: CloneStableDeref<Target = [u8]> + Debug, +{ + #[inline] + fn new(bytes: T) -> Self { + let ptr = bytes.as_ptr(); + let len = bytes.len(); + SubRange { bytes, ptr, len } + } + + #[inline] + fn bytes(&self) -> &[u8] { + // Safe because `T` implements `CloneStableDeref`, `bytes` can't be modified, + // and all operations that modify `ptr` and `len` ensure they stay in range. + unsafe { slice::from_raw_parts(self.ptr, self.len) } + } + + #[inline] + fn len(&self) -> usize { + self.len + } + + #[inline] + fn truncate(&mut self, len: usize) { + assert!(len <= self.len); + self.len = len; + } + + #[inline] + fn skip(&mut self, len: usize) { + assert!(len <= self.len); + self.ptr = unsafe { self.ptr.add(len) }; + self.len -= len; + } + + #[inline] + fn read_slice(&mut self, len: usize) -> Option<&[u8]> { + if self.len() < len { + None + } else { + // Same as for `bytes()`. + let bytes = unsafe { slice::from_raw_parts(self.ptr, len) }; + self.skip(len); + Some(bytes) + } + } +} + +impl<Endian, T> EndianReader<Endian, T> +where + Endian: Endianity, + T: CloneStableDeref<Target = [u8]> + Debug, +{ + /// Construct a new `EndianReader` with the given bytes. + #[inline] + pub fn new(bytes: T, endian: Endian) -> EndianReader<Endian, T> { + EndianReader { + range: SubRange::new(bytes), + endian, + } + } + + /// Return a reference to the raw bytes underlying this reader. + #[inline] + pub fn bytes(&self) -> &[u8] { + self.range.bytes() + } +} + +/// # Range Methods +/// +/// Unfortunately, `std::ops::Index` *must* return a reference, so we can't +/// implement `Index<Range<usize>>` to return a new `EndianReader` the way we +/// would like to. Instead, we abandon fancy indexing operators and have these +/// plain old methods. +impl<Endian, T> EndianReader<Endian, T> +where + Endian: Endianity, + T: CloneStableDeref<Target = [u8]> + Debug, +{ + /// Take the given `start..end` range of the underlying buffer and return a + /// new `EndianReader`. + /// + /// ``` + /// # #[cfg(feature = "std")] { + /// use gimli::{EndianReader, LittleEndian}; + /// use std::sync::Arc; + /// + /// let buf = Arc::<[u8]>::from(&[0x01, 0x02, 0x03, 0x04][..]); + /// let reader = EndianReader::new(buf.clone(), LittleEndian); + /// assert_eq!(reader.range(1..3), + /// EndianReader::new(&buf[1..3], LittleEndian)); + /// # } + /// ``` + /// + /// # Panics + /// + /// Panics if the range is out of bounds. + pub fn range(&self, idx: Range<usize>) -> EndianReader<Endian, T> { + let mut r = self.clone(); + r.range.skip(idx.start); + r.range.truncate(idx.len()); + r + } + + /// Take the given `start..` range of the underlying buffer and return a new + /// `EndianReader`. + /// + /// ``` + /// # #[cfg(feature = "std")] { + /// use gimli::{EndianReader, LittleEndian}; + /// use std::sync::Arc; + /// + /// let buf = Arc::<[u8]>::from(&[0x01, 0x02, 0x03, 0x04][..]); + /// let reader = EndianReader::new(buf.clone(), LittleEndian); + /// assert_eq!(reader.range_from(2..), + /// EndianReader::new(&buf[2..], LittleEndian)); + /// # } + /// ``` + /// + /// # Panics + /// + /// Panics if the range is out of bounds. + pub fn range_from(&self, idx: RangeFrom<usize>) -> EndianReader<Endian, T> { + let mut r = self.clone(); + r.range.skip(idx.start); + r + } + + /// Take the given `..end` range of the underlying buffer and return a new + /// `EndianReader`. + /// + /// ``` + /// # #[cfg(feature = "std")] { + /// use gimli::{EndianReader, LittleEndian}; + /// use std::sync::Arc; + /// + /// let buf = Arc::<[u8]>::from(&[0x01, 0x02, 0x03, 0x04][..]); + /// let reader = EndianReader::new(buf.clone(), LittleEndian); + /// assert_eq!(reader.range_to(..3), + /// EndianReader::new(&buf[..3], LittleEndian)); + /// # } + /// ``` + /// + /// # Panics + /// + /// Panics if the range is out of bounds. + pub fn range_to(&self, idx: RangeTo<usize>) -> EndianReader<Endian, T> { + let mut r = self.clone(); + r.range.truncate(idx.end); + r + } +} + +impl<Endian, T> Index<usize> for EndianReader<Endian, T> +where + Endian: Endianity, + T: CloneStableDeref<Target = [u8]> + Debug, +{ + type Output = u8; + fn index(&self, idx: usize) -> &Self::Output { + &self.bytes()[idx] + } +} + +impl<Endian, T> Index<RangeFrom<usize>> for EndianReader<Endian, T> +where + Endian: Endianity, + T: CloneStableDeref<Target = [u8]> + Debug, +{ + type Output = [u8]; + fn index(&self, idx: RangeFrom<usize>) -> &Self::Output { + &self.bytes()[idx] + } +} + +impl<Endian, T> Deref for EndianReader<Endian, T> +where + Endian: Endianity, + T: CloneStableDeref<Target = [u8]> + Debug, +{ + type Target = [u8]; + fn deref(&self) -> &Self::Target { + self.bytes() + } +} + +impl<Endian, T> Reader for EndianReader<Endian, T> +where + Endian: Endianity, + T: CloneStableDeref<Target = [u8]> + Debug, +{ + type Endian = Endian; + type Offset = usize; + + #[inline] + fn endian(&self) -> Endian { + self.endian + } + + #[inline] + fn len(&self) -> usize { + self.range.len() + } + + #[inline] + fn empty(&mut self) { + self.range.truncate(0); + } + + #[inline] + fn truncate(&mut self, len: usize) -> Result<()> { + if self.len() < len { + Err(Error::UnexpectedEof(self.offset_id())) + } else { + self.range.truncate(len); + Ok(()) + } + } + + #[inline] + fn offset_from(&self, base: &EndianReader<Endian, T>) -> usize { + let base_ptr = base.bytes().as_ptr() as *const u8 as usize; + let ptr = self.bytes().as_ptr() as *const u8 as usize; + debug_assert!(base_ptr <= ptr); + debug_assert!(ptr + self.bytes().len() <= base_ptr + base.bytes().len()); + ptr - base_ptr + } + + #[inline] + fn offset_id(&self) -> ReaderOffsetId { + ReaderOffsetId(self.bytes().as_ptr() as u64) + } + + #[inline] + fn lookup_offset_id(&self, id: ReaderOffsetId) -> Option<Self::Offset> { + let id = id.0; + let self_id = self.bytes().as_ptr() as u64; + let self_len = self.bytes().len() as u64; + if id >= self_id && id <= self_id + self_len { + Some((id - self_id) as usize) + } else { + None + } + } + + #[inline] + fn find(&self, byte: u8) -> Result<usize> { + self.bytes() + .iter() + .position(|x| *x == byte) + .ok_or_else(|| Error::UnexpectedEof(self.offset_id())) + } + + #[inline] + fn skip(&mut self, len: usize) -> Result<()> { + if self.len() < len { + Err(Error::UnexpectedEof(self.offset_id())) + } else { + self.range.skip(len); + Ok(()) + } + } + + #[inline] + fn split(&mut self, len: usize) -> Result<Self> { + if self.len() < len { + Err(Error::UnexpectedEof(self.offset_id())) + } else { + let mut r = self.clone(); + r.range.truncate(len); + self.range.skip(len); + Ok(r) + } + } + + #[inline] + fn to_slice(&self) -> Result<Cow<[u8]>> { + Ok(self.bytes().into()) + } + + #[inline] + fn to_string(&self) -> Result<Cow<str>> { + match str::from_utf8(self.bytes()) { + Ok(s) => Ok(s.into()), + _ => Err(Error::BadUtf8), + } + } + + #[inline] + fn to_string_lossy(&self) -> Result<Cow<str>> { + Ok(String::from_utf8_lossy(self.bytes())) + } + + #[inline] + fn read_slice(&mut self, buf: &mut [u8]) -> Result<()> { + match self.range.read_slice(buf.len()) { + Some(slice) => { + buf.copy_from_slice(slice); + Ok(()) + } + None => Err(Error::UnexpectedEof(self.offset_id())), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::endianity::NativeEndian; + use crate::read::Reader; + + fn native_reader<T: CloneStableDeref<Target = [u8]> + Debug>( + bytes: T, + ) -> EndianReader<NativeEndian, T> { + EndianReader::new(bytes, NativeEndian) + } + + const BUF: &[u8] = &[1, 2, 3, 4, 5, 6, 7, 8, 9, 0]; + + #[test] + fn test_reader_split() { + let mut reader = native_reader(BUF); + let left = reader.split(3).unwrap(); + assert_eq!(left, native_reader(&BUF[..3])); + assert_eq!(reader, native_reader(&BUF[3..])); + } + + #[test] + fn test_reader_split_out_of_bounds() { + let mut reader = native_reader(BUF); + assert!(reader.split(30).is_err()); + } + + #[test] + fn bytes_and_len_and_range_and_eq() { + let reader = native_reader(BUF); + assert_eq!(reader.len(), BUF.len()); + assert_eq!(reader.bytes(), BUF); + assert_eq!(reader, native_reader(BUF)); + + let range = reader.range(2..8); + let buf_range = &BUF[2..8]; + assert_eq!(range.len(), buf_range.len()); + assert_eq!(range.bytes(), buf_range); + assert_ne!(range, native_reader(BUF)); + assert_eq!(range, native_reader(buf_range)); + + let range_from = range.range_from(1..); + let buf_range_from = &buf_range[1..]; + assert_eq!(range_from.len(), buf_range_from.len()); + assert_eq!(range_from.bytes(), buf_range_from); + assert_ne!(range_from, native_reader(BUF)); + assert_eq!(range_from, native_reader(buf_range_from)); + + let range_to = range_from.range_to(..4); + let buf_range_to = &buf_range_from[..4]; + assert_eq!(range_to.len(), buf_range_to.len()); + assert_eq!(range_to.bytes(), buf_range_to); + assert_ne!(range_to, native_reader(BUF)); + assert_eq!(range_to, native_reader(buf_range_to)); + } + + #[test] + fn find() { + let mut reader = native_reader(BUF); + reader.skip(2).unwrap(); + assert_eq!( + reader.find(5), + Ok(BUF[2..].iter().position(|x| *x == 5).unwrap()) + ); + } + + #[test] + fn indexing() { + let mut reader = native_reader(BUF); + reader.skip(2).unwrap(); + assert_eq!(reader[0], BUF[2]); + } + + #[test] + #[should_panic] + fn indexing_out_of_bounds() { + let mut reader = native_reader(BUF); + reader.skip(2).unwrap(); + let _ = reader[900]; + } + + #[test] + fn endian() { + let reader = native_reader(BUF); + assert_eq!(reader.endian(), NativeEndian); + } + + #[test] + fn empty() { + let mut reader = native_reader(BUF); + assert!(!reader.is_empty()); + reader.empty(); + assert!(reader.is_empty()); + assert!(reader.bytes().is_empty()); + } + + #[test] + fn truncate() { + let reader = native_reader(BUF); + let mut reader = reader.range(2..8); + reader.truncate(2).unwrap(); + assert_eq!(reader.bytes(), &BUF[2..4]); + } + + #[test] + fn offset_from() { + let reader = native_reader(BUF); + let sub = reader.range(2..8); + assert_eq!(sub.offset_from(&reader), 2); + } + + #[test] + fn skip() { + let mut reader = native_reader(BUF); + reader.skip(2).unwrap(); + assert_eq!(reader.bytes(), &BUF[2..]); + } + + #[test] + fn to_slice() { + assert_eq!( + native_reader(BUF).range(2..5).to_slice(), + Ok(Cow::from(&BUF[2..5])) + ); + } + + #[test] + fn to_string_ok() { + let buf = b"hello, world!"; + let reader = native_reader(&buf[..]); + let reader = reader.range_from(7..); + assert_eq!(reader.to_string(), Ok(Cow::from("world!"))); + } + + // The rocket emoji (🚀 = [0xf0, 0x9f, 0x9a, 0x80]) but rotated left by one + // to make it invalid UTF-8. + const BAD_UTF8: &[u8] = &[0x9f, 0x9a, 0x80, 0xf0]; + + #[test] + fn to_string_err() { + let reader = native_reader(BAD_UTF8); + assert!(reader.to_string().is_err()); + } + + #[test] + fn to_string_lossy() { + let reader = native_reader(BAD_UTF8); + assert_eq!(reader.to_string_lossy(), Ok(Cow::from("����"))); + } + + #[test] + fn read_u8_array() { + let mut reader = native_reader(BAD_UTF8); + reader.skip(1).unwrap(); + let arr: [u8; 2] = reader.read_u8_array().unwrap(); + assert_eq!(arr, &BAD_UTF8[1..3]); + assert_eq!(reader.bytes(), &BAD_UTF8[3..]); + } +} diff --git a/vendor/gimli/src/read/endian_slice.rs b/vendor/gimli/src/read/endian_slice.rs new file mode 100644 index 0000000..0db28da --- /dev/null +++ b/vendor/gimli/src/read/endian_slice.rs @@ -0,0 +1,360 @@ +//! Working with byte slices that have an associated endianity. + +#[cfg(feature = "read")] +use alloc::borrow::Cow; +#[cfg(feature = "read")] +use alloc::string::String; +use core::fmt; +use core::ops::{Deref, Range, RangeFrom, RangeTo}; +use core::str; + +use crate::endianity::Endianity; +use crate::read::{Error, Reader, ReaderOffsetId, Result}; + +/// A `&[u8]` slice with endianity metadata. +/// +/// This implements the `Reader` trait, which is used for all reading of DWARF sections. +#[derive(Default, Clone, Copy, PartialEq, Eq, Hash)] +pub struct EndianSlice<'input, Endian> +where + Endian: Endianity, +{ + slice: &'input [u8], + endian: Endian, +} + +impl<'input, Endian> EndianSlice<'input, Endian> +where + Endian: Endianity, +{ + /// Construct a new `EndianSlice` with the given slice and endianity. + #[inline] + pub fn new(slice: &'input [u8], endian: Endian) -> EndianSlice<'input, Endian> { + EndianSlice { slice, endian } + } + + /// Return a reference to the raw slice. + #[inline] + #[doc(hidden)] + #[deprecated(note = "Method renamed to EndianSlice::slice; use that instead.")] + pub fn buf(&self) -> &'input [u8] { + self.slice + } + + /// Return a reference to the raw slice. + #[inline] + pub fn slice(&self) -> &'input [u8] { + self.slice + } + + /// Split the slice in two at the given index, resulting in the tuple where + /// the first item has range [0, idx), and the second has range [idx, + /// len). Panics if the index is out of bounds. + #[inline] + pub fn split_at( + &self, + idx: usize, + ) -> (EndianSlice<'input, Endian>, EndianSlice<'input, Endian>) { + (self.range_to(..idx), self.range_from(idx..)) + } + + /// Find the first occurrence of a byte in the slice, and return its index. + #[inline] + pub fn find(&self, byte: u8) -> Option<usize> { + self.slice.iter().position(|ch| *ch == byte) + } + + /// Return the offset of the start of the slice relative to the start + /// of the given slice. + #[inline] + pub fn offset_from(&self, base: EndianSlice<'input, Endian>) -> usize { + let base_ptr = base.slice.as_ptr() as *const u8 as usize; + let ptr = self.slice.as_ptr() as *const u8 as usize; + debug_assert!(base_ptr <= ptr); + debug_assert!(ptr + self.slice.len() <= base_ptr + base.slice.len()); + ptr - base_ptr + } + + /// Converts the slice to a string using `str::from_utf8`. + /// + /// Returns an error if the slice contains invalid characters. + #[inline] + pub fn to_string(&self) -> Result<&'input str> { + str::from_utf8(self.slice).map_err(|_| Error::BadUtf8) + } + + /// Converts the slice to a string, including invalid characters, + /// using `String::from_utf8_lossy`. + #[cfg(feature = "read")] + #[inline] + pub fn to_string_lossy(&self) -> Cow<'input, str> { + String::from_utf8_lossy(self.slice) + } + + #[inline] + fn read_slice(&mut self, len: usize) -> Result<&'input [u8]> { + if self.slice.len() < len { + Err(Error::UnexpectedEof(self.offset_id())) + } else { + let val = &self.slice[..len]; + self.slice = &self.slice[len..]; + Ok(val) + } + } +} + +/// # Range Methods +/// +/// Unfortunately, `std::ops::Index` *must* return a reference, so we can't +/// implement `Index<Range<usize>>` to return a new `EndianSlice` the way we would +/// like to. Instead, we abandon fancy indexing operators and have these plain +/// old methods. +impl<'input, Endian> EndianSlice<'input, Endian> +where + Endian: Endianity, +{ + /// Take the given `start..end` range of the underlying slice and return a + /// new `EndianSlice`. + /// + /// ``` + /// use gimli::{EndianSlice, LittleEndian}; + /// + /// let slice = &[0x01, 0x02, 0x03, 0x04]; + /// let endian_slice = EndianSlice::new(slice, LittleEndian); + /// assert_eq!(endian_slice.range(1..3), + /// EndianSlice::new(&slice[1..3], LittleEndian)); + /// ``` + pub fn range(&self, idx: Range<usize>) -> EndianSlice<'input, Endian> { + EndianSlice { + slice: &self.slice[idx], + endian: self.endian, + } + } + + /// Take the given `start..` range of the underlying slice and return a new + /// `EndianSlice`. + /// + /// ``` + /// use gimli::{EndianSlice, LittleEndian}; + /// + /// let slice = &[0x01, 0x02, 0x03, 0x04]; + /// let endian_slice = EndianSlice::new(slice, LittleEndian); + /// assert_eq!(endian_slice.range_from(2..), + /// EndianSlice::new(&slice[2..], LittleEndian)); + /// ``` + pub fn range_from(&self, idx: RangeFrom<usize>) -> EndianSlice<'input, Endian> { + EndianSlice { + slice: &self.slice[idx], + endian: self.endian, + } + } + + /// Take the given `..end` range of the underlying slice and return a new + /// `EndianSlice`. + /// + /// ``` + /// use gimli::{EndianSlice, LittleEndian}; + /// + /// let slice = &[0x01, 0x02, 0x03, 0x04]; + /// let endian_slice = EndianSlice::new(slice, LittleEndian); + /// assert_eq!(endian_slice.range_to(..3), + /// EndianSlice::new(&slice[..3], LittleEndian)); + /// ``` + pub fn range_to(&self, idx: RangeTo<usize>) -> EndianSlice<'input, Endian> { + EndianSlice { + slice: &self.slice[idx], + endian: self.endian, + } + } +} + +impl<'input, Endian> Deref for EndianSlice<'input, Endian> +where + Endian: Endianity, +{ + type Target = [u8]; + fn deref(&self) -> &Self::Target { + self.slice + } +} + +impl<'input, Endian: Endianity> fmt::Debug for EndianSlice<'input, Endian> { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> core::result::Result<(), fmt::Error> { + fmt.debug_tuple("EndianSlice") + .field(&self.endian) + .field(&DebugBytes(self.slice)) + .finish() + } +} + +struct DebugBytes<'input>(&'input [u8]); + +impl<'input> core::fmt::Debug for DebugBytes<'input> { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> core::result::Result<(), fmt::Error> { + let mut list = fmt.debug_list(); + list.entries(self.0.iter().take(8).copied().map(DebugByte)); + if self.0.len() > 8 { + list.entry(&DebugLen(self.0.len())); + } + list.finish() + } +} + +struct DebugByte(u8); + +impl fmt::Debug for DebugByte { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(fmt, "0x{:02x}", self.0) + } +} + +struct DebugLen(usize); + +impl fmt::Debug for DebugLen { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(fmt, "...; {}", self.0) + } +} + +impl<'input, Endian> Reader for EndianSlice<'input, Endian> +where + Endian: Endianity, +{ + type Endian = Endian; + type Offset = usize; + + #[inline] + fn endian(&self) -> Endian { + self.endian + } + + #[inline] + fn len(&self) -> usize { + self.slice.len() + } + + #[inline] + fn is_empty(&self) -> bool { + self.slice.is_empty() + } + + #[inline] + fn empty(&mut self) { + self.slice = &[]; + } + + #[inline] + fn truncate(&mut self, len: usize) -> Result<()> { + if self.slice.len() < len { + Err(Error::UnexpectedEof(self.offset_id())) + } else { + self.slice = &self.slice[..len]; + Ok(()) + } + } + + #[inline] + fn offset_from(&self, base: &Self) -> usize { + self.offset_from(*base) + } + + #[inline] + fn offset_id(&self) -> ReaderOffsetId { + ReaderOffsetId(self.slice.as_ptr() as u64) + } + + #[inline] + fn lookup_offset_id(&self, id: ReaderOffsetId) -> Option<Self::Offset> { + let id = id.0; + let self_id = self.slice.as_ptr() as u64; + let self_len = self.slice.len() as u64; + if id >= self_id && id <= self_id + self_len { + Some((id - self_id) as usize) + } else { + None + } + } + + #[inline] + fn find(&self, byte: u8) -> Result<usize> { + self.find(byte) + .ok_or_else(|| Error::UnexpectedEof(self.offset_id())) + } + + #[inline] + fn skip(&mut self, len: usize) -> Result<()> { + if self.slice.len() < len { + Err(Error::UnexpectedEof(self.offset_id())) + } else { + self.slice = &self.slice[len..]; + Ok(()) + } + } + + #[inline] + fn split(&mut self, len: usize) -> Result<Self> { + let slice = self.read_slice(len)?; + Ok(EndianSlice::new(slice, self.endian)) + } + + #[cfg(not(feature = "read"))] + fn cannot_implement() -> super::reader::seal_if_no_alloc::Sealed { + super::reader::seal_if_no_alloc::Sealed + } + + #[cfg(feature = "read")] + #[inline] + fn to_slice(&self) -> Result<Cow<[u8]>> { + Ok(self.slice.into()) + } + + #[cfg(feature = "read")] + #[inline] + fn to_string(&self) -> Result<Cow<str>> { + match str::from_utf8(self.slice) { + Ok(s) => Ok(s.into()), + _ => Err(Error::BadUtf8), + } + } + + #[cfg(feature = "read")] + #[inline] + fn to_string_lossy(&self) -> Result<Cow<str>> { + Ok(String::from_utf8_lossy(self.slice)) + } + + #[inline] + fn read_slice(&mut self, buf: &mut [u8]) -> Result<()> { + let slice = self.read_slice(buf.len())?; + buf.copy_from_slice(slice); + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::endianity::NativeEndian; + + #[test] + fn test_endian_slice_split_at() { + let endian = NativeEndian; + let slice = &[1, 2, 3, 4, 5, 6, 7, 8, 9, 0]; + let eb = EndianSlice::new(slice, endian); + assert_eq!( + eb.split_at(3), + ( + EndianSlice::new(&slice[..3], endian), + EndianSlice::new(&slice[3..], endian) + ) + ); + } + + #[test] + #[should_panic] + fn test_endian_slice_split_at_out_of_bounds() { + let slice = &[1, 2, 3, 4, 5, 6, 7, 8, 9, 0]; + let eb = EndianSlice::new(slice, NativeEndian); + eb.split_at(30); + } +} diff --git a/vendor/gimli/src/read/index.rs b/vendor/gimli/src/read/index.rs new file mode 100644 index 0000000..129eb2f --- /dev/null +++ b/vendor/gimli/src/read/index.rs @@ -0,0 +1,535 @@ +use core::slice; + +use crate::common::SectionId; +use crate::constants; +use crate::endianity::Endianity; +use crate::read::{EndianSlice, Error, Reader, ReaderOffset, Result, Section}; + +/// The data in the `.debug_cu_index` section of a `.dwp` file. +/// +/// This section contains the compilation unit index. +#[derive(Debug, Default, Clone, Copy)] +pub struct DebugCuIndex<R> { + section: R, +} + +impl<'input, Endian> DebugCuIndex<EndianSlice<'input, Endian>> +where + Endian: Endianity, +{ + /// Construct a new `DebugCuIndex` instance from the data in the `.debug_cu_index` + /// section. + pub fn new(section: &'input [u8], endian: Endian) -> Self { + Self::from(EndianSlice::new(section, endian)) + } +} + +impl<R> Section<R> for DebugCuIndex<R> { + fn id() -> SectionId { + SectionId::DebugCuIndex + } + + fn reader(&self) -> &R { + &self.section + } +} + +impl<R> From<R> for DebugCuIndex<R> { + fn from(section: R) -> Self { + DebugCuIndex { section } + } +} + +impl<R: Reader> DebugCuIndex<R> { + /// Parse the index header. + pub fn index(self) -> Result<UnitIndex<R>> { + UnitIndex::parse(self.section) + } +} + +/// The data in the `.debug_tu_index` section of a `.dwp` file. +/// +/// This section contains the type unit index. +#[derive(Debug, Default, Clone, Copy)] +pub struct DebugTuIndex<R> { + section: R, +} + +impl<'input, Endian> DebugTuIndex<EndianSlice<'input, Endian>> +where + Endian: Endianity, +{ + /// Construct a new `DebugTuIndex` instance from the data in the `.debug_tu_index` + /// section. + pub fn new(section: &'input [u8], endian: Endian) -> Self { + Self::from(EndianSlice::new(section, endian)) + } +} + +impl<R> Section<R> for DebugTuIndex<R> { + fn id() -> SectionId { + SectionId::DebugTuIndex + } + + fn reader(&self) -> &R { + &self.section + } +} + +impl<R> From<R> for DebugTuIndex<R> { + fn from(section: R) -> Self { + DebugTuIndex { section } + } +} + +impl<R: Reader> DebugTuIndex<R> { + /// Parse the index header. + pub fn index(self) -> Result<UnitIndex<R>> { + UnitIndex::parse(self.section) + } +} + +const SECTION_COUNT_MAX: u8 = 8; + +/// The partially parsed index from a `DebugCuIndex` or `DebugTuIndex`. +#[derive(Debug, Clone)] +pub struct UnitIndex<R: Reader> { + version: u16, + section_count: u32, + unit_count: u32, + slot_count: u32, + hash_ids: R, + hash_rows: R, + // Only `section_count` values are valid. + sections: [SectionId; SECTION_COUNT_MAX as usize], + offsets: R, + sizes: R, +} + +impl<R: Reader> UnitIndex<R> { + fn parse(mut input: R) -> Result<UnitIndex<R>> { + if input.is_empty() { + return Ok(UnitIndex { + version: 5, + section_count: 0, + unit_count: 0, + slot_count: 0, + hash_ids: input.clone(), + hash_rows: input.clone(), + sections: [SectionId::DebugAbbrev; SECTION_COUNT_MAX as usize], + offsets: input.clone(), + sizes: input.clone(), + }); + } + + // GNU split-dwarf extension to DWARF 4 uses a 32-bit version, + // but DWARF 5 uses a 16-bit version followed by 16-bit padding. + let mut original_input = input.clone(); + let version; + if input.read_u32()? == 2 { + version = 2 + } else { + version = original_input.read_u16()?; + if version != 5 { + return Err(Error::UnknownVersion(version.into())); + } + } + + let section_count = input.read_u32()?; + let unit_count = input.read_u32()?; + let slot_count = input.read_u32()?; + if slot_count == 0 || slot_count & (slot_count - 1) != 0 || slot_count <= unit_count { + return Err(Error::InvalidIndexSlotCount); + } + + let hash_ids = input.split(R::Offset::from_u64(u64::from(slot_count) * 8)?)?; + let hash_rows = input.split(R::Offset::from_u64(u64::from(slot_count) * 4)?)?; + + let mut sections = [SectionId::DebugAbbrev; SECTION_COUNT_MAX as usize]; + if section_count > SECTION_COUNT_MAX.into() { + return Err(Error::InvalidIndexSectionCount); + } + for i in 0..section_count { + let section = input.read_u32()?; + sections[i as usize] = if version == 2 { + match constants::DwSectV2(section) { + constants::DW_SECT_V2_INFO => SectionId::DebugInfo, + constants::DW_SECT_V2_TYPES => SectionId::DebugTypes, + constants::DW_SECT_V2_ABBREV => SectionId::DebugAbbrev, + constants::DW_SECT_V2_LINE => SectionId::DebugLine, + constants::DW_SECT_V2_LOC => SectionId::DebugLoc, + constants::DW_SECT_V2_STR_OFFSETS => SectionId::DebugStrOffsets, + constants::DW_SECT_V2_MACINFO => SectionId::DebugMacinfo, + constants::DW_SECT_V2_MACRO => SectionId::DebugMacro, + _ => return Err(Error::UnknownIndexSection), + } + } else { + match constants::DwSect(section) { + constants::DW_SECT_INFO => SectionId::DebugInfo, + constants::DW_SECT_ABBREV => SectionId::DebugAbbrev, + constants::DW_SECT_LINE => SectionId::DebugLine, + constants::DW_SECT_LOCLISTS => SectionId::DebugLocLists, + constants::DW_SECT_STR_OFFSETS => SectionId::DebugStrOffsets, + constants::DW_SECT_MACRO => SectionId::DebugMacro, + constants::DW_SECT_RNGLISTS => SectionId::DebugRngLists, + _ => return Err(Error::UnknownIndexSection), + } + }; + } + + let offsets = input.split(R::Offset::from_u64( + u64::from(unit_count) * u64::from(section_count) * 4, + )?)?; + let sizes = input.split(R::Offset::from_u64( + u64::from(unit_count) * u64::from(section_count) * 4, + )?)?; + + Ok(UnitIndex { + version, + section_count, + unit_count, + slot_count, + hash_ids, + hash_rows, + sections, + offsets, + sizes, + }) + } + + /// Find `id` in the index hash table, and return the row index. + /// + /// `id` may be a compilation unit ID if this index is from `.debug_cu_index`, + /// or a type signature if this index is from `.debug_tu_index`. + pub fn find(&self, id: u64) -> Option<u32> { + if self.slot_count == 0 { + return None; + } + let mask = u64::from(self.slot_count - 1); + let mut hash1 = id & mask; + let hash2 = ((id >> 32) & mask) | 1; + for _ in 0..self.slot_count { + // The length of these arrays was validated in `UnitIndex::parse`. + let mut hash_ids = self.hash_ids.clone(); + hash_ids.skip(R::Offset::from_u64(hash1 * 8).ok()?).ok()?; + let hash_id = hash_ids.read_u64().ok()?; + if hash_id == id { + let mut hash_rows = self.hash_rows.clone(); + hash_rows.skip(R::Offset::from_u64(hash1 * 4).ok()?).ok()?; + let hash_row = hash_rows.read_u32().ok()?; + return Some(hash_row); + } + if hash_id == 0 { + return None; + } + hash1 = (hash1 + hash2) & mask; + } + None + } + + /// Return the section offsets and sizes for the given row index. + pub fn sections(&self, mut row: u32) -> Result<UnitIndexSectionIterator<R>> { + if row == 0 { + return Err(Error::InvalidIndexRow); + } + row -= 1; + if row >= self.unit_count { + return Err(Error::InvalidIndexRow); + } + let mut offsets = self.offsets.clone(); + offsets.skip(R::Offset::from_u64( + u64::from(row) * u64::from(self.section_count) * 4, + )?)?; + let mut sizes = self.sizes.clone(); + sizes.skip(R::Offset::from_u64( + u64::from(row) * u64::from(self.section_count) * 4, + )?)?; + Ok(UnitIndexSectionIterator { + sections: self.sections[..self.section_count as usize].iter(), + offsets, + sizes, + }) + } + + /// Return the version. + pub fn version(&self) -> u16 { + self.version + } + + /// Return the number of sections. + pub fn section_count(&self) -> u32 { + self.section_count + } + + /// Return the number of units. + pub fn unit_count(&self) -> u32 { + self.unit_count + } + + /// Return the number of slots. + pub fn slot_count(&self) -> u32 { + self.slot_count + } +} + +/// An iterator over the section offsets and sizes for a row in a `UnitIndex`. +#[derive(Debug, Clone)] +pub struct UnitIndexSectionIterator<'index, R: Reader> { + sections: slice::Iter<'index, SectionId>, + offsets: R, + sizes: R, +} + +impl<'index, R: Reader> Iterator for UnitIndexSectionIterator<'index, R> { + type Item = UnitIndexSection; + + fn next(&mut self) -> Option<UnitIndexSection> { + let section = *self.sections.next()?; + // The length of these arrays was validated in `UnitIndex::parse`. + let offset = self.offsets.read_u32().ok()?; + let size = self.sizes.read_u32().ok()?; + Some(UnitIndexSection { + section, + offset, + size, + }) + } +} + +/// Information about a unit's contribution to a section in a `.dwp` file. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct UnitIndexSection { + /// The section kind. + pub section: SectionId, + /// The base offset of the unit's contribution to the section. + pub offset: u32, + /// The size of the unit's contribution to the section. + pub size: u32, +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::endianity::BigEndian; + use test_assembler::{Endian, Section}; + + #[test] + fn test_empty() { + let buf = EndianSlice::new(&[], BigEndian); + let index = UnitIndex::parse(buf).unwrap(); + assert!(index.find(0).is_none()); + } + + #[test] + fn test_version_2() { + #[rustfmt::skip] + let section = Section::with_endian(Endian::Big) + // Header. + .D32(2).D32(0).D32(0).D32(1) + // Slots. + .D64(0).D32(0); + let buf = section.get_contents().unwrap(); + let buf = EndianSlice::new(&buf, BigEndian); + let index = UnitIndex::parse(buf).unwrap(); + assert_eq!(index.version, 2); + } + + #[test] + fn test_version_5() { + #[rustfmt::skip] + let section = Section::with_endian(Endian::Big) + // Header. + .D16(5).D16(0).D32(0).D32(0).D32(1) + // Slots. + .D64(0).D32(0); + let buf = section.get_contents().unwrap(); + let buf = EndianSlice::new(&buf, BigEndian); + let index = UnitIndex::parse(buf).unwrap(); + assert_eq!(index.version, 5); + } + + #[test] + fn test_version_5_invalid() { + #[rustfmt::skip] + let section = Section::with_endian(Endian::Big) + // Header. + .D32(5).D32(0).D32(0).D32(1) + // Slots. + .D64(0).D32(0); + let buf = section.get_contents().unwrap(); + let buf = EndianSlice::new(&buf, BigEndian); + assert!(UnitIndex::parse(buf).is_err()); + } + + #[test] + fn test_version_2_sections() { + #[rustfmt::skip] + let section = Section::with_endian(Endian::Big) + // Header. + .D32(2).D32(8).D32(1).D32(2) + // Slots. + .D64(0).D64(0).D32(0).D32(0) + // Sections. + .D32(constants::DW_SECT_V2_INFO.0) + .D32(constants::DW_SECT_V2_TYPES.0) + .D32(constants::DW_SECT_V2_ABBREV.0) + .D32(constants::DW_SECT_V2_LINE.0) + .D32(constants::DW_SECT_V2_LOC.0) + .D32(constants::DW_SECT_V2_STR_OFFSETS.0) + .D32(constants::DW_SECT_V2_MACINFO.0) + .D32(constants::DW_SECT_V2_MACRO.0) + // Offsets. + .D32(11).D32(12).D32(13).D32(14).D32(15).D32(16).D32(17).D32(18) + // Sizes. + .D32(21).D32(22).D32(23).D32(24).D32(25).D32(26).D32(27).D32(28); + let buf = section.get_contents().unwrap(); + let buf = EndianSlice::new(&buf, BigEndian); + let index = UnitIndex::parse(buf).unwrap(); + assert_eq!(index.section_count, 8); + assert_eq!( + index.sections, + [ + SectionId::DebugInfo, + SectionId::DebugTypes, + SectionId::DebugAbbrev, + SectionId::DebugLine, + SectionId::DebugLoc, + SectionId::DebugStrOffsets, + SectionId::DebugMacinfo, + SectionId::DebugMacro, + ] + ); + #[rustfmt::skip] + let expect = [ + UnitIndexSection { section: SectionId::DebugInfo, offset: 11, size: 21 }, + UnitIndexSection { section: SectionId::DebugTypes, offset: 12, size: 22 }, + UnitIndexSection { section: SectionId::DebugAbbrev, offset: 13, size: 23 }, + UnitIndexSection { section: SectionId::DebugLine, offset: 14, size: 24 }, + UnitIndexSection { section: SectionId::DebugLoc, offset: 15, size: 25 }, + UnitIndexSection { section: SectionId::DebugStrOffsets, offset: 16, size: 26 }, + UnitIndexSection { section: SectionId::DebugMacinfo, offset: 17, size: 27 }, + UnitIndexSection { section: SectionId::DebugMacro, offset: 18, size: 28 }, + ]; + let mut sections = index.sections(1).unwrap(); + for section in &expect { + assert_eq!(*section, sections.next().unwrap()); + } + assert!(sections.next().is_none()); + } + + #[test] + fn test_version_5_sections() { + #[rustfmt::skip] + let section = Section::with_endian(Endian::Big) + // Header. + .D16(5).D16(0).D32(7).D32(1).D32(2) + // Slots. + .D64(0).D64(0).D32(0).D32(0) + // Sections. + .D32(constants::DW_SECT_INFO.0) + .D32(constants::DW_SECT_ABBREV.0) + .D32(constants::DW_SECT_LINE.0) + .D32(constants::DW_SECT_LOCLISTS.0) + .D32(constants::DW_SECT_STR_OFFSETS.0) + .D32(constants::DW_SECT_MACRO.0) + .D32(constants::DW_SECT_RNGLISTS.0) + // Offsets. + .D32(11).D32(12).D32(13).D32(14).D32(15).D32(16).D32(17) + // Sizes. + .D32(21).D32(22).D32(23).D32(24).D32(25).D32(26).D32(27); + let buf = section.get_contents().unwrap(); + let buf = EndianSlice::new(&buf, BigEndian); + let index = UnitIndex::parse(buf).unwrap(); + assert_eq!(index.section_count, 7); + assert_eq!( + index.sections[..7], + [ + SectionId::DebugInfo, + SectionId::DebugAbbrev, + SectionId::DebugLine, + SectionId::DebugLocLists, + SectionId::DebugStrOffsets, + SectionId::DebugMacro, + SectionId::DebugRngLists, + ] + ); + #[rustfmt::skip] + let expect = [ + UnitIndexSection { section: SectionId::DebugInfo, offset: 11, size: 21 }, + UnitIndexSection { section: SectionId::DebugAbbrev, offset: 12, size: 22 }, + UnitIndexSection { section: SectionId::DebugLine, offset: 13, size: 23 }, + UnitIndexSection { section: SectionId::DebugLocLists, offset: 14, size: 24 }, + UnitIndexSection { section: SectionId::DebugStrOffsets, offset: 15, size: 25 }, + UnitIndexSection { section: SectionId::DebugMacro, offset: 16, size: 26 }, + UnitIndexSection { section: SectionId::DebugRngLists, offset: 17, size: 27 }, + ]; + let mut sections = index.sections(1).unwrap(); + for section in &expect { + assert_eq!(*section, sections.next().unwrap()); + } + assert!(sections.next().is_none()); + + assert!(index.sections(0).is_err()); + assert!(index.sections(2).is_err()); + } + + #[test] + fn test_hash() { + #[rustfmt::skip] + let section = Section::with_endian(Endian::Big) + // Header. + .D16(5).D16(0).D32(2).D32(3).D32(4) + // Slots. + .D64(0xffff_fff2_ffff_fff1) + .D64(0xffff_fff0_ffff_fff1) + .D64(0xffff_fff1_ffff_fff1) + .D64(0) + .D32(3).D32(1).D32(2).D32(0) + // Sections. + .D32(constants::DW_SECT_INFO.0) + .D32(constants::DW_SECT_ABBREV.0) + // Offsets. + .D32(0).D32(0).D32(0).D32(0).D32(0).D32(0) + // Sizes. + .D32(0).D32(0).D32(0).D32(0).D32(0).D32(0); + let buf = section.get_contents().unwrap(); + let buf = EndianSlice::new(&buf, BigEndian); + let index = UnitIndex::parse(buf).unwrap(); + assert_eq!(index.version(), 5); + assert_eq!(index.slot_count(), 4); + assert_eq!(index.unit_count(), 3); + assert_eq!(index.section_count(), 2); + assert_eq!(index.find(0xffff_fff0_ffff_fff1), Some(1)); + assert_eq!(index.find(0xffff_fff1_ffff_fff1), Some(2)); + assert_eq!(index.find(0xffff_fff2_ffff_fff1), Some(3)); + assert_eq!(index.find(0xffff_fff3_ffff_fff1), None); + } + + #[test] + fn test_cu_index() { + #[rustfmt::skip] + let section = Section::with_endian(Endian::Big) + // Header. + .D16(5).D16(0).D32(0).D32(0).D32(1) + // Slots. + .D64(0).D32(0); + let buf = section.get_contents().unwrap(); + let cu_index = DebugCuIndex::new(&buf, BigEndian); + let index = cu_index.index().unwrap(); + assert_eq!(index.version, 5); + } + + #[test] + fn test_tu_index() { + #[rustfmt::skip] + let section = Section::with_endian(Endian::Big) + // Header. + .D16(5).D16(0).D32(0).D32(0).D32(1) + // Slots. + .D64(0).D32(0); + let buf = section.get_contents().unwrap(); + let tu_index = DebugTuIndex::new(&buf, BigEndian); + let index = tu_index.index().unwrap(); + assert_eq!(index.version, 5); + } +} diff --git a/vendor/gimli/src/read/line.rs b/vendor/gimli/src/read/line.rs new file mode 100644 index 0000000..47eae92 --- /dev/null +++ b/vendor/gimli/src/read/line.rs @@ -0,0 +1,3130 @@ +use alloc::vec::Vec; +use core::fmt; +use core::num::{NonZeroU64, Wrapping}; +use core::result; + +use crate::common::{ + DebugLineOffset, DebugLineStrOffset, DebugStrOffset, DebugStrOffsetsIndex, Encoding, Format, + LineEncoding, SectionId, +}; +use crate::constants; +use crate::endianity::Endianity; +use crate::read::{AttributeValue, EndianSlice, Error, Reader, ReaderOffset, Result, Section}; + +/// The `DebugLine` struct contains the source location to instruction mapping +/// found in the `.debug_line` section. +#[derive(Debug, Default, Clone, Copy)] +pub struct DebugLine<R> { + debug_line_section: R, +} + +impl<'input, Endian> DebugLine<EndianSlice<'input, Endian>> +where + Endian: Endianity, +{ + /// Construct a new `DebugLine` instance from the data in the `.debug_line` + /// section. + /// + /// It is the caller's responsibility to read the `.debug_line` section and + /// present it as a `&[u8]` slice. That means using some ELF loader on + /// Linux, a Mach-O loader on macOS, etc. + /// + /// ``` + /// use gimli::{DebugLine, LittleEndian}; + /// + /// # let buf = [0x00, 0x01, 0x02, 0x03]; + /// # let read_debug_line_section_somehow = || &buf; + /// let debug_line = DebugLine::new(read_debug_line_section_somehow(), LittleEndian); + /// ``` + pub fn new(debug_line_section: &'input [u8], endian: Endian) -> Self { + Self::from(EndianSlice::new(debug_line_section, endian)) + } +} + +impl<R: Reader> DebugLine<R> { + /// Parse the line number program whose header is at the given `offset` in the + /// `.debug_line` section. + /// + /// The `address_size` must match the compilation unit that the lines apply to. + /// The `comp_dir` should be from the `DW_AT_comp_dir` attribute of the compilation + /// unit. The `comp_name` should be from the `DW_AT_name` attribute of the + /// compilation unit. + /// + /// ```rust,no_run + /// use gimli::{DebugLine, DebugLineOffset, IncompleteLineProgram, EndianSlice, LittleEndian}; + /// + /// # let buf = []; + /// # let read_debug_line_section_somehow = || &buf; + /// let debug_line = DebugLine::new(read_debug_line_section_somehow(), LittleEndian); + /// + /// // In a real example, we'd grab the offset via a compilation unit + /// // entry's `DW_AT_stmt_list` attribute, and the address size from that + /// // unit directly. + /// let offset = DebugLineOffset(0); + /// let address_size = 8; + /// + /// let program = debug_line.program(offset, address_size, None, None) + /// .expect("should have found a header at that offset, and parsed it OK"); + /// ``` + pub fn program( + &self, + offset: DebugLineOffset<R::Offset>, + address_size: u8, + comp_dir: Option<R>, + comp_name: Option<R>, + ) -> Result<IncompleteLineProgram<R>> { + let input = &mut self.debug_line_section.clone(); + input.skip(offset.0)?; + let header = LineProgramHeader::parse(input, offset, address_size, comp_dir, comp_name)?; + let program = IncompleteLineProgram { header }; + Ok(program) + } +} + +impl<T> DebugLine<T> { + /// Create a `DebugLine` section that references the data in `self`. + /// + /// This is useful when `R` implements `Reader` but `T` does not. + /// + /// ## Example Usage + /// + /// ```rust,no_run + /// # let load_section = || unimplemented!(); + /// // Read the DWARF section into a `Vec` with whatever object loader you're using. + /// let owned_section: gimli::DebugLine<Vec<u8>> = load_section(); + /// // Create a reference to the DWARF section. + /// let section = owned_section.borrow(|section| { + /// gimli::EndianSlice::new(§ion, gimli::LittleEndian) + /// }); + /// ``` + pub fn borrow<'a, F, R>(&'a self, mut borrow: F) -> DebugLine<R> + where + F: FnMut(&'a T) -> R, + { + borrow(&self.debug_line_section).into() + } +} + +impl<R> Section<R> for DebugLine<R> { + fn id() -> SectionId { + SectionId::DebugLine + } + + fn reader(&self) -> &R { + &self.debug_line_section + } +} + +impl<R> From<R> for DebugLine<R> { + fn from(debug_line_section: R) -> Self { + DebugLine { debug_line_section } + } +} + +/// Deprecated. `LineNumberProgram` has been renamed to `LineProgram`. +#[deprecated(note = "LineNumberProgram has been renamed to LineProgram, use that instead.")] +pub type LineNumberProgram<R, Offset> = dyn LineProgram<R, Offset>; + +/// A `LineProgram` provides access to a `LineProgramHeader` and +/// a way to add files to the files table if necessary. Gimli consumers should +/// never need to use or see this trait. +pub trait LineProgram<R, Offset = <R as Reader>::Offset> +where + R: Reader<Offset = Offset>, + Offset: ReaderOffset, +{ + /// Get a reference to the held `LineProgramHeader`. + fn header(&self) -> &LineProgramHeader<R, Offset>; + /// Add a file to the file table if necessary. + fn add_file(&mut self, file: FileEntry<R, Offset>); +} + +impl<R, Offset> LineProgram<R, Offset> for IncompleteLineProgram<R, Offset> +where + R: Reader<Offset = Offset>, + Offset: ReaderOffset, +{ + fn header(&self) -> &LineProgramHeader<R, Offset> { + &self.header + } + fn add_file(&mut self, file: FileEntry<R, Offset>) { + self.header.file_names.push(file); + } +} + +impl<'program, R, Offset> LineProgram<R, Offset> for &'program CompleteLineProgram<R, Offset> +where + R: Reader<Offset = Offset>, + Offset: ReaderOffset, +{ + fn header(&self) -> &LineProgramHeader<R, Offset> { + &self.header + } + fn add_file(&mut self, _: FileEntry<R, Offset>) { + // Nop. Our file table is already complete. + } +} + +/// Deprecated. `StateMachine` has been renamed to `LineRows`. +#[deprecated(note = "StateMachine has been renamed to LineRows, use that instead.")] +pub type StateMachine<R, Program, Offset> = LineRows<R, Program, Offset>; + +/// Executes a `LineProgram` to iterate over the rows in the matrix of line number information. +/// +/// "The hypothetical machine used by a consumer of the line number information +/// to expand the byte-coded instruction stream into a matrix of line number +/// information." -- Section 6.2.1 +#[derive(Debug, Clone)] +pub struct LineRows<R, Program, Offset = <R as Reader>::Offset> +where + Program: LineProgram<R, Offset>, + R: Reader<Offset = Offset>, + Offset: ReaderOffset, +{ + program: Program, + row: LineRow, + instructions: LineInstructions<R>, +} + +type OneShotLineRows<R, Offset = <R as Reader>::Offset> = + LineRows<R, IncompleteLineProgram<R, Offset>, Offset>; + +type ResumedLineRows<'program, R, Offset = <R as Reader>::Offset> = + LineRows<R, &'program CompleteLineProgram<R, Offset>, Offset>; + +impl<R, Program, Offset> LineRows<R, Program, Offset> +where + Program: LineProgram<R, Offset>, + R: Reader<Offset = Offset>, + Offset: ReaderOffset, +{ + fn new(program: IncompleteLineProgram<R, Offset>) -> OneShotLineRows<R, Offset> { + let row = LineRow::new(program.header()); + let instructions = LineInstructions { + input: program.header().program_buf.clone(), + }; + LineRows { + program, + row, + instructions, + } + } + + fn resume<'program>( + program: &'program CompleteLineProgram<R, Offset>, + sequence: &LineSequence<R>, + ) -> ResumedLineRows<'program, R, Offset> { + let row = LineRow::new(program.header()); + let instructions = sequence.instructions.clone(); + LineRows { + program, + row, + instructions, + } + } + + /// Get a reference to the header for this state machine's line number + /// program. + #[inline] + pub fn header(&self) -> &LineProgramHeader<R, Offset> { + self.program.header() + } + + /// Parse and execute the next instructions in the line number program until + /// another row in the line number matrix is computed. + /// + /// The freshly computed row is returned as `Ok(Some((header, row)))`. + /// If the matrix is complete, and there are no more new rows in the line + /// number matrix, then `Ok(None)` is returned. If there was an error parsing + /// an instruction, then `Err(e)` is returned. + /// + /// Unfortunately, the references mean that this cannot be a + /// `FallibleIterator`. + pub fn next_row(&mut self) -> Result<Option<(&LineProgramHeader<R, Offset>, &LineRow)>> { + // Perform any reset that was required after copying the previous row. + self.row.reset(self.program.header()); + + loop { + // Split the borrow here, rather than calling `self.header()`. + match self.instructions.next_instruction(self.program.header()) { + Err(err) => return Err(err), + Ok(None) => return Ok(None), + Ok(Some(instruction)) => { + if self.row.execute(instruction, &mut self.program) { + if self.row.tombstone { + // Perform any reset that was required for the tombstone row. + // Normally this is done when `next_row` is called again, but for + // tombstones we loop immediately. + self.row.reset(self.program.header()); + } else { + return Ok(Some((self.header(), &self.row))); + } + } + // Fall through, parse the next instruction, and see if that + // yields a row. + } + } + } + } +} + +/// Deprecated. `Opcode` has been renamed to `LineInstruction`. +#[deprecated(note = "Opcode has been renamed to LineInstruction, use that instead.")] +pub type Opcode<R> = LineInstruction<R, <R as Reader>::Offset>; + +/// A parsed line number program instruction. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum LineInstruction<R, Offset = <R as Reader>::Offset> +where + R: Reader<Offset = Offset>, + Offset: ReaderOffset, +{ + /// > ### 6.2.5.1 Special Opcodes + /// > + /// > Each ubyte special opcode has the following effect on the state machine: + /// > + /// > 1. Add a signed integer to the line register. + /// > + /// > 2. Modify the operation pointer by incrementing the address and + /// > op_index registers as described below. + /// > + /// > 3. Append a row to the matrix using the current values of the state + /// > machine registers. + /// > + /// > 4. Set the basic_block register to “false.” + /// > + /// > 5. Set the prologue_end register to “false.” + /// > + /// > 6. Set the epilogue_begin register to “false.” + /// > + /// > 7. Set the discriminator register to 0. + /// > + /// > All of the special opcodes do those same seven things; they differ from + /// > one another only in what values they add to the line, address and + /// > op_index registers. + Special(u8), + + /// "[`LineInstruction::Copy`] appends a row to the matrix using the current + /// values of the state machine registers. Then it sets the discriminator + /// register to 0, and sets the basic_block, prologue_end and epilogue_begin + /// registers to “false.”" + Copy, + + /// "The DW_LNS_advance_pc opcode takes a single unsigned LEB128 operand as + /// the operation advance and modifies the address and op_index registers + /// [the same as `LineInstruction::Special`]" + AdvancePc(u64), + + /// "The DW_LNS_advance_line opcode takes a single signed LEB128 operand and + /// adds that value to the line register of the state machine." + AdvanceLine(i64), + + /// "The DW_LNS_set_file opcode takes a single unsigned LEB128 operand and + /// stores it in the file register of the state machine." + SetFile(u64), + + /// "The DW_LNS_set_column opcode takes a single unsigned LEB128 operand and + /// stores it in the column register of the state machine." + SetColumn(u64), + + /// "The DW_LNS_negate_stmt opcode takes no operands. It sets the is_stmt + /// register of the state machine to the logical negation of its current + /// value." + NegateStatement, + + /// "The DW_LNS_set_basic_block opcode takes no operands. It sets the + /// basic_block register of the state machine to “true.”" + SetBasicBlock, + + /// > The DW_LNS_const_add_pc opcode takes no operands. It advances the + /// > address and op_index registers by the increments corresponding to + /// > special opcode 255. + /// > + /// > When the line number program needs to advance the address by a small + /// > amount, it can use a single special opcode, which occupies a single + /// > byte. When it needs to advance the address by up to twice the range of + /// > the last special opcode, it can use DW_LNS_const_add_pc followed by a + /// > special opcode, for a total of two bytes. Only if it needs to advance + /// > the address by more than twice that range will it need to use both + /// > DW_LNS_advance_pc and a special opcode, requiring three or more bytes. + ConstAddPc, + + /// > The DW_LNS_fixed_advance_pc opcode takes a single uhalf (unencoded) + /// > operand and adds it to the address register of the state machine and + /// > sets the op_index register to 0. This is the only standard opcode whose + /// > operand is not a variable length number. It also does not multiply the + /// > operand by the minimum_instruction_length field of the header. + FixedAddPc(u16), + + /// "[`LineInstruction::SetPrologueEnd`] sets the prologue_end register to “true”." + SetPrologueEnd, + + /// "[`LineInstruction::SetEpilogueBegin`] sets the epilogue_begin register to + /// “true”." + SetEpilogueBegin, + + /// "The DW_LNS_set_isa opcode takes a single unsigned LEB128 operand and + /// stores that value in the isa register of the state machine." + SetIsa(u64), + + /// An unknown standard opcode with zero operands. + UnknownStandard0(constants::DwLns), + + /// An unknown standard opcode with one operand. + UnknownStandard1(constants::DwLns, u64), + + /// An unknown standard opcode with multiple operands. + UnknownStandardN(constants::DwLns, R), + + /// > [`LineInstruction::EndSequence`] sets the end_sequence register of the state + /// > machine to “true” and appends a row to the matrix using the current + /// > values of the state-machine registers. Then it resets the registers to + /// > the initial values specified above (see Section 6.2.2). Every line + /// > number program sequence must end with a DW_LNE_end_sequence instruction + /// > which creates a row whose address is that of the byte after the last + /// > target machine instruction of the sequence. + EndSequence, + + /// > The DW_LNE_set_address opcode takes a single relocatable address as an + /// > operand. The size of the operand is the size of an address on the target + /// > machine. It sets the address register to the value given by the + /// > relocatable address and sets the op_index register to 0. + /// > + /// > All of the other line number program opcodes that affect the address + /// > register add a delta to it. This instruction stores a relocatable value + /// > into it instead. + SetAddress(u64), + + /// Defines a new source file in the line number program and appends it to + /// the line number program header's list of source files. + DefineFile(FileEntry<R, Offset>), + + /// "The DW_LNE_set_discriminator opcode takes a single parameter, an + /// unsigned LEB128 integer. It sets the discriminator register to the new + /// value." + SetDiscriminator(u64), + + /// An unknown extended opcode and the slice of its unparsed operands. + UnknownExtended(constants::DwLne, R), +} + +impl<R, Offset> LineInstruction<R, Offset> +where + R: Reader<Offset = Offset>, + Offset: ReaderOffset, +{ + fn parse<'header>( + header: &'header LineProgramHeader<R>, + input: &mut R, + ) -> Result<LineInstruction<R>> + where + R: 'header, + { + let opcode = input.read_u8()?; + if opcode == 0 { + let length = input.read_uleb128().and_then(R::Offset::from_u64)?; + let mut instr_rest = input.split(length)?; + let opcode = instr_rest.read_u8()?; + + match constants::DwLne(opcode) { + constants::DW_LNE_end_sequence => Ok(LineInstruction::EndSequence), + + constants::DW_LNE_set_address => { + let address = instr_rest.read_address(header.address_size())?; + Ok(LineInstruction::SetAddress(address)) + } + + constants::DW_LNE_define_file => { + if header.version() <= 4 { + let path_name = instr_rest.read_null_terminated_slice()?; + let entry = FileEntry::parse(&mut instr_rest, path_name)?; + Ok(LineInstruction::DefineFile(entry)) + } else { + Ok(LineInstruction::UnknownExtended( + constants::DW_LNE_define_file, + instr_rest, + )) + } + } + + constants::DW_LNE_set_discriminator => { + let discriminator = instr_rest.read_uleb128()?; + Ok(LineInstruction::SetDiscriminator(discriminator)) + } + + otherwise => Ok(LineInstruction::UnknownExtended(otherwise, instr_rest)), + } + } else if opcode >= header.opcode_base { + Ok(LineInstruction::Special(opcode)) + } else { + match constants::DwLns(opcode) { + constants::DW_LNS_copy => Ok(LineInstruction::Copy), + + constants::DW_LNS_advance_pc => { + let advance = input.read_uleb128()?; + Ok(LineInstruction::AdvancePc(advance)) + } + + constants::DW_LNS_advance_line => { + let increment = input.read_sleb128()?; + Ok(LineInstruction::AdvanceLine(increment)) + } + + constants::DW_LNS_set_file => { + let file = input.read_uleb128()?; + Ok(LineInstruction::SetFile(file)) + } + + constants::DW_LNS_set_column => { + let column = input.read_uleb128()?; + Ok(LineInstruction::SetColumn(column)) + } + + constants::DW_LNS_negate_stmt => Ok(LineInstruction::NegateStatement), + + constants::DW_LNS_set_basic_block => Ok(LineInstruction::SetBasicBlock), + + constants::DW_LNS_const_add_pc => Ok(LineInstruction::ConstAddPc), + + constants::DW_LNS_fixed_advance_pc => { + let advance = input.read_u16()?; + Ok(LineInstruction::FixedAddPc(advance)) + } + + constants::DW_LNS_set_prologue_end => Ok(LineInstruction::SetPrologueEnd), + + constants::DW_LNS_set_epilogue_begin => Ok(LineInstruction::SetEpilogueBegin), + + constants::DW_LNS_set_isa => { + let isa = input.read_uleb128()?; + Ok(LineInstruction::SetIsa(isa)) + } + + otherwise => { + let mut opcode_lengths = header.standard_opcode_lengths().clone(); + opcode_lengths.skip(R::Offset::from_u8(opcode - 1))?; + let num_args = opcode_lengths.read_u8()? as usize; + match num_args { + 0 => Ok(LineInstruction::UnknownStandard0(otherwise)), + 1 => { + let arg = input.read_uleb128()?; + Ok(LineInstruction::UnknownStandard1(otherwise, arg)) + } + _ => { + let mut args = input.clone(); + for _ in 0..num_args { + input.read_uleb128()?; + } + let len = input.offset_from(&args); + args.truncate(len)?; + Ok(LineInstruction::UnknownStandardN(otherwise, args)) + } + } + } + } + } + } +} + +impl<R, Offset> fmt::Display for LineInstruction<R, Offset> +where + R: Reader<Offset = Offset>, + Offset: ReaderOffset, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> result::Result<(), fmt::Error> { + match *self { + LineInstruction::Special(opcode) => write!(f, "Special opcode {}", opcode), + LineInstruction::Copy => write!(f, "{}", constants::DW_LNS_copy), + LineInstruction::AdvancePc(advance) => { + write!(f, "{} by {}", constants::DW_LNS_advance_pc, advance) + } + LineInstruction::AdvanceLine(increment) => { + write!(f, "{} by {}", constants::DW_LNS_advance_line, increment) + } + LineInstruction::SetFile(file) => { + write!(f, "{} to {}", constants::DW_LNS_set_file, file) + } + LineInstruction::SetColumn(column) => { + write!(f, "{} to {}", constants::DW_LNS_set_column, column) + } + LineInstruction::NegateStatement => write!(f, "{}", constants::DW_LNS_negate_stmt), + LineInstruction::SetBasicBlock => write!(f, "{}", constants::DW_LNS_set_basic_block), + LineInstruction::ConstAddPc => write!(f, "{}", constants::DW_LNS_const_add_pc), + LineInstruction::FixedAddPc(advance) => { + write!(f, "{} by {}", constants::DW_LNS_fixed_advance_pc, advance) + } + LineInstruction::SetPrologueEnd => write!(f, "{}", constants::DW_LNS_set_prologue_end), + LineInstruction::SetEpilogueBegin => { + write!(f, "{}", constants::DW_LNS_set_epilogue_begin) + } + LineInstruction::SetIsa(isa) => write!(f, "{} to {}", constants::DW_LNS_set_isa, isa), + LineInstruction::UnknownStandard0(opcode) => write!(f, "Unknown {}", opcode), + LineInstruction::UnknownStandard1(opcode, arg) => { + write!(f, "Unknown {} with operand {}", opcode, arg) + } + LineInstruction::UnknownStandardN(opcode, ref args) => { + write!(f, "Unknown {} with operands {:?}", opcode, args) + } + LineInstruction::EndSequence => write!(f, "{}", constants::DW_LNE_end_sequence), + LineInstruction::SetAddress(address) => { + write!(f, "{} to {}", constants::DW_LNE_set_address, address) + } + LineInstruction::DefineFile(_) => write!(f, "{}", constants::DW_LNE_define_file), + LineInstruction::SetDiscriminator(discr) => { + write!(f, "{} to {}", constants::DW_LNE_set_discriminator, discr) + } + LineInstruction::UnknownExtended(opcode, _) => write!(f, "Unknown {}", opcode), + } + } +} + +/// Deprecated. `OpcodesIter` has been renamed to `LineInstructions`. +#[deprecated(note = "OpcodesIter has been renamed to LineInstructions, use that instead.")] +pub type OpcodesIter<R> = LineInstructions<R>; + +/// An iterator yielding parsed instructions. +/// +/// See +/// [`LineProgramHeader::instructions`](./struct.LineProgramHeader.html#method.instructions) +/// for more details. +#[derive(Clone, Debug)] +pub struct LineInstructions<R: Reader> { + input: R, +} + +impl<R: Reader> LineInstructions<R> { + fn remove_trailing(&self, other: &LineInstructions<R>) -> Result<LineInstructions<R>> { + let offset = other.input.offset_from(&self.input); + let mut input = self.input.clone(); + input.truncate(offset)?; + Ok(LineInstructions { input }) + } +} + +impl<R: Reader> LineInstructions<R> { + /// Advance the iterator and return the next instruction. + /// + /// Returns the newly parsed instruction as `Ok(Some(instruction))`. Returns + /// `Ok(None)` when iteration is complete and all instructions have already been + /// parsed and yielded. If an error occurs while parsing the next attribute, + /// then this error is returned as `Err(e)`, and all subsequent calls return + /// `Ok(None)`. + /// + /// Unfortunately, the `header` parameter means that this cannot be a + /// `FallibleIterator`. + #[inline(always)] + pub fn next_instruction( + &mut self, + header: &LineProgramHeader<R>, + ) -> Result<Option<LineInstruction<R>>> { + if self.input.is_empty() { + return Ok(None); + } + + match LineInstruction::parse(header, &mut self.input) { + Ok(instruction) => Ok(Some(instruction)), + Err(e) => { + self.input.empty(); + Err(e) + } + } + } +} + +/// Deprecated. `LineNumberRow` has been renamed to `LineRow`. +#[deprecated(note = "LineNumberRow has been renamed to LineRow, use that instead.")] +pub type LineNumberRow = LineRow; + +/// A row in the line number program's resulting matrix. +/// +/// Each row is a copy of the registers of the state machine, as defined in section 6.2.2. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct LineRow { + tombstone: bool, + address: Wrapping<u64>, + op_index: Wrapping<u64>, + file: u64, + line: Wrapping<u64>, + column: u64, + is_stmt: bool, + basic_block: bool, + end_sequence: bool, + prologue_end: bool, + epilogue_begin: bool, + isa: u64, + discriminator: u64, +} + +impl LineRow { + /// Create a line number row in the initial state for the given program. + pub fn new<R: Reader>(header: &LineProgramHeader<R>) -> Self { + LineRow { + // "At the beginning of each sequence within a line number program, the + // state of the registers is:" -- Section 6.2.2 + tombstone: false, + address: Wrapping(0), + op_index: Wrapping(0), + file: 1, + line: Wrapping(1), + column: 0, + // "determined by default_is_stmt in the line number program header" + is_stmt: header.line_encoding.default_is_stmt, + basic_block: false, + end_sequence: false, + prologue_end: false, + epilogue_begin: false, + // "The isa value 0 specifies that the instruction set is the + // architecturally determined default instruction set. This may be fixed + // by the ABI, or it may be specified by other means, for example, by + // the object file description." + isa: 0, + discriminator: 0, + } + } + + /// "The program-counter value corresponding to a machine instruction + /// generated by the compiler." + #[inline] + pub fn address(&self) -> u64 { + self.address.0 + } + + /// > An unsigned integer representing the index of an operation within a VLIW + /// > instruction. The index of the first operation is 0. For non-VLIW + /// > architectures, this register will always be 0. + /// > + /// > The address and op_index registers, taken together, form an operation + /// > pointer that can reference any individual operation with the + /// > instruction stream. + #[inline] + pub fn op_index(&self) -> u64 { + self.op_index.0 + } + + /// "An unsigned integer indicating the identity of the source file + /// corresponding to a machine instruction." + #[inline] + pub fn file_index(&self) -> u64 { + self.file + } + + /// The source file corresponding to the current machine instruction. + #[inline] + pub fn file<'header, R: Reader>( + &self, + header: &'header LineProgramHeader<R>, + ) -> Option<&'header FileEntry<R>> { + header.file(self.file) + } + + /// "An unsigned integer indicating a source line number. Lines are numbered + /// beginning at 1. The compiler may emit the value 0 in cases where an + /// instruction cannot be attributed to any source line." + /// Line number values of 0 are represented as `None`. + #[inline] + pub fn line(&self) -> Option<NonZeroU64> { + NonZeroU64::new(self.line.0) + } + + /// "An unsigned integer indicating a column number within a source + /// line. Columns are numbered beginning at 1. The value 0 is reserved to + /// indicate that a statement begins at the “left edge” of the line." + #[inline] + pub fn column(&self) -> ColumnType { + NonZeroU64::new(self.column) + .map(ColumnType::Column) + .unwrap_or(ColumnType::LeftEdge) + } + + /// "A boolean indicating that the current instruction is a recommended + /// breakpoint location. A recommended breakpoint location is intended to + /// “represent” a line, a statement and/or a semantically distinct subpart + /// of a statement." + #[inline] + pub fn is_stmt(&self) -> bool { + self.is_stmt + } + + /// "A boolean indicating that the current instruction is the beginning of a + /// basic block." + #[inline] + pub fn basic_block(&self) -> bool { + self.basic_block + } + + /// "A boolean indicating that the current address is that of the first byte + /// after the end of a sequence of target machine instructions. end_sequence + /// terminates a sequence of lines; therefore other information in the same + /// row is not meaningful." + #[inline] + pub fn end_sequence(&self) -> bool { + self.end_sequence + } + + /// "A boolean indicating that the current address is one (of possibly many) + /// where execution should be suspended for an entry breakpoint of a + /// function." + #[inline] + pub fn prologue_end(&self) -> bool { + self.prologue_end + } + + /// "A boolean indicating that the current address is one (of possibly many) + /// where execution should be suspended for an exit breakpoint of a + /// function." + #[inline] + pub fn epilogue_begin(&self) -> bool { + self.epilogue_begin + } + + /// Tag for the current instruction set architecture. + /// + /// > An unsigned integer whose value encodes the applicable instruction set + /// > architecture for the current instruction. + /// > + /// > The encoding of instruction sets should be shared by all users of a + /// > given architecture. It is recommended that this encoding be defined by + /// > the ABI authoring committee for each architecture. + #[inline] + pub fn isa(&self) -> u64 { + self.isa + } + + /// "An unsigned integer identifying the block to which the current + /// instruction belongs. Discriminator values are assigned arbitrarily by + /// the DWARF producer and serve to distinguish among multiple blocks that + /// may all be associated with the same source file, line, and column. Where + /// only one block exists for a given source position, the discriminator + /// value should be zero." + #[inline] + pub fn discriminator(&self) -> u64 { + self.discriminator + } + + /// Execute the given instruction, and return true if a new row in the + /// line number matrix needs to be generated. + /// + /// Unknown opcodes are treated as no-ops. + #[inline] + pub fn execute<R, Program>( + &mut self, + instruction: LineInstruction<R>, + program: &mut Program, + ) -> bool + where + Program: LineProgram<R>, + R: Reader, + { + match instruction { + LineInstruction::Special(opcode) => { + self.exec_special_opcode(opcode, program.header()); + true + } + + LineInstruction::Copy => true, + + LineInstruction::AdvancePc(operation_advance) => { + self.apply_operation_advance(operation_advance, program.header()); + false + } + + LineInstruction::AdvanceLine(line_increment) => { + self.apply_line_advance(line_increment); + false + } + + LineInstruction::SetFile(file) => { + self.file = file; + false + } + + LineInstruction::SetColumn(column) => { + self.column = column; + false + } + + LineInstruction::NegateStatement => { + self.is_stmt = !self.is_stmt; + false + } + + LineInstruction::SetBasicBlock => { + self.basic_block = true; + false + } + + LineInstruction::ConstAddPc => { + let adjusted = self.adjust_opcode(255, program.header()); + let operation_advance = adjusted / program.header().line_encoding.line_range; + self.apply_operation_advance(u64::from(operation_advance), program.header()); + false + } + + LineInstruction::FixedAddPc(operand) => { + self.address += Wrapping(u64::from(operand)); + self.op_index.0 = 0; + false + } + + LineInstruction::SetPrologueEnd => { + self.prologue_end = true; + false + } + + LineInstruction::SetEpilogueBegin => { + self.epilogue_begin = true; + false + } + + LineInstruction::SetIsa(isa) => { + self.isa = isa; + false + } + + LineInstruction::EndSequence => { + self.end_sequence = true; + true + } + + LineInstruction::SetAddress(address) => { + let tombstone_address = !0 >> (64 - program.header().encoding.address_size * 8); + self.tombstone = address == tombstone_address; + self.address.0 = address; + self.op_index.0 = 0; + false + } + + LineInstruction::DefineFile(entry) => { + program.add_file(entry); + false + } + + LineInstruction::SetDiscriminator(discriminator) => { + self.discriminator = discriminator; + false + } + + // Compatibility with future opcodes. + LineInstruction::UnknownStandard0(_) + | LineInstruction::UnknownStandard1(_, _) + | LineInstruction::UnknownStandardN(_, _) + | LineInstruction::UnknownExtended(_, _) => false, + } + } + + /// Perform any reset that was required after copying the previous row. + #[inline] + pub fn reset<R: Reader>(&mut self, header: &LineProgramHeader<R>) { + if self.end_sequence { + // Previous instruction was EndSequence, so reset everything + // as specified in Section 6.2.5.3. + *self = Self::new(header); + } else { + // Previous instruction was one of: + // - Special - specified in Section 6.2.5.1, steps 4-7 + // - Copy - specified in Section 6.2.5.2 + // The reset behaviour is the same in both cases. + self.discriminator = 0; + self.basic_block = false; + self.prologue_end = false; + self.epilogue_begin = false; + } + } + + /// Step 1 of section 6.2.5.1 + fn apply_line_advance(&mut self, line_increment: i64) { + if line_increment < 0 { + let decrement = -line_increment as u64; + if decrement <= self.line.0 { + self.line.0 -= decrement; + } else { + self.line.0 = 0; + } + } else { + self.line += Wrapping(line_increment as u64); + } + } + + /// Step 2 of section 6.2.5.1 + fn apply_operation_advance<R: Reader>( + &mut self, + operation_advance: u64, + header: &LineProgramHeader<R>, + ) { + let operation_advance = Wrapping(operation_advance); + + let minimum_instruction_length = u64::from(header.line_encoding.minimum_instruction_length); + let minimum_instruction_length = Wrapping(minimum_instruction_length); + + let maximum_operations_per_instruction = + u64::from(header.line_encoding.maximum_operations_per_instruction); + let maximum_operations_per_instruction = Wrapping(maximum_operations_per_instruction); + + if maximum_operations_per_instruction.0 == 1 { + self.address += minimum_instruction_length * operation_advance; + self.op_index.0 = 0; + } else { + let op_index_with_advance = self.op_index + operation_advance; + self.address += minimum_instruction_length + * (op_index_with_advance / maximum_operations_per_instruction); + self.op_index = op_index_with_advance % maximum_operations_per_instruction; + } + } + + #[inline] + fn adjust_opcode<R: Reader>(&self, opcode: u8, header: &LineProgramHeader<R>) -> u8 { + opcode - header.opcode_base + } + + /// Section 6.2.5.1 + fn exec_special_opcode<R: Reader>(&mut self, opcode: u8, header: &LineProgramHeader<R>) { + let adjusted_opcode = self.adjust_opcode(opcode, header); + + let line_range = header.line_encoding.line_range; + let line_advance = adjusted_opcode % line_range; + let operation_advance = adjusted_opcode / line_range; + + // Step 1 + let line_base = i64::from(header.line_encoding.line_base); + self.apply_line_advance(line_base + i64::from(line_advance)); + + // Step 2 + self.apply_operation_advance(u64::from(operation_advance), header); + } +} + +/// The type of column that a row is referring to. +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] +pub enum ColumnType { + /// The `LeftEdge` means that the statement begins at the start of the new + /// line. + LeftEdge, + /// A column number, whose range begins at 1. + Column(NonZeroU64), +} + +/// Deprecated. `LineNumberSequence` has been renamed to `LineSequence`. +#[deprecated(note = "LineNumberSequence has been renamed to LineSequence, use that instead.")] +pub type LineNumberSequence<R> = LineSequence<R>; + +/// A sequence within a line number program. A sequence, as defined in section +/// 6.2.5 of the standard, is a linear subset of a line number program within +/// which addresses are monotonically increasing. +#[derive(Clone, Debug)] +pub struct LineSequence<R: Reader> { + /// The first address that is covered by this sequence within the line number + /// program. + pub start: u64, + /// The first address that is *not* covered by this sequence within the line + /// number program. + pub end: u64, + instructions: LineInstructions<R>, +} + +/// Deprecated. `LineNumberProgramHeader` has been renamed to `LineProgramHeader`. +#[deprecated( + note = "LineNumberProgramHeader has been renamed to LineProgramHeader, use that instead." +)] +pub type LineNumberProgramHeader<R, Offset> = LineProgramHeader<R, Offset>; + +/// A header for a line number program in the `.debug_line` section, as defined +/// in section 6.2.4 of the standard. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct LineProgramHeader<R, Offset = <R as Reader>::Offset> +where + R: Reader<Offset = Offset>, + Offset: ReaderOffset, +{ + encoding: Encoding, + offset: DebugLineOffset<Offset>, + unit_length: Offset, + + header_length: Offset, + + line_encoding: LineEncoding, + + /// "The number assigned to the first special opcode." + opcode_base: u8, + + /// "This array specifies the number of LEB128 operands for each of the + /// standard opcodes. The first element of the array corresponds to the + /// opcode whose value is 1, and the last element corresponds to the opcode + /// whose value is `opcode_base - 1`." + standard_opcode_lengths: R, + + /// "A sequence of directory entry format descriptions." + directory_entry_format: Vec<FileEntryFormat>, + + /// > Entries in this sequence describe each path that was searched for + /// > included source files in this compilation. (The paths include those + /// > directories specified explicitly by the user for the compiler to search + /// > and those the compiler searches without explicit direction.) Each path + /// > entry is either a full path name or is relative to the current directory + /// > of the compilation. + /// > + /// > The last entry is followed by a single null byte. + include_directories: Vec<AttributeValue<R, Offset>>, + + /// "A sequence of file entry format descriptions." + file_name_entry_format: Vec<FileEntryFormat>, + + /// "Entries in this sequence describe source files that contribute to the + /// line number information for this compilation unit or is used in other + /// contexts." + file_names: Vec<FileEntry<R, Offset>>, + + /// The encoded line program instructions. + program_buf: R, + + /// The current directory of the compilation. + comp_dir: Option<R>, + + /// The primary source file. + comp_file: Option<FileEntry<R, Offset>>, +} + +impl<R, Offset> LineProgramHeader<R, Offset> +where + R: Reader<Offset = Offset>, + Offset: ReaderOffset, +{ + /// Return the offset of the line number program header in the `.debug_line` section. + pub fn offset(&self) -> DebugLineOffset<R::Offset> { + self.offset + } + + /// Return the length of the line number program and header, not including + /// the length of the encoded length itself. + pub fn unit_length(&self) -> R::Offset { + self.unit_length + } + + /// Return the encoding parameters for this header's line program. + pub fn encoding(&self) -> Encoding { + self.encoding + } + + /// Get the version of this header's line program. + pub fn version(&self) -> u16 { + self.encoding.version + } + + /// Get the length of the encoded line number program header, not including + /// the length of the encoded length itself. + pub fn header_length(&self) -> R::Offset { + self.header_length + } + + /// Get the size in bytes of a target machine address. + pub fn address_size(&self) -> u8 { + self.encoding.address_size + } + + /// Whether this line program is encoded in 64- or 32-bit DWARF. + pub fn format(&self) -> Format { + self.encoding.format + } + + /// Get the line encoding parameters for this header's line program. + pub fn line_encoding(&self) -> LineEncoding { + self.line_encoding + } + + /// Get the minimum instruction length any instruction in this header's line + /// program may have. + pub fn minimum_instruction_length(&self) -> u8 { + self.line_encoding.minimum_instruction_length + } + + /// Get the maximum number of operations each instruction in this header's + /// line program may have. + pub fn maximum_operations_per_instruction(&self) -> u8 { + self.line_encoding.maximum_operations_per_instruction + } + + /// Get the default value of the `is_stmt` register for this header's line + /// program. + pub fn default_is_stmt(&self) -> bool { + self.line_encoding.default_is_stmt + } + + /// Get the line base for this header's line program. + pub fn line_base(&self) -> i8 { + self.line_encoding.line_base + } + + /// Get the line range for this header's line program. + pub fn line_range(&self) -> u8 { + self.line_encoding.line_range + } + + /// Get opcode base for this header's line program. + pub fn opcode_base(&self) -> u8 { + self.opcode_base + } + + /// An array of `u8` that specifies the number of LEB128 operands for + /// each of the standard opcodes. + pub fn standard_opcode_lengths(&self) -> &R { + &self.standard_opcode_lengths + } + + /// Get the format of a directory entry. + pub fn directory_entry_format(&self) -> &[FileEntryFormat] { + &self.directory_entry_format[..] + } + + /// Get the set of include directories for this header's line program. + /// + /// For DWARF version <= 4, the compilation's current directory is not included + /// in the return value, but is implicitly considered to be in the set per spec. + pub fn include_directories(&self) -> &[AttributeValue<R, Offset>] { + &self.include_directories[..] + } + + /// The include directory with the given directory index. + /// + /// A directory index of 0 corresponds to the compilation unit directory. + pub fn directory(&self, directory: u64) -> Option<AttributeValue<R, Offset>> { + if self.encoding.version <= 4 { + if directory == 0 { + self.comp_dir.clone().map(AttributeValue::String) + } else { + let directory = directory as usize - 1; + self.include_directories.get(directory).cloned() + } + } else { + self.include_directories.get(directory as usize).cloned() + } + } + + /// Get the format of a file name entry. + pub fn file_name_entry_format(&self) -> &[FileEntryFormat] { + &self.file_name_entry_format[..] + } + + /// Return true if the file entries may have valid timestamps. + /// + /// Only returns false if we definitely know that all timestamp fields + /// are invalid. + pub fn file_has_timestamp(&self) -> bool { + self.encoding.version <= 4 + || self + .file_name_entry_format + .iter() + .any(|x| x.content_type == constants::DW_LNCT_timestamp) + } + + /// Return true if the file entries may have valid sizes. + /// + /// Only returns false if we definitely know that all size fields + /// are invalid. + pub fn file_has_size(&self) -> bool { + self.encoding.version <= 4 + || self + .file_name_entry_format + .iter() + .any(|x| x.content_type == constants::DW_LNCT_size) + } + + /// Return true if the file name entry format contains an MD5 field. + pub fn file_has_md5(&self) -> bool { + self.file_name_entry_format + .iter() + .any(|x| x.content_type == constants::DW_LNCT_MD5) + } + + /// Get the list of source files that appear in this header's line program. + pub fn file_names(&self) -> &[FileEntry<R, Offset>] { + &self.file_names[..] + } + + /// The source file with the given file index. + /// + /// A file index of 0 corresponds to the compilation unit file. + /// Note that a file index of 0 is invalid for DWARF version <= 4, + /// but we support it anyway. + pub fn file(&self, file: u64) -> Option<&FileEntry<R, Offset>> { + if self.encoding.version <= 4 { + if file == 0 { + self.comp_file.as_ref() + } else { + let file = file as usize - 1; + self.file_names.get(file) + } + } else { + self.file_names.get(file as usize) + } + } + + /// Get the raw, un-parsed `EndianSlice` containing this header's line number + /// program. + /// + /// ``` + /// # fn foo() { + /// use gimli::{LineProgramHeader, EndianSlice, NativeEndian}; + /// + /// fn get_line_number_program_header<'a>() -> LineProgramHeader<EndianSlice<'a, NativeEndian>> { + /// // Get a line number program header from some offset in a + /// // `.debug_line` section... + /// # unimplemented!() + /// } + /// + /// let header = get_line_number_program_header(); + /// let raw_program = header.raw_program_buf(); + /// println!("The length of the raw program in bytes is {}", raw_program.len()); + /// # } + /// ``` + pub fn raw_program_buf(&self) -> R { + self.program_buf.clone() + } + + /// Iterate over the instructions in this header's line number program, parsing + /// them as we go. + pub fn instructions(&self) -> LineInstructions<R> { + LineInstructions { + input: self.program_buf.clone(), + } + } + + fn parse( + input: &mut R, + offset: DebugLineOffset<Offset>, + mut address_size: u8, + mut comp_dir: Option<R>, + comp_name: Option<R>, + ) -> Result<LineProgramHeader<R, Offset>> { + let (unit_length, format) = input.read_initial_length()?; + let rest = &mut input.split(unit_length)?; + + let version = rest.read_u16()?; + if version < 2 || version > 5 { + return Err(Error::UnknownVersion(u64::from(version))); + } + + if version >= 5 { + address_size = rest.read_u8()?; + let segment_selector_size = rest.read_u8()?; + if segment_selector_size != 0 { + return Err(Error::UnsupportedSegmentSize); + } + } + + let encoding = Encoding { + format, + version, + address_size, + }; + + let header_length = rest.read_length(format)?; + + let mut program_buf = rest.clone(); + program_buf.skip(header_length)?; + rest.truncate(header_length)?; + + let minimum_instruction_length = rest.read_u8()?; + if minimum_instruction_length == 0 { + return Err(Error::MinimumInstructionLengthZero); + } + + // This field did not exist before DWARF 4, but is specified to be 1 for + // non-VLIW architectures, which makes it a no-op. + let maximum_operations_per_instruction = if version >= 4 { rest.read_u8()? } else { 1 }; + if maximum_operations_per_instruction == 0 { + return Err(Error::MaximumOperationsPerInstructionZero); + } + + let default_is_stmt = rest.read_u8()? != 0; + let line_base = rest.read_i8()?; + let line_range = rest.read_u8()?; + if line_range == 0 { + return Err(Error::LineRangeZero); + } + let line_encoding = LineEncoding { + minimum_instruction_length, + maximum_operations_per_instruction, + default_is_stmt, + line_base, + line_range, + }; + + let opcode_base = rest.read_u8()?; + if opcode_base == 0 { + return Err(Error::OpcodeBaseZero); + } + + let standard_opcode_count = R::Offset::from_u8(opcode_base - 1); + let standard_opcode_lengths = rest.split(standard_opcode_count)?; + + let directory_entry_format; + let mut include_directories = Vec::new(); + if version <= 4 { + directory_entry_format = Vec::new(); + loop { + let directory = rest.read_null_terminated_slice()?; + if directory.is_empty() { + break; + } + include_directories.push(AttributeValue::String(directory)); + } + } else { + comp_dir = None; + directory_entry_format = FileEntryFormat::parse(rest)?; + let count = rest.read_uleb128()?; + for _ in 0..count { + include_directories.push(parse_directory_v5( + rest, + encoding, + &directory_entry_format, + )?); + } + } + + let comp_file; + let file_name_entry_format; + let mut file_names = Vec::new(); + if version <= 4 { + comp_file = comp_name.map(|name| FileEntry { + path_name: AttributeValue::String(name), + directory_index: 0, + timestamp: 0, + size: 0, + md5: [0; 16], + }); + + file_name_entry_format = Vec::new(); + loop { + let path_name = rest.read_null_terminated_slice()?; + if path_name.is_empty() { + break; + } + file_names.push(FileEntry::parse(rest, path_name)?); + } + } else { + comp_file = None; + file_name_entry_format = FileEntryFormat::parse(rest)?; + let count = rest.read_uleb128()?; + for _ in 0..count { + file_names.push(parse_file_v5(rest, encoding, &file_name_entry_format)?); + } + } + + let header = LineProgramHeader { + encoding, + offset, + unit_length, + header_length, + line_encoding, + opcode_base, + standard_opcode_lengths, + directory_entry_format, + include_directories, + file_name_entry_format, + file_names, + program_buf, + comp_dir, + comp_file, + }; + Ok(header) + } +} + +/// Deprecated. `IncompleteLineNumberProgram` has been renamed to `IncompleteLineProgram`. +#[deprecated( + note = "IncompleteLineNumberProgram has been renamed to IncompleteLineProgram, use that instead." +)] +pub type IncompleteLineNumberProgram<R, Offset> = IncompleteLineProgram<R, Offset>; + +/// A line number program that has not been run to completion. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct IncompleteLineProgram<R, Offset = <R as Reader>::Offset> +where + R: Reader<Offset = Offset>, + Offset: ReaderOffset, +{ + header: LineProgramHeader<R, Offset>, +} + +impl<R, Offset> IncompleteLineProgram<R, Offset> +where + R: Reader<Offset = Offset>, + Offset: ReaderOffset, +{ + /// Retrieve the `LineProgramHeader` for this program. + pub fn header(&self) -> &LineProgramHeader<R, Offset> { + &self.header + } + + /// Construct a new `LineRows` for executing this program to iterate + /// over rows in the line information matrix. + pub fn rows(self) -> OneShotLineRows<R, Offset> { + OneShotLineRows::new(self) + } + + /// Execute the line number program, completing the `IncompleteLineProgram` + /// into a `CompleteLineProgram` and producing an array of sequences within + /// the line number program that can later be used with + /// `CompleteLineProgram::resume_from`. + /// + /// ``` + /// # fn foo() { + /// use gimli::{IncompleteLineProgram, EndianSlice, NativeEndian}; + /// + /// fn get_line_number_program<'a>() -> IncompleteLineProgram<EndianSlice<'a, NativeEndian>> { + /// // Get a line number program from some offset in a + /// // `.debug_line` section... + /// # unimplemented!() + /// } + /// + /// let program = get_line_number_program(); + /// let (program, sequences) = program.sequences().unwrap(); + /// println!("There are {} sequences in this line number program", sequences.len()); + /// # } + /// ``` + #[allow(clippy::type_complexity)] + pub fn sequences(self) -> Result<(CompleteLineProgram<R, Offset>, Vec<LineSequence<R>>)> { + let mut sequences = Vec::new(); + let mut rows = self.rows(); + let mut instructions = rows.instructions.clone(); + let mut sequence_start_addr = None; + loop { + let sequence_end_addr; + if rows.next_row()?.is_none() { + break; + } + + let row = &rows.row; + if row.end_sequence() { + sequence_end_addr = row.address(); + } else if sequence_start_addr.is_none() { + sequence_start_addr = Some(row.address()); + continue; + } else { + continue; + } + + // We just finished a sequence. + sequences.push(LineSequence { + // In theory one could have multiple DW_LNE_end_sequence instructions + // in a row. + start: sequence_start_addr.unwrap_or(0), + end: sequence_end_addr, + instructions: instructions.remove_trailing(&rows.instructions)?, + }); + sequence_start_addr = None; + instructions = rows.instructions.clone(); + } + + let program = CompleteLineProgram { + header: rows.program.header, + }; + Ok((program, sequences)) + } +} + +/// Deprecated. `CompleteLineNumberProgram` has been renamed to `CompleteLineProgram`. +#[deprecated( + note = "CompleteLineNumberProgram has been renamed to CompleteLineProgram, use that instead." +)] +pub type CompleteLineNumberProgram<R, Offset> = CompleteLineProgram<R, Offset>; + +/// A line number program that has previously been run to completion. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct CompleteLineProgram<R, Offset = <R as Reader>::Offset> +where + R: Reader<Offset = Offset>, + Offset: ReaderOffset, +{ + header: LineProgramHeader<R, Offset>, +} + +impl<R, Offset> CompleteLineProgram<R, Offset> +where + R: Reader<Offset = Offset>, + Offset: ReaderOffset, +{ + /// Retrieve the `LineProgramHeader` for this program. + pub fn header(&self) -> &LineProgramHeader<R, Offset> { + &self.header + } + + /// Construct a new `LineRows` for executing the subset of the line + /// number program identified by 'sequence' and generating the line information + /// matrix. + /// + /// ``` + /// # fn foo() { + /// use gimli::{IncompleteLineProgram, EndianSlice, NativeEndian}; + /// + /// fn get_line_number_program<'a>() -> IncompleteLineProgram<EndianSlice<'a, NativeEndian>> { + /// // Get a line number program from some offset in a + /// // `.debug_line` section... + /// # unimplemented!() + /// } + /// + /// let program = get_line_number_program(); + /// let (program, sequences) = program.sequences().unwrap(); + /// for sequence in &sequences { + /// let mut sm = program.resume_from(sequence); + /// } + /// # } + /// ``` + pub fn resume_from<'program>( + &'program self, + sequence: &LineSequence<R>, + ) -> ResumedLineRows<'program, R, Offset> { + ResumedLineRows::resume(self, sequence) + } +} + +/// An entry in the `LineProgramHeader`'s `file_names` set. +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub struct FileEntry<R, Offset = <R as Reader>::Offset> +where + R: Reader<Offset = Offset>, + Offset: ReaderOffset, +{ + path_name: AttributeValue<R, Offset>, + directory_index: u64, + timestamp: u64, + size: u64, + md5: [u8; 16], +} + +impl<R, Offset> FileEntry<R, Offset> +where + R: Reader<Offset = Offset>, + Offset: ReaderOffset, +{ + // version 2-4 + fn parse(input: &mut R, path_name: R) -> Result<FileEntry<R, Offset>> { + let directory_index = input.read_uleb128()?; + let timestamp = input.read_uleb128()?; + let size = input.read_uleb128()?; + + let entry = FileEntry { + path_name: AttributeValue::String(path_name), + directory_index, + timestamp, + size, + md5: [0; 16], + }; + + Ok(entry) + } + + /// > A slice containing the full or relative path name of + /// > a source file. If the entry contains a file name or a relative path + /// > name, the file is located relative to either the compilation directory + /// > (as specified by the DW_AT_comp_dir attribute given in the compilation + /// > unit) or one of the directories in the include_directories section. + pub fn path_name(&self) -> AttributeValue<R, Offset> { + self.path_name.clone() + } + + /// > An unsigned LEB128 number representing the directory index of the + /// > directory in which the file was found. + /// > + /// > ... + /// > + /// > The directory index represents an entry in the include_directories + /// > section of the line number program header. The index is 0 if the file + /// > was found in the current directory of the compilation, 1 if it was found + /// > in the first directory in the include_directories section, and so + /// > on. The directory index is ignored for file names that represent full + /// > path names. + pub fn directory_index(&self) -> u64 { + self.directory_index + } + + /// Get this file's directory. + /// + /// A directory index of 0 corresponds to the compilation unit directory. + pub fn directory(&self, header: &LineProgramHeader<R>) -> Option<AttributeValue<R, Offset>> { + header.directory(self.directory_index) + } + + /// The implementation-defined time of last modification of the file, + /// or 0 if not available. + pub fn timestamp(&self) -> u64 { + self.timestamp + } + + /// "An unsigned LEB128 number representing the time of last modification of + /// the file, or 0 if not available." + // Terminology changed in DWARF version 5. + #[doc(hidden)] + pub fn last_modification(&self) -> u64 { + self.timestamp + } + + /// The size of the file in bytes, or 0 if not available. + pub fn size(&self) -> u64 { + self.size + } + + /// "An unsigned LEB128 number representing the length in bytes of the file, + /// or 0 if not available." + // Terminology changed in DWARF version 5. + #[doc(hidden)] + pub fn length(&self) -> u64 { + self.size + } + + /// A 16-byte MD5 digest of the file contents. + /// + /// Only valid if `LineProgramHeader::file_has_md5` returns `true`. + pub fn md5(&self) -> &[u8; 16] { + &self.md5 + } +} + +/// The format of a component of an include directory or file name entry. +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub struct FileEntryFormat { + /// The type of information that is represented by the component. + pub content_type: constants::DwLnct, + + /// The encoding form of the component value. + pub form: constants::DwForm, +} + +impl FileEntryFormat { + fn parse<R: Reader>(input: &mut R) -> Result<Vec<FileEntryFormat>> { + let format_count = input.read_u8()? as usize; + let mut format = Vec::with_capacity(format_count); + let mut path_count = 0; + for _ in 0..format_count { + let content_type = input.read_uleb128()?; + let content_type = if content_type > u64::from(u16::max_value()) { + constants::DwLnct(u16::max_value()) + } else { + constants::DwLnct(content_type as u16) + }; + if content_type == constants::DW_LNCT_path { + path_count += 1; + } + + let form = constants::DwForm(input.read_uleb128_u16()?); + + format.push(FileEntryFormat { content_type, form }); + } + if path_count != 1 { + return Err(Error::MissingFileEntryFormatPath); + } + Ok(format) + } +} + +fn parse_directory_v5<R: Reader>( + input: &mut R, + encoding: Encoding, + formats: &[FileEntryFormat], +) -> Result<AttributeValue<R>> { + let mut path_name = None; + + for format in formats { + let value = parse_attribute(input, encoding, format.form)?; + if format.content_type == constants::DW_LNCT_path { + path_name = Some(value); + } + } + + Ok(path_name.unwrap()) +} + +fn parse_file_v5<R: Reader>( + input: &mut R, + encoding: Encoding, + formats: &[FileEntryFormat], +) -> Result<FileEntry<R>> { + let mut path_name = None; + let mut directory_index = 0; + let mut timestamp = 0; + let mut size = 0; + let mut md5 = [0; 16]; + + for format in formats { + let value = parse_attribute(input, encoding, format.form)?; + match format.content_type { + constants::DW_LNCT_path => path_name = Some(value), + constants::DW_LNCT_directory_index => { + if let Some(value) = value.udata_value() { + directory_index = value; + } + } + constants::DW_LNCT_timestamp => { + if let Some(value) = value.udata_value() { + timestamp = value; + } + } + constants::DW_LNCT_size => { + if let Some(value) = value.udata_value() { + size = value; + } + } + constants::DW_LNCT_MD5 => { + if let AttributeValue::Block(mut value) = value { + if value.len().into_u64() == 16 { + md5 = value.read_u8_array()?; + } + } + } + // Ignore unknown content types. + _ => {} + } + } + + Ok(FileEntry { + path_name: path_name.unwrap(), + directory_index, + timestamp, + size, + md5, + }) +} + +// TODO: this should be shared with unit::parse_attribute(), but that is hard to do. +fn parse_attribute<R: Reader>( + input: &mut R, + encoding: Encoding, + form: constants::DwForm, +) -> Result<AttributeValue<R>> { + Ok(match form { + constants::DW_FORM_block1 => { + let len = input.read_u8().map(R::Offset::from_u8)?; + let block = input.split(len)?; + AttributeValue::Block(block) + } + constants::DW_FORM_block2 => { + let len = input.read_u16().map(R::Offset::from_u16)?; + let block = input.split(len)?; + AttributeValue::Block(block) + } + constants::DW_FORM_block4 => { + let len = input.read_u32().map(R::Offset::from_u32)?; + let block = input.split(len)?; + AttributeValue::Block(block) + } + constants::DW_FORM_block => { + let len = input.read_uleb128().and_then(R::Offset::from_u64)?; + let block = input.split(len)?; + AttributeValue::Block(block) + } + constants::DW_FORM_data1 => { + let data = input.read_u8()?; + AttributeValue::Data1(data) + } + constants::DW_FORM_data2 => { + let data = input.read_u16()?; + AttributeValue::Data2(data) + } + constants::DW_FORM_data4 => { + let data = input.read_u32()?; + AttributeValue::Data4(data) + } + constants::DW_FORM_data8 => { + let data = input.read_u64()?; + AttributeValue::Data8(data) + } + constants::DW_FORM_data16 => { + let block = input.split(R::Offset::from_u8(16))?; + AttributeValue::Block(block) + } + constants::DW_FORM_udata => { + let data = input.read_uleb128()?; + AttributeValue::Udata(data) + } + constants::DW_FORM_sdata => { + let data = input.read_sleb128()?; + AttributeValue::Sdata(data) + } + constants::DW_FORM_flag => { + let present = input.read_u8()?; + AttributeValue::Flag(present != 0) + } + constants::DW_FORM_sec_offset => { + let offset = input.read_offset(encoding.format)?; + AttributeValue::SecOffset(offset) + } + constants::DW_FORM_string => { + let string = input.read_null_terminated_slice()?; + AttributeValue::String(string) + } + constants::DW_FORM_strp => { + let offset = input.read_offset(encoding.format)?; + AttributeValue::DebugStrRef(DebugStrOffset(offset)) + } + constants::DW_FORM_strp_sup | constants::DW_FORM_GNU_strp_alt => { + let offset = input.read_offset(encoding.format)?; + AttributeValue::DebugStrRefSup(DebugStrOffset(offset)) + } + constants::DW_FORM_line_strp => { + let offset = input.read_offset(encoding.format)?; + AttributeValue::DebugLineStrRef(DebugLineStrOffset(offset)) + } + constants::DW_FORM_strx | constants::DW_FORM_GNU_str_index => { + let index = input.read_uleb128().and_then(R::Offset::from_u64)?; + AttributeValue::DebugStrOffsetsIndex(DebugStrOffsetsIndex(index)) + } + constants::DW_FORM_strx1 => { + let index = input.read_u8().map(R::Offset::from_u8)?; + AttributeValue::DebugStrOffsetsIndex(DebugStrOffsetsIndex(index)) + } + constants::DW_FORM_strx2 => { + let index = input.read_u16().map(R::Offset::from_u16)?; + AttributeValue::DebugStrOffsetsIndex(DebugStrOffsetsIndex(index)) + } + constants::DW_FORM_strx3 => { + let index = input.read_uint(3).and_then(R::Offset::from_u64)?; + AttributeValue::DebugStrOffsetsIndex(DebugStrOffsetsIndex(index)) + } + constants::DW_FORM_strx4 => { + let index = input.read_u32().map(R::Offset::from_u32)?; + AttributeValue::DebugStrOffsetsIndex(DebugStrOffsetsIndex(index)) + } + _ => { + return Err(Error::UnknownForm); + } + }) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::constants; + use crate::endianity::LittleEndian; + use crate::read::{EndianSlice, Error}; + use crate::test_util::GimliSectionMethods; + use core::u64; + use core::u8; + use test_assembler::{Endian, Label, LabelMaker, Section}; + + #[test] + fn test_parse_debug_line_32_ok() { + #[rustfmt::skip] + let buf = [ + // 32-bit length = 62. + 0x3e, 0x00, 0x00, 0x00, + // Version. + 0x04, 0x00, + // Header length = 40. + 0x28, 0x00, 0x00, 0x00, + // Minimum instruction length. + 0x01, + // Maximum operations per byte. + 0x01, + // Default is_stmt. + 0x01, + // Line base. + 0x00, + // Line range. + 0x01, + // Opcode base. + 0x03, + // Standard opcode lengths for opcodes 1 .. opcode base - 1. + 0x01, 0x02, + // Include directories = '/', 'i', 'n', 'c', '\0', '/', 'i', 'n', 'c', '2', '\0', '\0' + 0x2f, 0x69, 0x6e, 0x63, 0x00, 0x2f, 0x69, 0x6e, 0x63, 0x32, 0x00, 0x00, + // File names + // foo.rs + 0x66, 0x6f, 0x6f, 0x2e, 0x72, 0x73, 0x00, + 0x00, + 0x00, + 0x00, + // bar.h + 0x62, 0x61, 0x72, 0x2e, 0x68, 0x00, + 0x01, + 0x00, + 0x00, + // End file names. + 0x00, + + // Dummy line program data. + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + // Dummy next line program. + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + ]; + + let rest = &mut EndianSlice::new(&buf, LittleEndian); + let comp_dir = EndianSlice::new(b"/comp_dir", LittleEndian); + let comp_name = EndianSlice::new(b"/comp_name", LittleEndian); + + let header = + LineProgramHeader::parse(rest, DebugLineOffset(0), 4, Some(comp_dir), Some(comp_name)) + .expect("should parse header ok"); + + assert_eq!( + *rest, + EndianSlice::new(&buf[buf.len() - 16..], LittleEndian) + ); + + assert_eq!(header.offset, DebugLineOffset(0)); + assert_eq!(header.version(), 4); + assert_eq!(header.minimum_instruction_length(), 1); + assert_eq!(header.maximum_operations_per_instruction(), 1); + assert_eq!(header.default_is_stmt(), true); + assert_eq!(header.line_base(), 0); + assert_eq!(header.line_range(), 1); + assert_eq!(header.opcode_base(), 3); + assert_eq!(header.directory(0), Some(AttributeValue::String(comp_dir))); + assert_eq!( + header.file(0).unwrap().path_name, + AttributeValue::String(comp_name) + ); + + let expected_lengths = [1, 2]; + assert_eq!(header.standard_opcode_lengths().slice(), &expected_lengths); + + let expected_include_directories = [ + AttributeValue::String(EndianSlice::new(b"/inc", LittleEndian)), + AttributeValue::String(EndianSlice::new(b"/inc2", LittleEndian)), + ]; + assert_eq!(header.include_directories(), &expected_include_directories); + + let expected_file_names = [ + FileEntry { + path_name: AttributeValue::String(EndianSlice::new(b"foo.rs", LittleEndian)), + directory_index: 0, + timestamp: 0, + size: 0, + md5: [0; 16], + }, + FileEntry { + path_name: AttributeValue::String(EndianSlice::new(b"bar.h", LittleEndian)), + directory_index: 1, + timestamp: 0, + size: 0, + md5: [0; 16], + }, + ]; + assert_eq!(&*header.file_names(), &expected_file_names); + } + + #[test] + fn test_parse_debug_line_header_length_too_short() { + #[rustfmt::skip] + let buf = [ + // 32-bit length = 62. + 0x3e, 0x00, 0x00, 0x00, + // Version. + 0x04, 0x00, + // Header length = 20. TOO SHORT!!! + 0x15, 0x00, 0x00, 0x00, + // Minimum instruction length. + 0x01, + // Maximum operations per byte. + 0x01, + // Default is_stmt. + 0x01, + // Line base. + 0x00, + // Line range. + 0x01, + // Opcode base. + 0x03, + // Standard opcode lengths for opcodes 1 .. opcode base - 1. + 0x01, 0x02, + // Include directories = '/', 'i', 'n', 'c', '\0', '/', 'i', 'n', 'c', '2', '\0', '\0' + 0x2f, 0x69, 0x6e, 0x63, 0x00, 0x2f, 0x69, 0x6e, 0x63, 0x32, 0x00, 0x00, + // File names + // foo.rs + 0x66, 0x6f, 0x6f, 0x2e, 0x72, 0x73, 0x00, + 0x00, + 0x00, + 0x00, + // bar.h + 0x62, 0x61, 0x72, 0x2e, 0x68, 0x00, + 0x01, + 0x00, + 0x00, + // End file names. + 0x00, + + // Dummy line program data. + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + // Dummy next line program. + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + ]; + + let input = &mut EndianSlice::new(&buf, LittleEndian); + + match LineProgramHeader::parse(input, DebugLineOffset(0), 4, None, None) { + Err(Error::UnexpectedEof(_)) => return, + otherwise => panic!("Unexpected result: {:?}", otherwise), + } + } + + #[test] + fn test_parse_debug_line_unit_length_too_short() { + #[rustfmt::skip] + let buf = [ + // 32-bit length = 40. TOO SHORT!!! + 0x28, 0x00, 0x00, 0x00, + // Version. + 0x04, 0x00, + // Header length = 40. + 0x28, 0x00, 0x00, 0x00, + // Minimum instruction length. + 0x01, + // Maximum operations per byte. + 0x01, + // Default is_stmt. + 0x01, + // Line base. + 0x00, + // Line range. + 0x01, + // Opcode base. + 0x03, + // Standard opcode lengths for opcodes 1 .. opcode base - 1. + 0x01, 0x02, + // Include directories = '/', 'i', 'n', 'c', '\0', '/', 'i', 'n', 'c', '2', '\0', '\0' + 0x2f, 0x69, 0x6e, 0x63, 0x00, 0x2f, 0x69, 0x6e, 0x63, 0x32, 0x00, 0x00, + // File names + // foo.rs + 0x66, 0x6f, 0x6f, 0x2e, 0x72, 0x73, 0x00, + 0x00, + 0x00, + 0x00, + // bar.h + 0x62, 0x61, 0x72, 0x2e, 0x68, 0x00, + 0x01, + 0x00, + 0x00, + // End file names. + 0x00, + + // Dummy line program data. + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + // Dummy next line program. + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + ]; + + let input = &mut EndianSlice::new(&buf, LittleEndian); + + match LineProgramHeader::parse(input, DebugLineOffset(0), 4, None, None) { + Err(Error::UnexpectedEof(_)) => return, + otherwise => panic!("Unexpected result: {:?}", otherwise), + } + } + + const OPCODE_BASE: u8 = 13; + const STANDARD_OPCODE_LENGTHS: &[u8] = &[0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1]; + + fn make_test_header( + buf: EndianSlice<LittleEndian>, + ) -> LineProgramHeader<EndianSlice<LittleEndian>> { + let encoding = Encoding { + format: Format::Dwarf32, + version: 4, + address_size: 8, + }; + let line_encoding = LineEncoding { + line_base: -3, + line_range: 12, + ..Default::default() + }; + LineProgramHeader { + encoding, + offset: DebugLineOffset(0), + unit_length: 1, + header_length: 1, + line_encoding, + opcode_base: OPCODE_BASE, + standard_opcode_lengths: EndianSlice::new(STANDARD_OPCODE_LENGTHS, LittleEndian), + file_names: vec![ + FileEntry { + path_name: AttributeValue::String(EndianSlice::new(b"foo.c", LittleEndian)), + directory_index: 0, + timestamp: 0, + size: 0, + md5: [0; 16], + }, + FileEntry { + path_name: AttributeValue::String(EndianSlice::new(b"bar.rs", LittleEndian)), + directory_index: 0, + timestamp: 0, + size: 0, + md5: [0; 16], + }, + ], + include_directories: vec![], + directory_entry_format: vec![], + file_name_entry_format: vec![], + program_buf: buf, + comp_dir: None, + comp_file: None, + } + } + + fn make_test_program( + buf: EndianSlice<LittleEndian>, + ) -> IncompleteLineProgram<EndianSlice<LittleEndian>> { + IncompleteLineProgram { + header: make_test_header(buf), + } + } + + #[test] + fn test_parse_special_opcodes() { + for i in OPCODE_BASE..u8::MAX { + let input = [i, 0, 0, 0]; + let input = EndianSlice::new(&input, LittleEndian); + let header = make_test_header(input); + + let mut rest = input; + let opcode = + LineInstruction::parse(&header, &mut rest).expect("Should parse the opcode OK"); + + assert_eq!(*rest, *input.range_from(1..)); + assert_eq!(opcode, LineInstruction::Special(i)); + } + } + + #[test] + fn test_parse_standard_opcodes() { + fn test<Operands>( + raw: constants::DwLns, + operands: Operands, + expected: LineInstruction<EndianSlice<LittleEndian>>, + ) where + Operands: AsRef<[u8]>, + { + let mut input = Vec::new(); + input.push(raw.0); + input.extend_from_slice(operands.as_ref()); + + let expected_rest = [0, 1, 2, 3, 4]; + input.extend_from_slice(&expected_rest); + + let input = EndianSlice::new(&*input, LittleEndian); + let header = make_test_header(input); + + let mut rest = input; + let opcode = + LineInstruction::parse(&header, &mut rest).expect("Should parse the opcode OK"); + + assert_eq!(opcode, expected); + assert_eq!(*rest, expected_rest); + } + + test(constants::DW_LNS_copy, [], LineInstruction::Copy); + test( + constants::DW_LNS_advance_pc, + [42], + LineInstruction::AdvancePc(42), + ); + test( + constants::DW_LNS_advance_line, + [9], + LineInstruction::AdvanceLine(9), + ); + test(constants::DW_LNS_set_file, [7], LineInstruction::SetFile(7)); + test( + constants::DW_LNS_set_column, + [1], + LineInstruction::SetColumn(1), + ); + test( + constants::DW_LNS_negate_stmt, + [], + LineInstruction::NegateStatement, + ); + test( + constants::DW_LNS_set_basic_block, + [], + LineInstruction::SetBasicBlock, + ); + test( + constants::DW_LNS_const_add_pc, + [], + LineInstruction::ConstAddPc, + ); + test( + constants::DW_LNS_fixed_advance_pc, + [42, 0], + LineInstruction::FixedAddPc(42), + ); + test( + constants::DW_LNS_set_prologue_end, + [], + LineInstruction::SetPrologueEnd, + ); + test( + constants::DW_LNS_set_isa, + [57 + 0x80, 100], + LineInstruction::SetIsa(12857), + ); + } + + #[test] + fn test_parse_unknown_standard_opcode_no_args() { + let input = [OPCODE_BASE, 1, 2, 3]; + let input = EndianSlice::new(&input, LittleEndian); + let mut standard_opcode_lengths = Vec::new(); + let mut header = make_test_header(input); + standard_opcode_lengths.extend(header.standard_opcode_lengths.slice()); + standard_opcode_lengths.push(0); + header.opcode_base += 1; + header.standard_opcode_lengths = EndianSlice::new(&standard_opcode_lengths, LittleEndian); + + let mut rest = input; + let opcode = + LineInstruction::parse(&header, &mut rest).expect("Should parse the opcode OK"); + + assert_eq!( + opcode, + LineInstruction::UnknownStandard0(constants::DwLns(OPCODE_BASE)) + ); + assert_eq!(*rest, *input.range_from(1..)); + } + + #[test] + fn test_parse_unknown_standard_opcode_one_arg() { + let input = [OPCODE_BASE, 1, 2, 3]; + let input = EndianSlice::new(&input, LittleEndian); + let mut standard_opcode_lengths = Vec::new(); + let mut header = make_test_header(input); + standard_opcode_lengths.extend(header.standard_opcode_lengths.slice()); + standard_opcode_lengths.push(1); + header.opcode_base += 1; + header.standard_opcode_lengths = EndianSlice::new(&standard_opcode_lengths, LittleEndian); + + let mut rest = input; + let opcode = + LineInstruction::parse(&header, &mut rest).expect("Should parse the opcode OK"); + + assert_eq!( + opcode, + LineInstruction::UnknownStandard1(constants::DwLns(OPCODE_BASE), 1) + ); + assert_eq!(*rest, *input.range_from(2..)); + } + + #[test] + fn test_parse_unknown_standard_opcode_many_args() { + let input = [OPCODE_BASE, 1, 2, 3]; + let input = EndianSlice::new(&input, LittleEndian); + let args = input.range_from(1..); + let mut standard_opcode_lengths = Vec::new(); + let mut header = make_test_header(input); + standard_opcode_lengths.extend(header.standard_opcode_lengths.slice()); + standard_opcode_lengths.push(3); + header.opcode_base += 1; + header.standard_opcode_lengths = EndianSlice::new(&standard_opcode_lengths, LittleEndian); + + let mut rest = input; + let opcode = + LineInstruction::parse(&header, &mut rest).expect("Should parse the opcode OK"); + + assert_eq!( + opcode, + LineInstruction::UnknownStandardN(constants::DwLns(OPCODE_BASE), args) + ); + assert_eq!(*rest, []); + } + + #[test] + fn test_parse_extended_opcodes() { + fn test<Operands>( + raw: constants::DwLne, + operands: Operands, + expected: LineInstruction<EndianSlice<LittleEndian>>, + ) where + Operands: AsRef<[u8]>, + { + let mut input = Vec::new(); + input.push(0); + + let operands = operands.as_ref(); + input.push(1 + operands.len() as u8); + + input.push(raw.0); + input.extend_from_slice(operands); + + let expected_rest = [0, 1, 2, 3, 4]; + input.extend_from_slice(&expected_rest); + + let input = EndianSlice::new(&input, LittleEndian); + let header = make_test_header(input); + + let mut rest = input; + let opcode = + LineInstruction::parse(&header, &mut rest).expect("Should parse the opcode OK"); + + assert_eq!(opcode, expected); + assert_eq!(*rest, expected_rest); + } + + test( + constants::DW_LNE_end_sequence, + [], + LineInstruction::EndSequence, + ); + test( + constants::DW_LNE_set_address, + [1, 2, 3, 4, 5, 6, 7, 8], + LineInstruction::SetAddress(578_437_695_752_307_201), + ); + test( + constants::DW_LNE_set_discriminator, + [42], + LineInstruction::SetDiscriminator(42), + ); + + let mut file = Vec::new(); + // "foo.c" + let path_name = [b'f', b'o', b'o', b'.', b'c', 0]; + file.extend_from_slice(&path_name); + // Directory index. + file.push(0); + // Last modification of file. + file.push(1); + // Size of file. + file.push(2); + + test( + constants::DW_LNE_define_file, + file, + LineInstruction::DefineFile(FileEntry { + path_name: AttributeValue::String(EndianSlice::new(b"foo.c", LittleEndian)), + directory_index: 0, + timestamp: 1, + size: 2, + md5: [0; 16], + }), + ); + + // Unknown extended opcode. + let operands = [1, 2, 3, 4, 5, 6]; + let opcode = constants::DwLne(99); + test( + opcode, + operands, + LineInstruction::UnknownExtended(opcode, EndianSlice::new(&operands, LittleEndian)), + ); + } + + #[test] + fn test_file_entry_directory() { + let path_name = [b'f', b'o', b'o', b'.', b'r', b's', 0]; + + let mut file = FileEntry { + path_name: AttributeValue::String(EndianSlice::new(&path_name, LittleEndian)), + directory_index: 1, + timestamp: 0, + size: 0, + md5: [0; 16], + }; + + let mut header = make_test_header(EndianSlice::new(&[], LittleEndian)); + + let dir = AttributeValue::String(EndianSlice::new(b"dir", LittleEndian)); + header.include_directories.push(dir); + + assert_eq!(file.directory(&header), Some(dir)); + + // Now test the compilation's current directory. + file.directory_index = 0; + assert_eq!(file.directory(&header), None); + } + + fn assert_exec_opcode<'input>( + header: LineProgramHeader<EndianSlice<'input, LittleEndian>>, + mut registers: LineRow, + opcode: LineInstruction<EndianSlice<'input, LittleEndian>>, + expected_registers: LineRow, + expect_new_row: bool, + ) { + let mut program = IncompleteLineProgram { header }; + let is_new_row = registers.execute(opcode, &mut program); + + assert_eq!(is_new_row, expect_new_row); + assert_eq!(registers, expected_registers); + } + + #[test] + fn test_exec_special_noop() { + let header = make_test_header(EndianSlice::new(&[], LittleEndian)); + + let initial_registers = LineRow::new(&header); + let opcode = LineInstruction::Special(16); + let expected_registers = initial_registers; + + assert_exec_opcode(header, initial_registers, opcode, expected_registers, true); + } + + #[test] + fn test_exec_special_negative_line_advance() { + let header = make_test_header(EndianSlice::new(&[], LittleEndian)); + + let mut initial_registers = LineRow::new(&header); + initial_registers.line.0 = 10; + + let opcode = LineInstruction::Special(13); + + let mut expected_registers = initial_registers; + expected_registers.line.0 -= 3; + + assert_exec_opcode(header, initial_registers, opcode, expected_registers, true); + } + + #[test] + fn test_exec_special_positive_line_advance() { + let header = make_test_header(EndianSlice::new(&[], LittleEndian)); + + let initial_registers = LineRow::new(&header); + + let opcode = LineInstruction::Special(19); + + let mut expected_registers = initial_registers; + expected_registers.line.0 += 3; + + assert_exec_opcode(header, initial_registers, opcode, expected_registers, true); + } + + #[test] + fn test_exec_special_positive_address_advance() { + let header = make_test_header(EndianSlice::new(&[], LittleEndian)); + + let initial_registers = LineRow::new(&header); + + let opcode = LineInstruction::Special(52); + + let mut expected_registers = initial_registers; + expected_registers.address.0 += 3; + + assert_exec_opcode(header, initial_registers, opcode, expected_registers, true); + } + + #[test] + fn test_exec_special_positive_address_and_line_advance() { + let header = make_test_header(EndianSlice::new(&[], LittleEndian)); + + let initial_registers = LineRow::new(&header); + + let opcode = LineInstruction::Special(55); + + let mut expected_registers = initial_registers; + expected_registers.address.0 += 3; + expected_registers.line.0 += 3; + + assert_exec_opcode(header, initial_registers, opcode, expected_registers, true); + } + + #[test] + fn test_exec_special_positive_address_and_negative_line_advance() { + let header = make_test_header(EndianSlice::new(&[], LittleEndian)); + + let mut initial_registers = LineRow::new(&header); + initial_registers.line.0 = 10; + + let opcode = LineInstruction::Special(49); + + let mut expected_registers = initial_registers; + expected_registers.address.0 += 3; + expected_registers.line.0 -= 3; + + assert_exec_opcode(header, initial_registers, opcode, expected_registers, true); + } + + #[test] + fn test_exec_special_line_underflow() { + let header = make_test_header(EndianSlice::new(&[], LittleEndian)); + + let mut initial_registers = LineRow::new(&header); + initial_registers.line.0 = 2; + + // -3 line advance. + let opcode = LineInstruction::Special(13); + + let mut expected_registers = initial_registers; + // Clamp at 0. No idea if this is the best way to handle this situation + // or not... + expected_registers.line.0 = 0; + + assert_exec_opcode(header, initial_registers, opcode, expected_registers, true); + } + + #[test] + fn test_exec_copy() { + let header = make_test_header(EndianSlice::new(&[], LittleEndian)); + + let mut initial_registers = LineRow::new(&header); + initial_registers.address.0 = 1337; + initial_registers.line.0 = 42; + + let opcode = LineInstruction::Copy; + + let expected_registers = initial_registers; + + assert_exec_opcode(header, initial_registers, opcode, expected_registers, true); + } + + #[test] + fn test_exec_advance_pc() { + let header = make_test_header(EndianSlice::new(&[], LittleEndian)); + let initial_registers = LineRow::new(&header); + let opcode = LineInstruction::AdvancePc(42); + + let mut expected_registers = initial_registers; + expected_registers.address.0 += 42; + + assert_exec_opcode(header, initial_registers, opcode, expected_registers, false); + } + + #[test] + fn test_exec_advance_pc_overflow() { + let header = make_test_header(EndianSlice::new(&[], LittleEndian)); + let opcode = LineInstruction::AdvancePc(42); + + let mut initial_registers = LineRow::new(&header); + initial_registers.address.0 = u64::MAX; + + let mut expected_registers = initial_registers; + expected_registers.address.0 = 41; + + assert_exec_opcode(header, initial_registers, opcode, expected_registers, false); + } + + #[test] + fn test_exec_advance_line() { + let header = make_test_header(EndianSlice::new(&[], LittleEndian)); + let initial_registers = LineRow::new(&header); + let opcode = LineInstruction::AdvanceLine(42); + + let mut expected_registers = initial_registers; + expected_registers.line.0 += 42; + + assert_exec_opcode(header, initial_registers, opcode, expected_registers, false); + } + + #[test] + fn test_exec_advance_line_overflow() { + let header = make_test_header(EndianSlice::new(&[], LittleEndian)); + let opcode = LineInstruction::AdvanceLine(42); + + let mut initial_registers = LineRow::new(&header); + initial_registers.line.0 = u64::MAX; + + let mut expected_registers = initial_registers; + expected_registers.line.0 = 41; + + assert_exec_opcode(header, initial_registers, opcode, expected_registers, false); + } + + #[test] + fn test_exec_set_file_in_bounds() { + for file_idx in 1..3 { + let header = make_test_header(EndianSlice::new(&[], LittleEndian)); + let initial_registers = LineRow::new(&header); + let opcode = LineInstruction::SetFile(file_idx); + + let mut expected_registers = initial_registers; + expected_registers.file = file_idx; + + assert_exec_opcode(header, initial_registers, opcode, expected_registers, false); + } + } + + #[test] + fn test_exec_set_file_out_of_bounds() { + let header = make_test_header(EndianSlice::new(&[], LittleEndian)); + let initial_registers = LineRow::new(&header); + let opcode = LineInstruction::SetFile(100); + + // The spec doesn't say anything about rejecting input programs + // that set the file register out of bounds of the actual number + // of files that have been defined. Instead, we cross our + // fingers and hope that one gets defined before + // `LineRow::file` gets called and handle the error at + // that time if need be. + let mut expected_registers = initial_registers; + expected_registers.file = 100; + + assert_exec_opcode(header, initial_registers, opcode, expected_registers, false); + } + + #[test] + fn test_file_entry_file_index_out_of_bounds() { + // These indices are 1-based, so 0 is invalid. 100 is way more than the + // number of files defined in the header. + let out_of_bounds_indices = [0, 100]; + + for file_idx in &out_of_bounds_indices[..] { + let header = make_test_header(EndianSlice::new(&[], LittleEndian)); + let mut row = LineRow::new(&header); + + row.file = *file_idx; + + assert_eq!(row.file(&header), None); + } + } + + #[test] + fn test_file_entry_file_index_in_bounds() { + let header = make_test_header(EndianSlice::new(&[], LittleEndian)); + let mut row = LineRow::new(&header); + + row.file = 2; + + assert_eq!(row.file(&header), Some(&header.file_names()[1])); + } + + #[test] + fn test_exec_set_column() { + let header = make_test_header(EndianSlice::new(&[], LittleEndian)); + let initial_registers = LineRow::new(&header); + let opcode = LineInstruction::SetColumn(42); + + let mut expected_registers = initial_registers; + expected_registers.column = 42; + + assert_exec_opcode(header, initial_registers, opcode, expected_registers, false); + } + + #[test] + fn test_exec_negate_statement() { + let header = make_test_header(EndianSlice::new(&[], LittleEndian)); + let initial_registers = LineRow::new(&header); + let opcode = LineInstruction::NegateStatement; + + let mut expected_registers = initial_registers; + expected_registers.is_stmt = !initial_registers.is_stmt; + + assert_exec_opcode(header, initial_registers, opcode, expected_registers, false); + } + + #[test] + fn test_exec_set_basic_block() { + let header = make_test_header(EndianSlice::new(&[], LittleEndian)); + + let mut initial_registers = LineRow::new(&header); + initial_registers.basic_block = false; + + let opcode = LineInstruction::SetBasicBlock; + + let mut expected_registers = initial_registers; + expected_registers.basic_block = true; + + assert_exec_opcode(header, initial_registers, opcode, expected_registers, false); + } + + #[test] + fn test_exec_const_add_pc() { + let header = make_test_header(EndianSlice::new(&[], LittleEndian)); + let initial_registers = LineRow::new(&header); + let opcode = LineInstruction::ConstAddPc; + + let mut expected_registers = initial_registers; + expected_registers.address.0 += 20; + + assert_exec_opcode(header, initial_registers, opcode, expected_registers, false); + } + + #[test] + fn test_exec_fixed_add_pc() { + let header = make_test_header(EndianSlice::new(&[], LittleEndian)); + + let mut initial_registers = LineRow::new(&header); + initial_registers.op_index.0 = 1; + + let opcode = LineInstruction::FixedAddPc(10); + + let mut expected_registers = initial_registers; + expected_registers.address.0 += 10; + expected_registers.op_index.0 = 0; + + assert_exec_opcode(header, initial_registers, opcode, expected_registers, false); + } + + #[test] + fn test_exec_set_prologue_end() { + let header = make_test_header(EndianSlice::new(&[], LittleEndian)); + + let mut initial_registers = LineRow::new(&header); + initial_registers.prologue_end = false; + + let opcode = LineInstruction::SetPrologueEnd; + + let mut expected_registers = initial_registers; + expected_registers.prologue_end = true; + + assert_exec_opcode(header, initial_registers, opcode, expected_registers, false); + } + + #[test] + fn test_exec_set_isa() { + let header = make_test_header(EndianSlice::new(&[], LittleEndian)); + let initial_registers = LineRow::new(&header); + let opcode = LineInstruction::SetIsa(1993); + + let mut expected_registers = initial_registers; + expected_registers.isa = 1993; + + assert_exec_opcode(header, initial_registers, opcode, expected_registers, false); + } + + #[test] + fn test_exec_unknown_standard_0() { + let header = make_test_header(EndianSlice::new(&[], LittleEndian)); + let initial_registers = LineRow::new(&header); + let opcode = LineInstruction::UnknownStandard0(constants::DwLns(111)); + let expected_registers = initial_registers; + assert_exec_opcode(header, initial_registers, opcode, expected_registers, false); + } + + #[test] + fn test_exec_unknown_standard_1() { + let header = make_test_header(EndianSlice::new(&[], LittleEndian)); + let initial_registers = LineRow::new(&header); + let opcode = LineInstruction::UnknownStandard1(constants::DwLns(111), 2); + let expected_registers = initial_registers; + assert_exec_opcode(header, initial_registers, opcode, expected_registers, false); + } + + #[test] + fn test_exec_unknown_standard_n() { + let header = make_test_header(EndianSlice::new(&[], LittleEndian)); + let initial_registers = LineRow::new(&header); + let opcode = LineInstruction::UnknownStandardN( + constants::DwLns(111), + EndianSlice::new(&[2, 2, 2], LittleEndian), + ); + let expected_registers = initial_registers; + assert_exec_opcode(header, initial_registers, opcode, expected_registers, false); + } + + #[test] + fn test_exec_end_sequence() { + let header = make_test_header(EndianSlice::new(&[], LittleEndian)); + let initial_registers = LineRow::new(&header); + let opcode = LineInstruction::EndSequence; + + let mut expected_registers = initial_registers; + expected_registers.end_sequence = true; + + assert_exec_opcode(header, initial_registers, opcode, expected_registers, true); + } + + #[test] + fn test_exec_set_address() { + let header = make_test_header(EndianSlice::new(&[], LittleEndian)); + let initial_registers = LineRow::new(&header); + let opcode = LineInstruction::SetAddress(3030); + + let mut expected_registers = initial_registers; + expected_registers.address.0 = 3030; + + assert_exec_opcode(header, initial_registers, opcode, expected_registers, false); + } + + #[test] + fn test_exec_set_address_tombstone() { + let header = make_test_header(EndianSlice::new(&[], LittleEndian)); + let initial_registers = LineRow::new(&header); + let opcode = LineInstruction::SetAddress(!0); + + let mut expected_registers = initial_registers; + expected_registers.tombstone = true; + expected_registers.address.0 = !0; + + assert_exec_opcode(header, initial_registers, opcode, expected_registers, false); + } + + #[test] + fn test_exec_define_file() { + let mut program = make_test_program(EndianSlice::new(&[], LittleEndian)); + let mut row = LineRow::new(program.header()); + + let file = FileEntry { + path_name: AttributeValue::String(EndianSlice::new(b"test.cpp", LittleEndian)), + directory_index: 0, + timestamp: 0, + size: 0, + md5: [0; 16], + }; + + let opcode = LineInstruction::DefineFile(file); + let is_new_row = row.execute(opcode, &mut program); + + assert_eq!(is_new_row, false); + assert_eq!(Some(&file), program.header().file_names.last()); + } + + #[test] + fn test_exec_set_discriminator() { + let header = make_test_header(EndianSlice::new(&[], LittleEndian)); + let initial_registers = LineRow::new(&header); + let opcode = LineInstruction::SetDiscriminator(9); + + let mut expected_registers = initial_registers; + expected_registers.discriminator = 9; + + assert_exec_opcode(header, initial_registers, opcode, expected_registers, false); + } + + #[test] + fn test_exec_unknown_extended() { + let header = make_test_header(EndianSlice::new(&[], LittleEndian)); + let initial_registers = LineRow::new(&header); + let opcode = LineInstruction::UnknownExtended( + constants::DwLne(74), + EndianSlice::new(&[], LittleEndian), + ); + let expected_registers = initial_registers; + assert_exec_opcode(header, initial_registers, opcode, expected_registers, false); + } + + /// Ensure that `LineRows<R,P>` is covariant wrt R. + /// This only needs to compile. + #[allow(dead_code, unreachable_code, unused_variables)] + fn test_line_rows_variance<'a, 'b>(_: &'a [u8], _: &'b [u8]) + where + 'a: 'b, + { + let a: &OneShotLineRows<EndianSlice<'a, LittleEndian>> = unimplemented!(); + let _: &OneShotLineRows<EndianSlice<'b, LittleEndian>> = a; + } + + #[test] + fn test_parse_debug_line_v5_ok() { + let expected_lengths = &[1, 2]; + let expected_program = &[0, 1, 2, 3, 4]; + let expected_rest = &[5, 6, 7, 8, 9]; + let expected_include_directories = [ + AttributeValue::String(EndianSlice::new(b"dir1", LittleEndian)), + AttributeValue::String(EndianSlice::new(b"dir2", LittleEndian)), + ]; + let expected_file_names = [ + FileEntry { + path_name: AttributeValue::String(EndianSlice::new(b"file1", LittleEndian)), + directory_index: 0, + timestamp: 0, + size: 0, + md5: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], + }, + FileEntry { + path_name: AttributeValue::String(EndianSlice::new(b"file2", LittleEndian)), + directory_index: 1, + timestamp: 0, + size: 0, + md5: [ + 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + ], + }, + ]; + + for format in vec![Format::Dwarf32, Format::Dwarf64] { + let length = Label::new(); + let header_length = Label::new(); + let start = Label::new(); + let header_start = Label::new(); + let end = Label::new(); + let header_end = Label::new(); + let section = Section::with_endian(Endian::Little) + .initial_length(format, &length, &start) + .D16(5) + // Address size. + .D8(4) + // Segment selector size. + .D8(0) + .word_label(format.word_size(), &header_length) + .mark(&header_start) + // Minimum instruction length. + .D8(1) + // Maximum operations per byte. + .D8(1) + // Default is_stmt. + .D8(1) + // Line base. + .D8(0) + // Line range. + .D8(1) + // Opcode base. + .D8(expected_lengths.len() as u8 + 1) + // Standard opcode lengths for opcodes 1 .. opcode base - 1. + .append_bytes(expected_lengths) + // Directory entry format count. + .D8(1) + .uleb(constants::DW_LNCT_path.0 as u64) + .uleb(constants::DW_FORM_string.0 as u64) + // Directory count. + .D8(2) + .append_bytes(b"dir1\0") + .append_bytes(b"dir2\0") + // File entry format count. + .D8(3) + .uleb(constants::DW_LNCT_path.0 as u64) + .uleb(constants::DW_FORM_string.0 as u64) + .uleb(constants::DW_LNCT_directory_index.0 as u64) + .uleb(constants::DW_FORM_data1.0 as u64) + .uleb(constants::DW_LNCT_MD5.0 as u64) + .uleb(constants::DW_FORM_data16.0 as u64) + // File count. + .D8(2) + .append_bytes(b"file1\0") + .D8(0) + .append_bytes(&expected_file_names[0].md5) + .append_bytes(b"file2\0") + .D8(1) + .append_bytes(&expected_file_names[1].md5) + .mark(&header_end) + // Dummy line program data. + .append_bytes(expected_program) + .mark(&end) + // Dummy trailing data. + .append_bytes(expected_rest); + length.set_const((&end - &start) as u64); + header_length.set_const((&header_end - &header_start) as u64); + let section = section.get_contents().unwrap(); + + let input = &mut EndianSlice::new(§ion, LittleEndian); + + let header = LineProgramHeader::parse(input, DebugLineOffset(0), 0, None, None) + .expect("should parse header ok"); + + assert_eq!(header.raw_program_buf().slice(), expected_program); + assert_eq!(input.slice(), expected_rest); + + assert_eq!(header.offset, DebugLineOffset(0)); + assert_eq!(header.version(), 5); + assert_eq!(header.address_size(), 4); + assert_eq!(header.minimum_instruction_length(), 1); + assert_eq!(header.maximum_operations_per_instruction(), 1); + assert_eq!(header.default_is_stmt(), true); + assert_eq!(header.line_base(), 0); + assert_eq!(header.line_range(), 1); + assert_eq!(header.opcode_base(), expected_lengths.len() as u8 + 1); + assert_eq!(header.standard_opcode_lengths().slice(), expected_lengths); + assert_eq!( + header.directory_entry_format(), + &[FileEntryFormat { + content_type: constants::DW_LNCT_path, + form: constants::DW_FORM_string, + }] + ); + assert_eq!(header.include_directories(), expected_include_directories); + assert_eq!(header.directory(0), Some(expected_include_directories[0])); + assert_eq!( + header.file_name_entry_format(), + &[ + FileEntryFormat { + content_type: constants::DW_LNCT_path, + form: constants::DW_FORM_string, + }, + FileEntryFormat { + content_type: constants::DW_LNCT_directory_index, + form: constants::DW_FORM_data1, + }, + FileEntryFormat { + content_type: constants::DW_LNCT_MD5, + form: constants::DW_FORM_data16, + } + ] + ); + assert_eq!(header.file_names(), expected_file_names); + assert_eq!(header.file(0), Some(&expected_file_names[0])); + } + } + + #[test] + fn test_sequences() { + #[rustfmt::skip] + let buf = [ + // 32-bit length + 94, 0x00, 0x00, 0x00, + // Version. + 0x04, 0x00, + // Header length = 40. + 0x28, 0x00, 0x00, 0x00, + // Minimum instruction length. + 0x01, + // Maximum operations per byte. + 0x01, + // Default is_stmt. + 0x01, + // Line base. + 0x00, + // Line range. + 0x01, + // Opcode base. + 0x03, + // Standard opcode lengths for opcodes 1 .. opcode base - 1. + 0x01, 0x02, + // Include directories = '/', 'i', 'n', 'c', '\0', '/', 'i', 'n', 'c', '2', '\0', '\0' + 0x2f, 0x69, 0x6e, 0x63, 0x00, 0x2f, 0x69, 0x6e, 0x63, 0x32, 0x00, 0x00, + // File names + // foo.rs + 0x66, 0x6f, 0x6f, 0x2e, 0x72, 0x73, 0x00, + 0x00, + 0x00, + 0x00, + // bar.h + 0x62, 0x61, 0x72, 0x2e, 0x68, 0x00, + 0x01, + 0x00, + 0x00, + // End file names. + 0x00, + + 0, 5, constants::DW_LNE_set_address.0, 1, 0, 0, 0, + constants::DW_LNS_copy.0, + constants::DW_LNS_advance_pc.0, 1, + constants::DW_LNS_copy.0, + constants::DW_LNS_advance_pc.0, 2, + 0, 1, constants::DW_LNE_end_sequence.0, + + // Tombstone + 0, 5, constants::DW_LNE_set_address.0, 0xff, 0xff, 0xff, 0xff, + constants::DW_LNS_copy.0, + constants::DW_LNS_advance_pc.0, 1, + constants::DW_LNS_copy.0, + constants::DW_LNS_advance_pc.0, 2, + 0, 1, constants::DW_LNE_end_sequence.0, + + 0, 5, constants::DW_LNE_set_address.0, 11, 0, 0, 0, + constants::DW_LNS_copy.0, + constants::DW_LNS_advance_pc.0, 1, + constants::DW_LNS_copy.0, + constants::DW_LNS_advance_pc.0, 2, + 0, 1, constants::DW_LNE_end_sequence.0, + ]; + assert_eq!(buf[0] as usize, buf.len() - 4); + + let rest = &mut EndianSlice::new(&buf, LittleEndian); + + let header = LineProgramHeader::parse(rest, DebugLineOffset(0), 4, None, None) + .expect("should parse header ok"); + let program = IncompleteLineProgram { header }; + + let sequences = program.sequences().unwrap().1; + assert_eq!(sequences.len(), 2); + assert_eq!(sequences[0].start, 1); + assert_eq!(sequences[0].end, 4); + assert_eq!(sequences[1].start, 11); + assert_eq!(sequences[1].end, 14); + } +} diff --git a/vendor/gimli/src/read/lists.rs b/vendor/gimli/src/read/lists.rs new file mode 100644 index 0000000..898a757 --- /dev/null +++ b/vendor/gimli/src/read/lists.rs @@ -0,0 +1,68 @@ +use crate::common::{Encoding, Format}; +use crate::read::{Error, Reader, Result}; + +#[derive(Debug, Clone, Copy)] +pub(crate) struct ListsHeader { + encoding: Encoding, + #[allow(dead_code)] + offset_entry_count: u32, +} + +impl Default for ListsHeader { + fn default() -> Self { + ListsHeader { + encoding: Encoding { + format: Format::Dwarf32, + version: 5, + address_size: 0, + }, + offset_entry_count: 0, + } + } +} + +impl ListsHeader { + /// Return the serialized size of the table header. + #[allow(dead_code)] + #[inline] + fn size(self) -> u8 { + // initial_length + version + address_size + segment_selector_size + offset_entry_count + ListsHeader::size_for_encoding(self.encoding) + } + + /// Return the serialized size of the table header. + #[inline] + pub(crate) fn size_for_encoding(encoding: Encoding) -> u8 { + // initial_length + version + address_size + segment_selector_size + offset_entry_count + encoding.format.initial_length_size() + 2 + 1 + 1 + 4 + } +} + +// TODO: add an iterator over headers in the appropriate sections section +#[allow(dead_code)] +fn parse_header<R: Reader>(input: &mut R) -> Result<ListsHeader> { + let (length, format) = input.read_initial_length()?; + input.truncate(length)?; + + let version = input.read_u16()?; + if version != 5 { + return Err(Error::UnknownVersion(u64::from(version))); + } + + let address_size = input.read_u8()?; + let segment_selector_size = input.read_u8()?; + if segment_selector_size != 0 { + return Err(Error::UnsupportedSegmentSize); + } + let offset_entry_count = input.read_u32()?; + + let encoding = Encoding { + format, + version, + address_size, + }; + Ok(ListsHeader { + encoding, + offset_entry_count, + }) +} diff --git a/vendor/gimli/src/read/loclists.rs b/vendor/gimli/src/read/loclists.rs new file mode 100644 index 0000000..5cba675 --- /dev/null +++ b/vendor/gimli/src/read/loclists.rs @@ -0,0 +1,1627 @@ +use crate::common::{ + DebugAddrBase, DebugAddrIndex, DebugLocListsBase, DebugLocListsIndex, DwarfFileType, Encoding, + LocationListsOffset, SectionId, +}; +use crate::constants; +use crate::endianity::Endianity; +use crate::read::{ + lists::ListsHeader, DebugAddr, EndianSlice, Error, Expression, Range, RawRange, Reader, + ReaderOffset, ReaderOffsetId, Result, Section, +}; + +/// The raw contents of the `.debug_loc` section. +#[derive(Debug, Default, Clone, Copy)] +pub struct DebugLoc<R> { + pub(crate) section: R, +} + +impl<'input, Endian> DebugLoc<EndianSlice<'input, Endian>> +where + Endian: Endianity, +{ + /// Construct a new `DebugLoc` instance from the data in the `.debug_loc` + /// section. + /// + /// It is the caller's responsibility to read the `.debug_loc` section and + /// present it as a `&[u8]` slice. That means using some ELF loader on + /// Linux, a Mach-O loader on macOS, etc. + /// + /// ``` + /// use gimli::{DebugLoc, LittleEndian}; + /// + /// # let buf = [0x00, 0x01, 0x02, 0x03]; + /// # let read_debug_loc_section_somehow = || &buf; + /// let debug_loc = DebugLoc::new(read_debug_loc_section_somehow(), LittleEndian); + /// ``` + pub fn new(section: &'input [u8], endian: Endian) -> Self { + Self::from(EndianSlice::new(section, endian)) + } +} + +impl<R> Section<R> for DebugLoc<R> { + fn id() -> SectionId { + SectionId::DebugLoc + } + + fn reader(&self) -> &R { + &self.section + } +} + +impl<R> From<R> for DebugLoc<R> { + fn from(section: R) -> Self { + DebugLoc { section } + } +} + +/// The `DebugLocLists` struct represents the DWARF data +/// found in the `.debug_loclists` section. +#[derive(Debug, Default, Clone, Copy)] +pub struct DebugLocLists<R> { + section: R, +} + +impl<'input, Endian> DebugLocLists<EndianSlice<'input, Endian>> +where + Endian: Endianity, +{ + /// Construct a new `DebugLocLists` instance from the data in the `.debug_loclists` + /// section. + /// + /// It is the caller's responsibility to read the `.debug_loclists` section and + /// present it as a `&[u8]` slice. That means using some ELF loader on + /// Linux, a Mach-O loader on macOS, etc. + /// + /// ``` + /// use gimli::{DebugLocLists, LittleEndian}; + /// + /// # let buf = [0x00, 0x01, 0x02, 0x03]; + /// # let read_debug_loclists_section_somehow = || &buf; + /// let debug_loclists = DebugLocLists::new(read_debug_loclists_section_somehow(), LittleEndian); + /// ``` + pub fn new(section: &'input [u8], endian: Endian) -> Self { + Self::from(EndianSlice::new(section, endian)) + } +} + +impl<R> Section<R> for DebugLocLists<R> { + fn id() -> SectionId { + SectionId::DebugLocLists + } + + fn reader(&self) -> &R { + &self.section + } +} + +impl<R> From<R> for DebugLocLists<R> { + fn from(section: R) -> Self { + DebugLocLists { section } + } +} + +pub(crate) type LocListsHeader = ListsHeader; + +impl<Offset> DebugLocListsBase<Offset> +where + Offset: ReaderOffset, +{ + /// Returns a `DebugLocListsBase` with the default value of DW_AT_loclists_base + /// for the given `Encoding` and `DwarfFileType`. + pub fn default_for_encoding_and_file( + encoding: Encoding, + file_type: DwarfFileType, + ) -> DebugLocListsBase<Offset> { + if encoding.version >= 5 && file_type == DwarfFileType::Dwo { + // In .dwo files, the compiler omits the DW_AT_loclists_base attribute (because there is + // only a single unit in the file) but we must skip past the header, which the attribute + // would normally do for us. + DebugLocListsBase(Offset::from_u8(LocListsHeader::size_for_encoding(encoding))) + } else { + DebugLocListsBase(Offset::from_u8(0)) + } + } +} + +/// The DWARF data found in `.debug_loc` and `.debug_loclists` sections. +#[derive(Debug, Default, Clone, Copy)] +pub struct LocationLists<R> { + debug_loc: DebugLoc<R>, + debug_loclists: DebugLocLists<R>, +} + +impl<R> LocationLists<R> { + /// Construct a new `LocationLists` instance from the data in the `.debug_loc` and + /// `.debug_loclists` sections. + pub fn new(debug_loc: DebugLoc<R>, debug_loclists: DebugLocLists<R>) -> LocationLists<R> { + LocationLists { + debug_loc, + debug_loclists, + } + } +} + +impl<T> LocationLists<T> { + /// Create a `LocationLists` that references the data in `self`. + /// + /// This is useful when `R` implements `Reader` but `T` does not. + /// + /// ## Example Usage + /// + /// ```rust,no_run + /// # let load_section = || unimplemented!(); + /// // Read the DWARF section into a `Vec` with whatever object loader you're using. + /// let owned_section: gimli::LocationLists<Vec<u8>> = load_section(); + /// // Create a reference to the DWARF section. + /// let section = owned_section.borrow(|section| { + /// gimli::EndianSlice::new(§ion, gimli::LittleEndian) + /// }); + /// ``` + pub fn borrow<'a, F, R>(&'a self, mut borrow: F) -> LocationLists<R> + where + F: FnMut(&'a T) -> R, + { + LocationLists { + debug_loc: borrow(&self.debug_loc.section).into(), + debug_loclists: borrow(&self.debug_loclists.section).into(), + } + } +} + +impl<R: Reader> LocationLists<R> { + /// Iterate over the `LocationListEntry`s starting at the given offset. + /// + /// The `unit_encoding` must match the compilation unit that the + /// offset was contained in. + /// + /// The `base_address` should be obtained from the `DW_AT_low_pc` attribute in the + /// `DW_TAG_compile_unit` entry for the compilation unit that contains this location + /// list. + /// + /// Can be [used with + /// `FallibleIterator`](./index.html#using-with-fallibleiterator). + pub fn locations( + &self, + offset: LocationListsOffset<R::Offset>, + unit_encoding: Encoding, + base_address: u64, + debug_addr: &DebugAddr<R>, + debug_addr_base: DebugAddrBase<R::Offset>, + ) -> Result<LocListIter<R>> { + Ok(LocListIter::new( + self.raw_locations(offset, unit_encoding)?, + base_address, + debug_addr.clone(), + debug_addr_base, + )) + } + + /// Similar to `locations`, but with special handling for .dwo files. + /// This should only been used when this `LocationLists` was loaded from a + /// .dwo file. + pub fn locations_dwo( + &self, + offset: LocationListsOffset<R::Offset>, + unit_encoding: Encoding, + base_address: u64, + debug_addr: &DebugAddr<R>, + debug_addr_base: DebugAddrBase<R::Offset>, + ) -> Result<LocListIter<R>> { + Ok(LocListIter::new( + self.raw_locations_dwo(offset, unit_encoding)?, + base_address, + debug_addr.clone(), + debug_addr_base, + )) + } + + /// Iterate over the raw `LocationListEntry`s starting at the given offset. + /// + /// The `unit_encoding` must match the compilation unit that the + /// offset was contained in. + /// + /// This iterator does not perform any processing of the location entries, + /// such as handling base addresses. + /// + /// Can be [used with + /// `FallibleIterator`](./index.html#using-with-fallibleiterator). + pub fn raw_locations( + &self, + offset: LocationListsOffset<R::Offset>, + unit_encoding: Encoding, + ) -> Result<RawLocListIter<R>> { + let (mut input, format) = if unit_encoding.version <= 4 { + (self.debug_loc.section.clone(), LocListsFormat::Bare) + } else { + (self.debug_loclists.section.clone(), LocListsFormat::Lle) + }; + input.skip(offset.0)?; + Ok(RawLocListIter::new(input, unit_encoding, format)) + } + + /// Similar to `raw_locations`, but with special handling for .dwo files. + /// This should only been used when this `LocationLists` was loaded from a + /// .dwo file. + pub fn raw_locations_dwo( + &self, + offset: LocationListsOffset<R::Offset>, + unit_encoding: Encoding, + ) -> Result<RawLocListIter<R>> { + let mut input = if unit_encoding.version <= 4 { + // In the GNU split dwarf extension the locations are present in the + // .debug_loc section but are encoded with the DW_LLE values used + // for the DWARF 5 .debug_loclists section. + self.debug_loc.section.clone() + } else { + self.debug_loclists.section.clone() + }; + input.skip(offset.0)?; + Ok(RawLocListIter::new( + input, + unit_encoding, + LocListsFormat::Lle, + )) + } + + /// Returns the `.debug_loclists` offset at the given `base` and `index`. + /// + /// The `base` must be the `DW_AT_loclists_base` value from the compilation unit DIE. + /// This is an offset that points to the first entry following the header. + /// + /// The `index` is the value of a `DW_FORM_loclistx` attribute. + pub fn get_offset( + &self, + unit_encoding: Encoding, + base: DebugLocListsBase<R::Offset>, + index: DebugLocListsIndex<R::Offset>, + ) -> Result<LocationListsOffset<R::Offset>> { + let format = unit_encoding.format; + let input = &mut self.debug_loclists.section.clone(); + input.skip(base.0)?; + input.skip(R::Offset::from_u64( + index.0.into_u64() * u64::from(format.word_size()), + )?)?; + input + .read_offset(format) + .map(|x| LocationListsOffset(base.0 + x)) + } + + /// Call `Reader::lookup_offset_id` for each section, and return the first match. + pub fn lookup_offset_id(&self, id: ReaderOffsetId) -> Option<(SectionId, R::Offset)> { + self.debug_loc + .lookup_offset_id(id) + .or_else(|| self.debug_loclists.lookup_offset_id(id)) + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum LocListsFormat { + /// The bare location list format used before DWARF 5. + Bare, + /// The DW_LLE encoded range list format used in DWARF 5 and the non-standard GNU + /// split dwarf extension. + Lle, +} + +/// A raw iterator over a location list. +/// +/// This iterator does not perform any processing of the location entries, +/// such as handling base addresses. +#[derive(Debug)] +pub struct RawLocListIter<R: Reader> { + input: R, + encoding: Encoding, + format: LocListsFormat, +} + +/// A raw entry in .debug_loclists. +#[derive(Clone, Debug)] +pub enum RawLocListEntry<R: Reader> { + /// A location from DWARF version <= 4. + AddressOrOffsetPair { + /// Start of range. May be an address or an offset. + begin: u64, + /// End of range. May be an address or an offset. + end: u64, + /// expression + data: Expression<R>, + }, + /// DW_LLE_base_address + BaseAddress { + /// base address + addr: u64, + }, + /// DW_LLE_base_addressx + BaseAddressx { + /// base address + addr: DebugAddrIndex<R::Offset>, + }, + /// DW_LLE_startx_endx + StartxEndx { + /// start of range + begin: DebugAddrIndex<R::Offset>, + /// end of range + end: DebugAddrIndex<R::Offset>, + /// expression + data: Expression<R>, + }, + /// DW_LLE_startx_length + StartxLength { + /// start of range + begin: DebugAddrIndex<R::Offset>, + /// length of range + length: u64, + /// expression + data: Expression<R>, + }, + /// DW_LLE_offset_pair + OffsetPair { + /// start of range + begin: u64, + /// end of range + end: u64, + /// expression + data: Expression<R>, + }, + /// DW_LLE_default_location + DefaultLocation { + /// expression + data: Expression<R>, + }, + /// DW_LLE_start_end + StartEnd { + /// start of range + begin: u64, + /// end of range + end: u64, + /// expression + data: Expression<R>, + }, + /// DW_LLE_start_length + StartLength { + /// start of range + begin: u64, + /// length of range + length: u64, + /// expression + data: Expression<R>, + }, +} + +fn parse_data<R: Reader>(input: &mut R, encoding: Encoding) -> Result<Expression<R>> { + if encoding.version >= 5 { + let len = R::Offset::from_u64(input.read_uleb128()?)?; + Ok(Expression(input.split(len)?)) + } else { + // In the GNU split-dwarf extension this is a fixed 2 byte value. + let len = R::Offset::from_u16(input.read_u16()?); + Ok(Expression(input.split(len)?)) + } +} + +impl<R: Reader> RawLocListEntry<R> { + /// Parse a location list entry from `.debug_loclists` + fn parse(input: &mut R, encoding: Encoding, format: LocListsFormat) -> Result<Option<Self>> { + Ok(match format { + LocListsFormat::Bare => { + let range = RawRange::parse(input, encoding.address_size)?; + if range.is_end() { + None + } else if range.is_base_address(encoding.address_size) { + Some(RawLocListEntry::BaseAddress { addr: range.end }) + } else { + let len = R::Offset::from_u16(input.read_u16()?); + let data = Expression(input.split(len)?); + Some(RawLocListEntry::AddressOrOffsetPair { + begin: range.begin, + end: range.end, + data, + }) + } + } + LocListsFormat::Lle => match constants::DwLle(input.read_u8()?) { + constants::DW_LLE_end_of_list => None, + constants::DW_LLE_base_addressx => Some(RawLocListEntry::BaseAddressx { + addr: DebugAddrIndex(input.read_uleb128().and_then(R::Offset::from_u64)?), + }), + constants::DW_LLE_startx_endx => Some(RawLocListEntry::StartxEndx { + begin: DebugAddrIndex(input.read_uleb128().and_then(R::Offset::from_u64)?), + end: DebugAddrIndex(input.read_uleb128().and_then(R::Offset::from_u64)?), + data: parse_data(input, encoding)?, + }), + constants::DW_LLE_startx_length => Some(RawLocListEntry::StartxLength { + begin: DebugAddrIndex(input.read_uleb128().and_then(R::Offset::from_u64)?), + length: if encoding.version >= 5 { + input.read_uleb128()? + } else { + // In the GNU split-dwarf extension this is a fixed 4 byte value. + input.read_u32()? as u64 + }, + data: parse_data(input, encoding)?, + }), + constants::DW_LLE_offset_pair => Some(RawLocListEntry::OffsetPair { + begin: input.read_uleb128()?, + end: input.read_uleb128()?, + data: parse_data(input, encoding)?, + }), + constants::DW_LLE_default_location => Some(RawLocListEntry::DefaultLocation { + data: parse_data(input, encoding)?, + }), + constants::DW_LLE_base_address => Some(RawLocListEntry::BaseAddress { + addr: input.read_address(encoding.address_size)?, + }), + constants::DW_LLE_start_end => Some(RawLocListEntry::StartEnd { + begin: input.read_address(encoding.address_size)?, + end: input.read_address(encoding.address_size)?, + data: parse_data(input, encoding)?, + }), + constants::DW_LLE_start_length => Some(RawLocListEntry::StartLength { + begin: input.read_address(encoding.address_size)?, + length: input.read_uleb128()?, + data: parse_data(input, encoding)?, + }), + _ => { + return Err(Error::InvalidAddressRange); + } + }, + }) + } +} + +impl<R: Reader> RawLocListIter<R> { + /// Construct a `RawLocListIter`. + fn new(input: R, encoding: Encoding, format: LocListsFormat) -> RawLocListIter<R> { + RawLocListIter { + input, + encoding, + format, + } + } + + /// Advance the iterator to the next location. + pub fn next(&mut self) -> Result<Option<RawLocListEntry<R>>> { + if self.input.is_empty() { + return Ok(None); + } + + match RawLocListEntry::parse(&mut self.input, self.encoding, self.format) { + Ok(entry) => { + if entry.is_none() { + self.input.empty(); + } + Ok(entry) + } + Err(e) => { + self.input.empty(); + Err(e) + } + } + } +} + +#[cfg(feature = "fallible-iterator")] +impl<R: Reader> fallible_iterator::FallibleIterator for RawLocListIter<R> { + type Item = RawLocListEntry<R>; + type Error = Error; + + fn next(&mut self) -> ::core::result::Result<Option<Self::Item>, Self::Error> { + RawLocListIter::next(self) + } +} + +/// An iterator over a location list. +/// +/// This iterator internally handles processing of base address selection entries +/// and list end entries. Thus, it only returns location entries that are valid +/// and already adjusted for the base address. +#[derive(Debug)] +pub struct LocListIter<R: Reader> { + raw: RawLocListIter<R>, + base_address: u64, + debug_addr: DebugAddr<R>, + debug_addr_base: DebugAddrBase<R::Offset>, +} + +impl<R: Reader> LocListIter<R> { + /// Construct a `LocListIter`. + fn new( + raw: RawLocListIter<R>, + base_address: u64, + debug_addr: DebugAddr<R>, + debug_addr_base: DebugAddrBase<R::Offset>, + ) -> LocListIter<R> { + LocListIter { + raw, + base_address, + debug_addr, + debug_addr_base, + } + } + + #[inline] + fn get_address(&self, index: DebugAddrIndex<R::Offset>) -> Result<u64> { + self.debug_addr + .get_address(self.raw.encoding.address_size, self.debug_addr_base, index) + } + + /// Advance the iterator to the next location. + pub fn next(&mut self) -> Result<Option<LocationListEntry<R>>> { + loop { + let raw_loc = match self.raw.next()? { + Some(loc) => loc, + None => return Ok(None), + }; + + let loc = self.convert_raw(raw_loc)?; + if loc.is_some() { + return Ok(loc); + } + } + } + + /// Return the next raw location. + /// + /// The raw location should be passed to `convert_raw`. + #[doc(hidden)] + pub fn next_raw(&mut self) -> Result<Option<RawLocListEntry<R>>> { + self.raw.next() + } + + /// Convert a raw location into a location, and update the state of the iterator. + /// + /// The raw location should have been obtained from `next_raw`. + #[doc(hidden)] + pub fn convert_raw( + &mut self, + raw_loc: RawLocListEntry<R>, + ) -> Result<Option<LocationListEntry<R>>> { + let mask = !0 >> (64 - self.raw.encoding.address_size * 8); + let tombstone = if self.raw.encoding.version <= 4 { + mask - 1 + } else { + mask + }; + + let (range, data) = match raw_loc { + RawLocListEntry::BaseAddress { addr } => { + self.base_address = addr; + return Ok(None); + } + RawLocListEntry::BaseAddressx { addr } => { + self.base_address = self.get_address(addr)?; + return Ok(None); + } + RawLocListEntry::StartxEndx { begin, end, data } => { + let begin = self.get_address(begin)?; + let end = self.get_address(end)?; + (Range { begin, end }, data) + } + RawLocListEntry::StartxLength { + begin, + length, + data, + } => { + let begin = self.get_address(begin)?; + let end = begin.wrapping_add(length) & mask; + (Range { begin, end }, data) + } + RawLocListEntry::DefaultLocation { data } => ( + Range { + begin: 0, + end: u64::max_value(), + }, + data, + ), + RawLocListEntry::AddressOrOffsetPair { begin, end, data } + | RawLocListEntry::OffsetPair { begin, end, data } => { + if self.base_address == tombstone { + return Ok(None); + } + let mut range = Range { begin, end }; + range.add_base_address(self.base_address, self.raw.encoding.address_size); + (range, data) + } + RawLocListEntry::StartEnd { begin, end, data } => (Range { begin, end }, data), + RawLocListEntry::StartLength { + begin, + length, + data, + } => { + let end = begin.wrapping_add(length) & mask; + (Range { begin, end }, data) + } + }; + + if range.begin == tombstone { + return Ok(None); + } + + if range.begin > range.end { + self.raw.input.empty(); + return Err(Error::InvalidLocationAddressRange); + } + + Ok(Some(LocationListEntry { range, data })) + } +} + +#[cfg(feature = "fallible-iterator")] +impl<R: Reader> fallible_iterator::FallibleIterator for LocListIter<R> { + type Item = LocationListEntry<R>; + type Error = Error; + + fn next(&mut self) -> ::core::result::Result<Option<Self::Item>, Self::Error> { + LocListIter::next(self) + } +} + +/// A location list entry from the `.debug_loc` or `.debug_loclists` sections. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct LocationListEntry<R: Reader> { + /// The address range that this location is valid for. + pub range: Range, + + /// The data containing a single location description. + pub data: Expression<R>, +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::common::Format; + use crate::endianity::LittleEndian; + use crate::read::{EndianSlice, Range}; + use crate::test_util::GimliSectionMethods; + use test_assembler::{Endian, Label, LabelMaker, Section}; + + #[test] + fn test_loclists_32() { + let tombstone = !0u32; + let encoding = Encoding { + format: Format::Dwarf32, + version: 5, + address_size: 4, + }; + + let section = Section::with_endian(Endian::Little) + .L32(0x0300_0000) + .L32(0x0301_0300) + .L32(0x0301_0400) + .L32(0x0301_0500) + .L32(tombstone) + .L32(0x0301_0600); + let buf = section.get_contents().unwrap(); + let debug_addr = &DebugAddr::from(EndianSlice::new(&buf, LittleEndian)); + let debug_addr_base = DebugAddrBase(0); + + let start = Label::new(); + let first = Label::new(); + let size = Label::new(); + #[rustfmt::skip] + let section = Section::with_endian(Endian::Little) + // Header + .mark(&start) + .L32(&size) + .L16(encoding.version) + .L8(encoding.address_size) + .L8(0) + .L32(0) + .mark(&first) + // OffsetPair + .L8(4).uleb(0x10200).uleb(0x10300).uleb(4).L32(2) + // A base address selection followed by an OffsetPair. + .L8(6).L32(0x0200_0000) + .L8(4).uleb(0x10400).uleb(0x10500).uleb(4).L32(3) + // An empty OffsetPair followed by a normal OffsetPair. + .L8(4).uleb(0x10600).uleb(0x10600).uleb(4).L32(4) + .L8(4).uleb(0x10800).uleb(0x10900).uleb(4).L32(5) + // A StartEnd + .L8(7).L32(0x201_0a00).L32(0x201_0b00).uleb(4).L32(6) + // A StartLength + .L8(8).L32(0x201_0c00).uleb(0x100).uleb(4).L32(7) + // An OffsetPair that starts at 0. + .L8(4).uleb(0).uleb(1).uleb(4).L32(8) + // An OffsetPair that ends at -1. + .L8(6).L32(0) + .L8(4).uleb(0).uleb(0xffff_ffff).uleb(4).L32(9) + // A DefaultLocation + .L8(5).uleb(4).L32(10) + // A BaseAddressx + OffsetPair + .L8(1).uleb(0) + .L8(4).uleb(0x10100).uleb(0x10200).uleb(4).L32(11) + // A StartxEndx + .L8(2).uleb(1).uleb(2).uleb(4).L32(12) + // A StartxLength + .L8(3).uleb(3).uleb(0x100).uleb(4).L32(13) + + // Tombstone entries, all of which should be ignored. + // A BaseAddressx that is a tombstone. + .L8(1).uleb(4) + .L8(4).uleb(0x11100).uleb(0x11200).uleb(4).L32(20) + // A BaseAddress that is a tombstone. + .L8(6).L32(tombstone) + .L8(4).uleb(0x11300).uleb(0x11400).uleb(4).L32(21) + // A StartxEndx that is a tombstone. + .L8(2).uleb(4).uleb(5).uleb(4).L32(22) + // A StartxLength that is a tombstone. + .L8(3).uleb(4).uleb(0x100).uleb(4).L32(23) + // A StartEnd that is a tombstone. + .L8(7).L32(tombstone).L32(0x201_1500).uleb(4).L32(24) + // A StartLength that is a tombstone. + .L8(8).L32(tombstone).uleb(0x100).uleb(4).L32(25) + // A StartEnd (not ignored) + .L8(7).L32(0x201_1600).L32(0x201_1700).uleb(4).L32(26) + + // A range end. + .L8(0) + // Some extra data. + .L32(0xffff_ffff); + size.set_const((§ion.here() - &start - 4) as u64); + + let buf = section.get_contents().unwrap(); + let debug_loc = DebugLoc::new(&[], LittleEndian); + let debug_loclists = DebugLocLists::new(&buf, LittleEndian); + let loclists = LocationLists::new(debug_loc, debug_loclists); + let offset = LocationListsOffset((&first - &start) as usize); + let mut locations = loclists + .locations(offset, encoding, 0x0100_0000, debug_addr, debug_addr_base) + .unwrap(); + + // A normal location. + assert_eq!( + locations.next(), + Ok(Some(LocationListEntry { + range: Range { + begin: 0x0101_0200, + end: 0x0101_0300, + }, + data: Expression(EndianSlice::new(&[2, 0, 0, 0], LittleEndian)), + })) + ); + + // A base address selection followed by a normal location. + assert_eq!( + locations.next(), + Ok(Some(LocationListEntry { + range: Range { + begin: 0x0201_0400, + end: 0x0201_0500, + }, + data: Expression(EndianSlice::new(&[3, 0, 0, 0], LittleEndian)), + })) + ); + + // An empty location range followed by a normal location. + assert_eq!( + locations.next(), + Ok(Some(LocationListEntry { + range: Range { + begin: 0x0201_0600, + end: 0x0201_0600, + }, + data: Expression(EndianSlice::new(&[4, 0, 0, 0], LittleEndian)), + })) + ); + assert_eq!( + locations.next(), + Ok(Some(LocationListEntry { + range: Range { + begin: 0x0201_0800, + end: 0x0201_0900, + }, + data: Expression(EndianSlice::new(&[5, 0, 0, 0], LittleEndian)), + })) + ); + + // A normal location. + assert_eq!( + locations.next(), + Ok(Some(LocationListEntry { + range: Range { + begin: 0x0201_0a00, + end: 0x0201_0b00, + }, + data: Expression(EndianSlice::new(&[6, 0, 0, 0], LittleEndian)), + })) + ); + + // A normal location. + assert_eq!( + locations.next(), + Ok(Some(LocationListEntry { + range: Range { + begin: 0x0201_0c00, + end: 0x0201_0d00, + }, + data: Expression(EndianSlice::new(&[7, 0, 0, 0], LittleEndian)), + })) + ); + + // A location range that starts at 0. + assert_eq!( + locations.next(), + Ok(Some(LocationListEntry { + range: Range { + begin: 0x0200_0000, + end: 0x0200_0001, + }, + data: Expression(EndianSlice::new(&[8, 0, 0, 0], LittleEndian)), + })) + ); + + // A location range that ends at -1. + assert_eq!( + locations.next(), + Ok(Some(LocationListEntry { + range: Range { + begin: 0x0000_0000, + end: 0xffff_ffff, + }, + data: Expression(EndianSlice::new(&[9, 0, 0, 0], LittleEndian)), + })) + ); + + // A DefaultLocation. + assert_eq!( + locations.next(), + Ok(Some(LocationListEntry { + range: Range { + begin: 0, + end: u64::max_value(), + }, + data: Expression(EndianSlice::new(&[10, 0, 0, 0], LittleEndian)), + })) + ); + + // A BaseAddressx + OffsetPair + assert_eq!( + locations.next(), + Ok(Some(LocationListEntry { + range: Range { + begin: 0x0301_0100, + end: 0x0301_0200, + }, + data: Expression(EndianSlice::new(&[11, 0, 0, 0], LittleEndian)), + })) + ); + + // A StartxEndx + assert_eq!( + locations.next(), + Ok(Some(LocationListEntry { + range: Range { + begin: 0x0301_0300, + end: 0x0301_0400, + }, + data: Expression(EndianSlice::new(&[12, 0, 0, 0], LittleEndian)), + })) + ); + + // A StartxLength + assert_eq!( + locations.next(), + Ok(Some(LocationListEntry { + range: Range { + begin: 0x0301_0500, + end: 0x0301_0600, + }, + data: Expression(EndianSlice::new(&[13, 0, 0, 0], LittleEndian)), + })) + ); + + // A StartEnd location following the tombstones + assert_eq!( + locations.next(), + Ok(Some(LocationListEntry { + range: Range { + begin: 0x0201_1600, + end: 0x0201_1700, + }, + data: Expression(EndianSlice::new(&[26, 0, 0, 0], LittleEndian)), + })) + ); + + // A location list end. + assert_eq!(locations.next(), Ok(None)); + + // An offset at the end of buf. + let mut locations = loclists + .locations( + LocationListsOffset(buf.len()), + encoding, + 0x0100_0000, + debug_addr, + debug_addr_base, + ) + .unwrap(); + assert_eq!(locations.next(), Ok(None)); + } + + #[test] + fn test_loclists_64() { + let tombstone = !0u64; + let encoding = Encoding { + format: Format::Dwarf64, + version: 5, + address_size: 8, + }; + + let section = Section::with_endian(Endian::Little) + .L64(0x0300_0000) + .L64(0x0301_0300) + .L64(0x0301_0400) + .L64(0x0301_0500) + .L64(tombstone) + .L64(0x0301_0600); + let buf = section.get_contents().unwrap(); + let debug_addr = &DebugAddr::from(EndianSlice::new(&buf, LittleEndian)); + let debug_addr_base = DebugAddrBase(0); + + let start = Label::new(); + let first = Label::new(); + let size = Label::new(); + #[rustfmt::skip] + let section = Section::with_endian(Endian::Little) + // Header + .mark(&start) + .L32(0xffff_ffff) + .L64(&size) + .L16(encoding.version) + .L8(encoding.address_size) + .L8(0) + .L32(0) + .mark(&first) + // OffsetPair + .L8(4).uleb(0x10200).uleb(0x10300).uleb(4).L32(2) + // A base address selection followed by an OffsetPair. + .L8(6).L64(0x0200_0000) + .L8(4).uleb(0x10400).uleb(0x10500).uleb(4).L32(3) + // An empty OffsetPair followed by a normal OffsetPair. + .L8(4).uleb(0x10600).uleb(0x10600).uleb(4).L32(4) + .L8(4).uleb(0x10800).uleb(0x10900).uleb(4).L32(5) + // A StartEnd + .L8(7).L64(0x201_0a00).L64(0x201_0b00).uleb(4).L32(6) + // A StartLength + .L8(8).L64(0x201_0c00).uleb(0x100).uleb(4).L32(7) + // An OffsetPair that starts at 0. + .L8(4).uleb(0).uleb(1).uleb(4).L32(8) + // An OffsetPair that ends at -1. + .L8(6).L64(0) + .L8(4).uleb(0).uleb(0xffff_ffff).uleb(4).L32(9) + // A DefaultLocation + .L8(5).uleb(4).L32(10) + // A BaseAddressx + OffsetPair + .L8(1).uleb(0) + .L8(4).uleb(0x10100).uleb(0x10200).uleb(4).L32(11) + // A StartxEndx + .L8(2).uleb(1).uleb(2).uleb(4).L32(12) + // A StartxLength + .L8(3).uleb(3).uleb(0x100).uleb(4).L32(13) + + // Tombstone entries, all of which should be ignored. + // A BaseAddressx that is a tombstone. + .L8(1).uleb(4) + .L8(4).uleb(0x11100).uleb(0x11200).uleb(4).L32(20) + // A BaseAddress that is a tombstone. + .L8(6).L64(tombstone) + .L8(4).uleb(0x11300).uleb(0x11400).uleb(4).L32(21) + // A StartxEndx that is a tombstone. + .L8(2).uleb(4).uleb(5).uleb(4).L32(22) + // A StartxLength that is a tombstone. + .L8(3).uleb(4).uleb(0x100).uleb(4).L32(23) + // A StartEnd that is a tombstone. + .L8(7).L64(tombstone).L64(0x201_1500).uleb(4).L32(24) + // A StartLength that is a tombstone. + .L8(8).L64(tombstone).uleb(0x100).uleb(4).L32(25) + // A StartEnd (not ignored) + .L8(7).L64(0x201_1600).L64(0x201_1700).uleb(4).L32(26) + + // A range end. + .L8(0) + // Some extra data. + .L32(0xffff_ffff); + size.set_const((§ion.here() - &start - 12) as u64); + + let buf = section.get_contents().unwrap(); + let debug_loc = DebugLoc::new(&[], LittleEndian); + let debug_loclists = DebugLocLists::new(&buf, LittleEndian); + let loclists = LocationLists::new(debug_loc, debug_loclists); + let offset = LocationListsOffset((&first - &start) as usize); + let mut locations = loclists + .locations(offset, encoding, 0x0100_0000, debug_addr, debug_addr_base) + .unwrap(); + + // A normal location. + assert_eq!( + locations.next(), + Ok(Some(LocationListEntry { + range: Range { + begin: 0x0101_0200, + end: 0x0101_0300, + }, + data: Expression(EndianSlice::new(&[2, 0, 0, 0], LittleEndian)), + })) + ); + + // A base address selection followed by a normal location. + assert_eq!( + locations.next(), + Ok(Some(LocationListEntry { + range: Range { + begin: 0x0201_0400, + end: 0x0201_0500, + }, + data: Expression(EndianSlice::new(&[3, 0, 0, 0], LittleEndian)), + })) + ); + + // An empty location range followed by a normal location. + assert_eq!( + locations.next(), + Ok(Some(LocationListEntry { + range: Range { + begin: 0x0201_0600, + end: 0x0201_0600, + }, + data: Expression(EndianSlice::new(&[4, 0, 0, 0], LittleEndian)), + })) + ); + assert_eq!( + locations.next(), + Ok(Some(LocationListEntry { + range: Range { + begin: 0x0201_0800, + end: 0x0201_0900, + }, + data: Expression(EndianSlice::new(&[5, 0, 0, 0], LittleEndian)), + })) + ); + + // A normal location. + assert_eq!( + locations.next(), + Ok(Some(LocationListEntry { + range: Range { + begin: 0x0201_0a00, + end: 0x0201_0b00, + }, + data: Expression(EndianSlice::new(&[6, 0, 0, 0], LittleEndian)), + })) + ); + + // A normal location. + assert_eq!( + locations.next(), + Ok(Some(LocationListEntry { + range: Range { + begin: 0x0201_0c00, + end: 0x0201_0d00, + }, + data: Expression(EndianSlice::new(&[7, 0, 0, 0], LittleEndian)), + })) + ); + + // A location range that starts at 0. + assert_eq!( + locations.next(), + Ok(Some(LocationListEntry { + range: Range { + begin: 0x0200_0000, + end: 0x0200_0001, + }, + data: Expression(EndianSlice::new(&[8, 0, 0, 0], LittleEndian)), + })) + ); + + // A location range that ends at -1. + assert_eq!( + locations.next(), + Ok(Some(LocationListEntry { + range: Range { + begin: 0x0000_0000, + end: 0xffff_ffff, + }, + data: Expression(EndianSlice::new(&[9, 0, 0, 0], LittleEndian)), + })) + ); + + // A DefaultLocation. + assert_eq!( + locations.next(), + Ok(Some(LocationListEntry { + range: Range { + begin: 0, + end: u64::max_value(), + }, + data: Expression(EndianSlice::new(&[10, 0, 0, 0], LittleEndian)), + })) + ); + + // A BaseAddressx + OffsetPair + assert_eq!( + locations.next(), + Ok(Some(LocationListEntry { + range: Range { + begin: 0x0301_0100, + end: 0x0301_0200, + }, + data: Expression(EndianSlice::new(&[11, 0, 0, 0], LittleEndian)), + })) + ); + + // A StartxEndx + assert_eq!( + locations.next(), + Ok(Some(LocationListEntry { + range: Range { + begin: 0x0301_0300, + end: 0x0301_0400, + }, + data: Expression(EndianSlice::new(&[12, 0, 0, 0], LittleEndian)), + })) + ); + + // A StartxLength + assert_eq!( + locations.next(), + Ok(Some(LocationListEntry { + range: Range { + begin: 0x0301_0500, + end: 0x0301_0600, + }, + data: Expression(EndianSlice::new(&[13, 0, 0, 0], LittleEndian)), + })) + ); + + // A StartEnd location following the tombstones + assert_eq!( + locations.next(), + Ok(Some(LocationListEntry { + range: Range { + begin: 0x0201_1600, + end: 0x0201_1700, + }, + data: Expression(EndianSlice::new(&[26, 0, 0, 0], LittleEndian)), + })) + ); + + // A location list end. + assert_eq!(locations.next(), Ok(None)); + + // An offset at the end of buf. + let mut locations = loclists + .locations( + LocationListsOffset(buf.len()), + encoding, + 0x0100_0000, + debug_addr, + debug_addr_base, + ) + .unwrap(); + assert_eq!(locations.next(), Ok(None)); + } + + #[test] + fn test_location_list_32() { + let tombstone = !0u32 - 1; + let start = Label::new(); + let first = Label::new(); + #[rustfmt::skip] + let section = Section::with_endian(Endian::Little) + // A location before the offset. + .mark(&start) + .L32(0x10000).L32(0x10100).L16(4).L32(1) + .mark(&first) + // A normal location. + .L32(0x10200).L32(0x10300).L16(4).L32(2) + // A base address selection followed by a normal location. + .L32(0xffff_ffff).L32(0x0200_0000) + .L32(0x10400).L32(0x10500).L16(4).L32(3) + // An empty location range followed by a normal location. + .L32(0x10600).L32(0x10600).L16(4).L32(4) + .L32(0x10800).L32(0x10900).L16(4).L32(5) + // A location range that starts at 0. + .L32(0).L32(1).L16(4).L32(6) + // A location range that ends at -1. + .L32(0xffff_ffff).L32(0x0000_0000) + .L32(0).L32(0xffff_ffff).L16(4).L32(7) + // A normal location with tombstone. + .L32(tombstone).L32(tombstone).L16(4).L32(8) + // A base address selection with tombstone followed by a normal location. + .L32(0xffff_ffff).L32(tombstone) + .L32(0x10a00).L32(0x10b00).L16(4).L32(9) + // A location list end. + .L32(0).L32(0) + // Some extra data. + .L32(0); + + let buf = section.get_contents().unwrap(); + let debug_loc = DebugLoc::new(&buf, LittleEndian); + let debug_loclists = DebugLocLists::new(&[], LittleEndian); + let loclists = LocationLists::new(debug_loc, debug_loclists); + let offset = LocationListsOffset((&first - &start) as usize); + let debug_addr = &DebugAddr::from(EndianSlice::new(&[], LittleEndian)); + let debug_addr_base = DebugAddrBase(0); + let encoding = Encoding { + format: Format::Dwarf32, + version: 4, + address_size: 4, + }; + let mut locations = loclists + .locations(offset, encoding, 0x0100_0000, debug_addr, debug_addr_base) + .unwrap(); + + // A normal location. + assert_eq!( + locations.next(), + Ok(Some(LocationListEntry { + range: Range { + begin: 0x0101_0200, + end: 0x0101_0300, + }, + data: Expression(EndianSlice::new(&[2, 0, 0, 0], LittleEndian)), + })) + ); + + // A base address selection followed by a normal location. + assert_eq!( + locations.next(), + Ok(Some(LocationListEntry { + range: Range { + begin: 0x0201_0400, + end: 0x0201_0500, + }, + data: Expression(EndianSlice::new(&[3, 0, 0, 0], LittleEndian)), + })) + ); + + // An empty location range followed by a normal location. + assert_eq!( + locations.next(), + Ok(Some(LocationListEntry { + range: Range { + begin: 0x0201_0600, + end: 0x0201_0600, + }, + data: Expression(EndianSlice::new(&[4, 0, 0, 0], LittleEndian)), + })) + ); + assert_eq!( + locations.next(), + Ok(Some(LocationListEntry { + range: Range { + begin: 0x0201_0800, + end: 0x0201_0900, + }, + data: Expression(EndianSlice::new(&[5, 0, 0, 0], LittleEndian)), + })) + ); + + // A location range that starts at 0. + assert_eq!( + locations.next(), + Ok(Some(LocationListEntry { + range: Range { + begin: 0x0200_0000, + end: 0x0200_0001, + }, + data: Expression(EndianSlice::new(&[6, 0, 0, 0], LittleEndian)), + })) + ); + + // A location range that ends at -1. + assert_eq!( + locations.next(), + Ok(Some(LocationListEntry { + range: Range { + begin: 0x0000_0000, + end: 0xffff_ffff, + }, + data: Expression(EndianSlice::new(&[7, 0, 0, 0], LittleEndian)), + })) + ); + + // A location list end. + assert_eq!(locations.next(), Ok(None)); + + // An offset at the end of buf. + let mut locations = loclists + .locations( + LocationListsOffset(buf.len()), + encoding, + 0x0100_0000, + debug_addr, + debug_addr_base, + ) + .unwrap(); + assert_eq!(locations.next(), Ok(None)); + } + + #[test] + fn test_location_list_64() { + let tombstone = !0u64 - 1; + let start = Label::new(); + let first = Label::new(); + #[rustfmt::skip] + let section = Section::with_endian(Endian::Little) + // A location before the offset. + .mark(&start) + .L64(0x10000).L64(0x10100).L16(4).L32(1) + .mark(&first) + // A normal location. + .L64(0x10200).L64(0x10300).L16(4).L32(2) + // A base address selection followed by a normal location. + .L64(0xffff_ffff_ffff_ffff).L64(0x0200_0000) + .L64(0x10400).L64(0x10500).L16(4).L32(3) + // An empty location range followed by a normal location. + .L64(0x10600).L64(0x10600).L16(4).L32(4) + .L64(0x10800).L64(0x10900).L16(4).L32(5) + // A location range that starts at 0. + .L64(0).L64(1).L16(4).L32(6) + // A location range that ends at -1. + .L64(0xffff_ffff_ffff_ffff).L64(0x0000_0000) + .L64(0).L64(0xffff_ffff_ffff_ffff).L16(4).L32(7) + // A normal location with tombstone. + .L64(tombstone).L64(tombstone).L16(4).L32(8) + // A base address selection with tombstone followed by a normal location. + .L64(0xffff_ffff_ffff_ffff).L64(tombstone) + .L64(0x10a00).L64(0x10b00).L16(4).L32(9) + // A location list end. + .L64(0).L64(0) + // Some extra data. + .L64(0); + + let buf = section.get_contents().unwrap(); + let debug_loc = DebugLoc::new(&buf, LittleEndian); + let debug_loclists = DebugLocLists::new(&[], LittleEndian); + let loclists = LocationLists::new(debug_loc, debug_loclists); + let offset = LocationListsOffset((&first - &start) as usize); + let debug_addr = &DebugAddr::from(EndianSlice::new(&[], LittleEndian)); + let debug_addr_base = DebugAddrBase(0); + let encoding = Encoding { + format: Format::Dwarf64, + version: 4, + address_size: 8, + }; + let mut locations = loclists + .locations(offset, encoding, 0x0100_0000, debug_addr, debug_addr_base) + .unwrap(); + + // A normal location. + assert_eq!( + locations.next(), + Ok(Some(LocationListEntry { + range: Range { + begin: 0x0101_0200, + end: 0x0101_0300, + }, + data: Expression(EndianSlice::new(&[2, 0, 0, 0], LittleEndian)), + })) + ); + + // A base address selection followed by a normal location. + assert_eq!( + locations.next(), + Ok(Some(LocationListEntry { + range: Range { + begin: 0x0201_0400, + end: 0x0201_0500, + }, + data: Expression(EndianSlice::new(&[3, 0, 0, 0], LittleEndian)), + })) + ); + + // An empty location range followed by a normal location. + assert_eq!( + locations.next(), + Ok(Some(LocationListEntry { + range: Range { + begin: 0x0201_0600, + end: 0x0201_0600, + }, + data: Expression(EndianSlice::new(&[4, 0, 0, 0], LittleEndian)), + })) + ); + assert_eq!( + locations.next(), + Ok(Some(LocationListEntry { + range: Range { + begin: 0x0201_0800, + end: 0x0201_0900, + }, + data: Expression(EndianSlice::new(&[5, 0, 0, 0], LittleEndian)), + })) + ); + + // A location range that starts at 0. + assert_eq!( + locations.next(), + Ok(Some(LocationListEntry { + range: Range { + begin: 0x0200_0000, + end: 0x0200_0001, + }, + data: Expression(EndianSlice::new(&[6, 0, 0, 0], LittleEndian)), + })) + ); + + // A location range that ends at -1. + assert_eq!( + locations.next(), + Ok(Some(LocationListEntry { + range: Range { + begin: 0x0, + end: 0xffff_ffff_ffff_ffff, + }, + data: Expression(EndianSlice::new(&[7, 0, 0, 0], LittleEndian)), + })) + ); + + // A location list end. + assert_eq!(locations.next(), Ok(None)); + + // An offset at the end of buf. + let mut locations = loclists + .locations( + LocationListsOffset(buf.len()), + encoding, + 0x0100_0000, + debug_addr, + debug_addr_base, + ) + .unwrap(); + assert_eq!(locations.next(), Ok(None)); + } + + #[test] + fn test_locations_invalid() { + #[rustfmt::skip] + let section = Section::with_endian(Endian::Little) + // An invalid location range. + .L32(0x20000).L32(0x10000).L16(4).L32(1) + // An invalid range after wrapping. + .L32(0x20000).L32(0xff01_0000).L16(4).L32(2); + + let buf = section.get_contents().unwrap(); + let debug_loc = DebugLoc::new(&buf, LittleEndian); + let debug_loclists = DebugLocLists::new(&[], LittleEndian); + let loclists = LocationLists::new(debug_loc, debug_loclists); + let debug_addr = &DebugAddr::from(EndianSlice::new(&[], LittleEndian)); + let debug_addr_base = DebugAddrBase(0); + let encoding = Encoding { + format: Format::Dwarf32, + version: 4, + address_size: 4, + }; + + // An invalid location range. + let mut locations = loclists + .locations( + LocationListsOffset(0x0), + encoding, + 0x0100_0000, + debug_addr, + debug_addr_base, + ) + .unwrap(); + assert_eq!(locations.next(), Err(Error::InvalidLocationAddressRange)); + + // An invalid location range after wrapping. + let mut locations = loclists + .locations( + LocationListsOffset(14), + encoding, + 0x0100_0000, + debug_addr, + debug_addr_base, + ) + .unwrap(); + assert_eq!(locations.next(), Err(Error::InvalidLocationAddressRange)); + + // An invalid offset. + match loclists.locations( + LocationListsOffset(buf.len() + 1), + encoding, + 0x0100_0000, + debug_addr, + debug_addr_base, + ) { + Err(Error::UnexpectedEof(_)) => {} + otherwise => panic!("Unexpected result: {:?}", otherwise), + } + } + + #[test] + fn test_get_offset() { + for format in vec![Format::Dwarf32, Format::Dwarf64] { + let encoding = Encoding { + format, + version: 5, + address_size: 4, + }; + + let zero = Label::new(); + let length = Label::new(); + let start = Label::new(); + let first = Label::new(); + let end = Label::new(); + let mut section = Section::with_endian(Endian::Little) + .mark(&zero) + .initial_length(format, &length, &start) + .D16(encoding.version) + .D8(encoding.address_size) + .D8(0) + .D32(20) + .mark(&first); + for i in 0..20 { + section = section.word(format.word_size(), 1000 + i); + } + section = section.mark(&end); + length.set_const((&end - &start) as u64); + let section = section.get_contents().unwrap(); + + let debug_loc = DebugLoc::from(EndianSlice::new(&[], LittleEndian)); + let debug_loclists = DebugLocLists::from(EndianSlice::new(§ion, LittleEndian)); + let locations = LocationLists::new(debug_loc, debug_loclists); + + let base = DebugLocListsBase((&first - &zero) as usize); + assert_eq!( + locations.get_offset(encoding, base, DebugLocListsIndex(0)), + Ok(LocationListsOffset(base.0 + 1000)) + ); + assert_eq!( + locations.get_offset(encoding, base, DebugLocListsIndex(19)), + Ok(LocationListsOffset(base.0 + 1019)) + ); + } + } + + #[test] + fn test_loclists_gnu_v4_split_dwarf() { + #[rustfmt::skip] + let buf = [ + 0x03, // DW_LLE_startx_length + 0x00, // ULEB encoded b7 + 0x08, 0x00, 0x00, 0x00, // Fixed 4 byte length of 8 + 0x03, 0x00, // Fixed two byte length of the location + 0x11, 0x00, // DW_OP_constu 0 + 0x9f, // DW_OP_stack_value + // Padding data + //0x99, 0x99, 0x99, 0x99 + ]; + let data_buf = [0x11, 0x00, 0x9f]; + let expected_data = EndianSlice::new(&data_buf, LittleEndian); + let debug_loc = DebugLoc::new(&buf, LittleEndian); + let debug_loclists = DebugLocLists::new(&[], LittleEndian); + let loclists = LocationLists::new(debug_loc, debug_loclists); + let debug_addr = + &DebugAddr::from(EndianSlice::new(&[0x01, 0x02, 0x03, 0x04], LittleEndian)); + let debug_addr_base = DebugAddrBase(0); + let encoding = Encoding { + format: Format::Dwarf32, + version: 4, + address_size: 4, + }; + + // An invalid location range. + let mut locations = loclists + .locations_dwo( + LocationListsOffset(0x0), + encoding, + 0, + debug_addr, + debug_addr_base, + ) + .unwrap(); + assert_eq!( + locations.next(), + Ok(Some(LocationListEntry { + range: Range { + begin: 0x0403_0201, + end: 0x0403_0209 + }, + data: Expression(expected_data), + })) + ); + } +} diff --git a/vendor/gimli/src/read/lookup.rs b/vendor/gimli/src/read/lookup.rs new file mode 100644 index 0000000..1d082f2 --- /dev/null +++ b/vendor/gimli/src/read/lookup.rs @@ -0,0 +1,202 @@ +use core::marker::PhantomData; + +use crate::common::{DebugInfoOffset, Format}; +use crate::read::{parse_debug_info_offset, Error, Reader, ReaderOffset, Result, UnitOffset}; + +// The various "Accelerated Access" sections (DWARF standard v4 Section 6.1) all have +// similar structures. They consist of a header with metadata and an offset into the +// .debug_info section for the entire compilation unit, and a series +// of following entries that list addresses (for .debug_aranges) or names +// (for .debug_pubnames and .debug_pubtypes) that are covered. +// +// Because these three tables all have similar structures, we abstract out some of +// the parsing mechanics. + +pub trait LookupParser<R: Reader> { + /// The type of the produced header. + type Header; + /// The type of the produced entry. + type Entry; + + /// Parse a header from `input`. Returns a tuple of `input` sliced to contain just the entries + /// corresponding to this header (without the header itself), and the parsed representation of + /// the header itself. + fn parse_header(input: &mut R) -> Result<(R, Self::Header)>; + + /// Parse a single entry from `input`. Returns either a parsed representation of the entry + /// or None if `input` is exhausted. + fn parse_entry(input: &mut R, header: &Self::Header) -> Result<Option<Self::Entry>>; +} + +#[derive(Clone, Debug)] +pub struct DebugLookup<R, Parser> +where + R: Reader, + Parser: LookupParser<R>, +{ + input_buffer: R, + phantom: PhantomData<Parser>, +} + +impl<R, Parser> From<R> for DebugLookup<R, Parser> +where + R: Reader, + Parser: LookupParser<R>, +{ + fn from(input_buffer: R) -> Self { + DebugLookup { + input_buffer, + phantom: PhantomData, + } + } +} + +impl<R, Parser> DebugLookup<R, Parser> +where + R: Reader, + Parser: LookupParser<R>, +{ + pub fn items(&self) -> LookupEntryIter<R, Parser> { + LookupEntryIter { + current_set: None, + remaining_input: self.input_buffer.clone(), + } + } + + pub fn reader(&self) -> &R { + &self.input_buffer + } +} + +#[derive(Clone, Debug)] +pub struct LookupEntryIter<R, Parser> +where + R: Reader, + Parser: LookupParser<R>, +{ + current_set: Option<(R, Parser::Header)>, // Only none at the very beginning and end. + remaining_input: R, +} + +impl<R, Parser> LookupEntryIter<R, Parser> +where + R: Reader, + Parser: LookupParser<R>, +{ + /// Advance the iterator and return the next entry. + /// + /// Returns the newly parsed entry as `Ok(Some(Parser::Entry))`. Returns + /// `Ok(None)` when iteration is complete and all entries have already been + /// parsed and yielded. If an error occurs while parsing the next entry, + /// then this error is returned as `Err(e)`, and all subsequent calls return + /// `Ok(None)`. + /// + /// Can be [used with `FallibleIterator`](./index.html#using-with-fallibleiterator). + pub fn next(&mut self) -> Result<Option<Parser::Entry>> { + loop { + if let Some((ref mut input, ref header)) = self.current_set { + if !input.is_empty() { + match Parser::parse_entry(input, header) { + Ok(Some(entry)) => return Ok(Some(entry)), + Ok(None) => {} + Err(e) => { + input.empty(); + self.remaining_input.empty(); + return Err(e); + } + } + } + } + if self.remaining_input.is_empty() { + self.current_set = None; + return Ok(None); + } + match Parser::parse_header(&mut self.remaining_input) { + Ok(set) => { + self.current_set = Some(set); + } + Err(e) => { + self.current_set = None; + self.remaining_input.empty(); + return Err(e); + } + } + } + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct PubStuffHeader<T = usize> { + format: Format, + length: T, + version: u16, + unit_offset: DebugInfoOffset<T>, + unit_length: T, +} + +pub trait PubStuffEntry<R: Reader> { + fn new( + die_offset: UnitOffset<R::Offset>, + name: R, + unit_header_offset: DebugInfoOffset<R::Offset>, + ) -> Self; +} + +#[derive(Clone, Debug)] +pub struct PubStuffParser<R, Entry> +where + R: Reader, + Entry: PubStuffEntry<R>, +{ + // This struct is never instantiated. + phantom: PhantomData<(R, Entry)>, +} + +impl<R, Entry> LookupParser<R> for PubStuffParser<R, Entry> +where + R: Reader, + Entry: PubStuffEntry<R>, +{ + type Header = PubStuffHeader<R::Offset>; + type Entry = Entry; + + /// Parse an pubthings set header. Returns a tuple of the + /// pubthings to be parsed for this set, and the newly created PubThingHeader struct. + fn parse_header(input: &mut R) -> Result<(R, Self::Header)> { + let (length, format) = input.read_initial_length()?; + let mut rest = input.split(length)?; + + let version = rest.read_u16()?; + if version != 2 { + return Err(Error::UnknownVersion(u64::from(version))); + } + + let unit_offset = parse_debug_info_offset(&mut rest, format)?; + let unit_length = rest.read_length(format)?; + + let header = PubStuffHeader { + format, + length, + version, + unit_offset, + unit_length, + }; + Ok((rest, header)) + } + + /// Parse a single pubthing. Return `None` for the null pubthing, `Some` for an actual pubthing. + fn parse_entry(input: &mut R, header: &Self::Header) -> Result<Option<Self::Entry>> { + let offset = input.read_offset(header.format)?; + if offset.into_u64() == 0 { + input.empty(); + Ok(None) + } else { + let name = input.read_null_terminated_slice()?; + Ok(Some(Self::Entry::new( + UnitOffset(offset), + name, + header.unit_offset, + ))) + } + } +} diff --git a/vendor/gimli/src/read/mod.rs b/vendor/gimli/src/read/mod.rs new file mode 100644 index 0000000..2ad7f6a --- /dev/null +++ b/vendor/gimli/src/read/mod.rs @@ -0,0 +1,827 @@ +//! Read DWARF debugging information. +//! +//! * [Example Usage](#example-usage) +//! * [API Structure](#api-structure) +//! * [Using with `FallibleIterator`](#using-with-fallibleiterator) +//! +//! ## Example Usage +//! +//! Print out all of the functions in the debuggee program: +//! +//! ```rust,no_run +//! # fn example() -> Result<(), gimli::Error> { +//! # type R = gimli::EndianSlice<'static, gimli::LittleEndian>; +//! # let get_file_section_reader = |name| -> Result<R, gimli::Error> { unimplemented!() }; +//! # let get_sup_file_section_reader = |name| -> Result<R, gimli::Error> { unimplemented!() }; +//! // Read the DWARF sections with whatever object loader you're using. +//! // These closures should return a `Reader` instance (e.g. `EndianSlice`). +//! let loader = |section: gimli::SectionId| { get_file_section_reader(section.name()) }; +//! let sup_loader = |section: gimli::SectionId| { get_sup_file_section_reader(section.name()) }; +//! let mut dwarf = gimli::Dwarf::load(loader)?; +//! dwarf.load_sup(sup_loader)?; +//! +//! // Iterate over all compilation units. +//! let mut iter = dwarf.units(); +//! while let Some(header) = iter.next()? { +//! // Parse the abbreviations and other information for this compilation unit. +//! let unit = dwarf.unit(header)?; +//! +//! // Iterate over all of this compilation unit's entries. +//! let mut entries = unit.entries(); +//! while let Some((_, entry)) = entries.next_dfs()? { +//! // If we find an entry for a function, print it. +//! if entry.tag() == gimli::DW_TAG_subprogram { +//! println!("Found a function: {:?}", entry); +//! } +//! } +//! } +//! # unreachable!() +//! # } +//! ``` +//! +//! Full example programs: +//! +//! * [A simple parser](https://github.com/gimli-rs/gimli/blob/master/crates/examples/src/bin/simple.rs) +//! +//! * [A `dwarfdump` +//! clone](https://github.com/gimli-rs/gimli/blob/master/crates/examples/src/bin/dwarfdump.rs) +//! +//! * [An `addr2line` clone](https://github.com/gimli-rs/addr2line) +//! +//! * [`ddbug`](https://github.com/gimli-rs/ddbug), a utility giving insight into +//! code generation by making debugging information readable +//! +//! * [`dwprod`](https://github.com/fitzgen/dwprod), a tiny utility to list the +//! compilers used to create each compilation unit within a shared library or +//! executable (via `DW_AT_producer`) +//! +//! * [`dwarf-validate`](https://github.com/gimli-rs/gimli/blob/master/crates/examples/src/bin/dwarf-validate.rs), +//! a program to validate the integrity of some DWARF and its references +//! between sections and compilation units. +//! +//! ## API Structure +//! +//! * Basic familiarity with DWARF is assumed. +//! +//! * The [`Dwarf`](./struct.Dwarf.html) type contains the commonly used DWARF +//! sections. It has methods that simplify access to debugging data that spans +//! multiple sections. Use of this type is optional, but recommended. +//! +//! * Each section gets its own type. Consider these types the entry points to +//! the library: +//! +//! * [`DebugAbbrev`](./struct.DebugAbbrev.html): The `.debug_abbrev` section. +//! +//! * [`DebugAddr`](./struct.DebugAddr.html): The `.debug_addr` section. +//! +//! * [`DebugAranges`](./struct.DebugAranges.html): The `.debug_aranges` +//! section. +//! +//! * [`DebugFrame`](./struct.DebugFrame.html): The `.debug_frame` section. +//! +//! * [`DebugInfo`](./struct.DebugInfo.html): The `.debug_info` section. +//! +//! * [`DebugLine`](./struct.DebugLine.html): The `.debug_line` section. +//! +//! * [`DebugLineStr`](./struct.DebugLineStr.html): The `.debug_line_str` section. +//! +//! * [`DebugLoc`](./struct.DebugLoc.html): The `.debug_loc` section. +//! +//! * [`DebugLocLists`](./struct.DebugLocLists.html): The `.debug_loclists` section. +//! +//! * [`DebugPubNames`](./struct.DebugPubNames.html): The `.debug_pubnames` +//! section. +//! +//! * [`DebugPubTypes`](./struct.DebugPubTypes.html): The `.debug_pubtypes` +//! section. +//! +//! * [`DebugRanges`](./struct.DebugRanges.html): The `.debug_ranges` section. +//! +//! * [`DebugRngLists`](./struct.DebugRngLists.html): The `.debug_rnglists` section. +//! +//! * [`DebugStr`](./struct.DebugStr.html): The `.debug_str` section. +//! +//! * [`DebugStrOffsets`](./struct.DebugStrOffsets.html): The `.debug_str_offsets` section. +//! +//! * [`DebugTypes`](./struct.DebugTypes.html): The `.debug_types` section. +//! +//! * [`DebugCuIndex`](./struct.DebugCuIndex.html): The `.debug_cu_index` section. +//! +//! * [`DebugTuIndex`](./struct.DebugTuIndex.html): The `.debug_tu_index` section. +//! +//! * [`EhFrame`](./struct.EhFrame.html): The `.eh_frame` section. +//! +//! * [`EhFrameHdr`](./struct.EhFrameHdr.html): The `.eh_frame_hdr` section. +//! +//! * Each section type exposes methods for accessing the debugging data encoded +//! in that section. For example, the [`DebugInfo`](./struct.DebugInfo.html) +//! struct has the [`units`](./struct.DebugInfo.html#method.units) method for +//! iterating over the compilation units defined within it. +//! +//! * Offsets into a section are strongly typed: an offset into `.debug_info` is +//! the [`DebugInfoOffset`](./struct.DebugInfoOffset.html) type. It cannot be +//! used to index into the [`DebugLine`](./struct.DebugLine.html) type because +//! `DebugLine` represents the `.debug_line` section. There are similar types +//! for offsets relative to a compilation unit rather than a section. +//! +//! ## Using with `FallibleIterator` +//! +//! The standard library's `Iterator` trait and related APIs do not play well +//! with iterators where the `next` operation is fallible. One can make the +//! `Iterator`'s associated `Item` type be a `Result<T, E>`, however the +//! provided methods cannot gracefully handle the case when an `Err` is +//! returned. +//! +//! This situation led to the +//! [`fallible-iterator`](https://crates.io/crates/fallible-iterator) crate's +//! existence. You can read more of the rationale for its existence in its +//! docs. The crate provides the helpers you have come to expect (eg `map`, +//! `filter`, etc) for iterators that can fail. +//! +//! `gimli`'s many lazy parsing iterators are a perfect match for the +//! `fallible-iterator` crate's `FallibleIterator` trait because parsing is not +//! done eagerly. Parse errors later in the input might only be discovered after +//! having iterated through many items. +//! +//! To use `gimli` iterators with `FallibleIterator`, import the crate and trait +//! into your code: +//! +//! ``` +//! # #[cfg(feature = "fallible-iterator")] +//! # fn foo() { +//! // Use the `FallibleIterator` trait so its methods are in scope! +//! use fallible_iterator::FallibleIterator; +//! use gimli::{DebugAranges, EndianSlice, LittleEndian}; +//! +//! fn find_sum_of_address_range_lengths(aranges: DebugAranges<EndianSlice<LittleEndian>>) +//! -> gimli::Result<u64> +//! { +//! // `DebugAranges::headers` returns a `FallibleIterator`! +//! aranges.headers() +//! // `flat_map` is provided by `FallibleIterator`! +//! .flat_map(|header| Ok(header.entries())) +//! // `map` is provided by `FallibleIterator`! +//! .map(|arange| Ok(arange.length())) +//! // `fold` is provided by `FallibleIterator`! +//! .fold(0, |sum, len| Ok(sum + len)) +//! } +//! # } +//! # fn main() {} +//! ``` + +use core::fmt::{self, Debug}; +use core::result; +#[cfg(feature = "std")] +use std::{error, io}; + +use crate::common::{Register, SectionId}; +use crate::constants; + +mod util; +pub use util::*; + +mod addr; +pub use self::addr::*; + +mod cfi; +pub use self::cfi::*; + +#[cfg(feature = "read")] +mod dwarf; +#[cfg(feature = "read")] +pub use self::dwarf::*; + +mod endian_slice; +pub use self::endian_slice::*; + +#[cfg(feature = "endian-reader")] +mod endian_reader; +#[cfg(feature = "endian-reader")] +pub use self::endian_reader::*; + +mod reader; +pub use self::reader::*; + +#[cfg(feature = "read")] +mod abbrev; +#[cfg(feature = "read")] +pub use self::abbrev::*; + +mod aranges; +pub use self::aranges::*; + +mod index; +pub use self::index::*; + +#[cfg(feature = "read")] +mod line; +#[cfg(feature = "read")] +pub use self::line::*; + +mod lists; + +mod loclists; +pub use self::loclists::*; + +#[cfg(feature = "read")] +mod lookup; + +mod op; +pub use self::op::*; + +#[cfg(feature = "read")] +mod pubnames; +#[cfg(feature = "read")] +pub use self::pubnames::*; + +#[cfg(feature = "read")] +mod pubtypes; +#[cfg(feature = "read")] +pub use self::pubtypes::*; + +mod rnglists; +pub use self::rnglists::*; + +mod str; +pub use self::str::*; + +/// An offset into the current compilation or type unit. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Hash)] +pub struct UnitOffset<T = usize>(pub T); + +#[cfg(feature = "read")] +mod unit; +#[cfg(feature = "read")] +pub use self::unit::*; + +mod value; +pub use self::value::*; + +/// Indicates that storage should be allocated on heap. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct StoreOnHeap; + +/// `EndianBuf` has been renamed to `EndianSlice`. For ease of upgrading across +/// `gimli` versions, we export this type alias. +#[deprecated(note = "EndianBuf has been renamed to EndianSlice, use that instead.")] +pub type EndianBuf<'input, Endian> = EndianSlice<'input, Endian>; + +/// An error that occurred when parsing. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum Error { + /// An I/O error occurred while reading. + Io, + /// Found a PC relative pointer, but the section base is undefined. + PcRelativePointerButSectionBaseIsUndefined, + /// Found a `.text` relative pointer, but the `.text` base is undefined. + TextRelativePointerButTextBaseIsUndefined, + /// Found a data relative pointer, but the data base is undefined. + DataRelativePointerButDataBaseIsUndefined, + /// Found a function relative pointer in a context that does not have a + /// function base. + FuncRelativePointerInBadContext, + /// Cannot parse a pointer with a `DW_EH_PE_omit` encoding. + CannotParseOmitPointerEncoding, + /// An error parsing an unsigned LEB128 value. + BadUnsignedLeb128, + /// An error parsing a signed LEB128 value. + BadSignedLeb128, + /// An abbreviation declared that its tag is zero, but zero is reserved for + /// null records. + AbbreviationTagZero, + /// An attribute specification declared that its form is zero, but zero is + /// reserved for null records. + AttributeFormZero, + /// The abbreviation's has-children byte was not one of + /// `DW_CHILDREN_{yes,no}`. + BadHasChildren, + /// The specified length is impossible. + BadLength, + /// Found an unknown `DW_FORM_*` type. + UnknownForm, + /// Expected a zero, found something else. + ExpectedZero, + /// Found an abbreviation code that has already been used. + DuplicateAbbreviationCode, + /// Found a duplicate arange. + DuplicateArange, + /// Found an unknown reserved length value. + UnknownReservedLength, + /// Found an unknown DWARF version. + UnknownVersion(u64), + /// Found a record with an unknown abbreviation code. + UnknownAbbreviation, + /// Hit the end of input before it was expected. + UnexpectedEof(ReaderOffsetId), + /// Read a null entry before it was expected. + UnexpectedNull, + /// Found an unknown standard opcode. + UnknownStandardOpcode(constants::DwLns), + /// Found an unknown extended opcode. + UnknownExtendedOpcode(constants::DwLne), + /// The specified address size is not supported. + UnsupportedAddressSize(u8), + /// The specified offset size is not supported. + UnsupportedOffsetSize(u8), + /// The specified field size is not supported. + UnsupportedFieldSize(u8), + /// The minimum instruction length must not be zero. + MinimumInstructionLengthZero, + /// The maximum operations per instruction must not be zero. + MaximumOperationsPerInstructionZero, + /// The line range must not be zero. + LineRangeZero, + /// The opcode base must not be zero. + OpcodeBaseZero, + /// Found an invalid UTF-8 string. + BadUtf8, + /// Expected to find the CIE ID, but found something else. + NotCieId, + /// Expected to find a pointer to a CIE, but found the CIE ID instead. + NotCiePointer, + /// Expected to find a pointer to an FDE, but found a CIE instead. + NotFdePointer, + /// Invalid branch target for a DW_OP_bra or DW_OP_skip. + BadBranchTarget(u64), + /// DW_OP_push_object_address used but no address passed in. + InvalidPushObjectAddress, + /// Not enough items on the stack when evaluating an expression. + NotEnoughStackItems, + /// Too many iterations to compute the expression. + TooManyIterations, + /// An unrecognized operation was found while parsing a DWARF + /// expression. + InvalidExpression(constants::DwOp), + /// An unsupported operation was found while evaluating a DWARF expression. + UnsupportedEvaluation, + /// The expression had a piece followed by an expression + /// terminator without a piece. + InvalidPiece, + /// An expression-terminating operation was followed by something + /// other than the end of the expression or a piece operation. + InvalidExpressionTerminator(u64), + /// Division or modulus by zero when evaluating an expression. + DivisionByZero, + /// An expression operation used mismatching types. + TypeMismatch, + /// An expression operation required an integral type but saw a + /// floating point type. + IntegralTypeRequired, + /// An expression operation used types that are not supported. + UnsupportedTypeOperation, + /// The shift value in an expression must be a non-negative integer. + InvalidShiftExpression, + /// An unknown DW_CFA_* instruction. + UnknownCallFrameInstruction(constants::DwCfa), + /// The end of an address range was before the beginning. + InvalidAddressRange, + /// The end offset of a loc list entry was before the beginning. + InvalidLocationAddressRange, + /// Encountered a call frame instruction in a context in which it is not + /// valid. + CfiInstructionInInvalidContext, + /// When evaluating call frame instructions, found a `DW_CFA_restore_state` + /// stack pop instruction, but the stack was empty, and had nothing to pop. + PopWithEmptyStack, + /// Do not have unwind info for the given address. + NoUnwindInfoForAddress, + /// An offset value was larger than the maximum supported value. + UnsupportedOffset, + /// The given pointer encoding is either unknown or invalid. + UnknownPointerEncoding, + /// Did not find an entry at the given offset. + NoEntryAtGivenOffset, + /// The given offset is out of bounds. + OffsetOutOfBounds, + /// Found an unknown CFI augmentation. + UnknownAugmentation, + /// We do not support the given pointer encoding yet. + UnsupportedPointerEncoding, + /// Registers larger than `u16` are not supported. + UnsupportedRegister(u64), + /// The CFI program defined more register rules than we have storage for. + TooManyRegisterRules, + /// Attempted to push onto the CFI or evaluation stack, but it was already + /// at full capacity. + StackFull, + /// The `.eh_frame_hdr` binary search table claims to be variable-length encoded, + /// which makes binary search impossible. + VariableLengthSearchTable, + /// The `DW_UT_*` value for this unit is not supported yet. + UnsupportedUnitType, + /// Ranges using AddressIndex are not supported yet. + UnsupportedAddressIndex, + /// Nonzero segment selector sizes aren't supported yet. + UnsupportedSegmentSize, + /// A compilation unit or type unit is missing its top level DIE. + MissingUnitDie, + /// A DIE attribute used an unsupported form. + UnsupportedAttributeForm, + /// Missing DW_LNCT_path in file entry format. + MissingFileEntryFormatPath, + /// Expected an attribute value to be a string form. + ExpectedStringAttributeValue, + /// `DW_FORM_implicit_const` used in an invalid context. + InvalidImplicitConst, + /// Invalid section count in `.dwp` index. + InvalidIndexSectionCount, + /// Invalid slot count in `.dwp` index. + InvalidIndexSlotCount, + /// Invalid hash row in `.dwp` index. + InvalidIndexRow, + /// Unknown section type in `.dwp` index. + UnknownIndexSection, +} + +impl fmt::Display for Error { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter) -> ::core::result::Result<(), fmt::Error> { + write!(f, "{}", self.description()) + } +} + +impl Error { + /// A short description of the error. + pub fn description(&self) -> &str { + match *self { + Error::Io => "An I/O error occurred while reading.", + Error::PcRelativePointerButSectionBaseIsUndefined => { + "Found a PC relative pointer, but the section base is undefined." + } + Error::TextRelativePointerButTextBaseIsUndefined => { + "Found a `.text` relative pointer, but the `.text` base is undefined." + } + Error::DataRelativePointerButDataBaseIsUndefined => { + "Found a data relative pointer, but the data base is undefined." + } + Error::FuncRelativePointerInBadContext => { + "Found a function relative pointer in a context that does not have a function base." + } + Error::CannotParseOmitPointerEncoding => { + "Cannot parse a pointer with a `DW_EH_PE_omit` encoding." + } + Error::BadUnsignedLeb128 => "An error parsing an unsigned LEB128 value", + Error::BadSignedLeb128 => "An error parsing a signed LEB128 value", + Error::AbbreviationTagZero => { + "An abbreviation declared that its tag is zero, + but zero is reserved for null records" + } + Error::AttributeFormZero => { + "An attribute specification declared that its form is zero, + but zero is reserved for null records" + } + Error::BadHasChildren => { + "The abbreviation's has-children byte was not one of + `DW_CHILDREN_{yes,no}`" + } + Error::BadLength => "The specified length is impossible", + Error::UnknownForm => "Found an unknown `DW_FORM_*` type", + Error::ExpectedZero => "Expected a zero, found something else", + Error::DuplicateAbbreviationCode => { + "Found an abbreviation code that has already been used" + } + Error::DuplicateArange => "Found a duplicate arange", + Error::UnknownReservedLength => "Found an unknown reserved length value", + Error::UnknownVersion(_) => "Found an unknown DWARF version", + Error::UnknownAbbreviation => "Found a record with an unknown abbreviation code", + Error::UnexpectedEof(_) => "Hit the end of input before it was expected", + Error::UnexpectedNull => "Read a null entry before it was expected.", + Error::UnknownStandardOpcode(_) => "Found an unknown standard opcode", + Error::UnknownExtendedOpcode(_) => "Found an unknown extended opcode", + Error::UnsupportedAddressSize(_) => "The specified address size is not supported", + Error::UnsupportedOffsetSize(_) => "The specified offset size is not supported", + Error::UnsupportedFieldSize(_) => "The specified field size is not supported", + Error::MinimumInstructionLengthZero => { + "The minimum instruction length must not be zero." + } + Error::MaximumOperationsPerInstructionZero => { + "The maximum operations per instruction must not be zero." + } + Error::LineRangeZero => "The line range must not be zero.", + Error::OpcodeBaseZero => "The opcode base must not be zero.", + Error::BadUtf8 => "Found an invalid UTF-8 string.", + Error::NotCieId => "Expected to find the CIE ID, but found something else.", + Error::NotCiePointer => "Expected to find a CIE pointer, but found the CIE ID instead.", + Error::NotFdePointer => { + "Expected to find an FDE pointer, but found a CIE pointer instead." + } + Error::BadBranchTarget(_) => "Invalid branch target in DWARF expression", + Error::InvalidPushObjectAddress => { + "DW_OP_push_object_address used but no object address given" + } + Error::NotEnoughStackItems => "Not enough items on stack when evaluating expression", + Error::TooManyIterations => "Too many iterations to evaluate DWARF expression", + Error::InvalidExpression(_) => "Invalid opcode in DWARF expression", + Error::UnsupportedEvaluation => "Unsupported operation when evaluating expression", + Error::InvalidPiece => { + "DWARF expression has piece followed by non-piece expression at end" + } + Error::InvalidExpressionTerminator(_) => "Expected DW_OP_piece or DW_OP_bit_piece", + Error::DivisionByZero => "Division or modulus by zero when evaluating expression", + Error::TypeMismatch => "Type mismatch when evaluating expression", + Error::IntegralTypeRequired => "Integral type expected when evaluating expression", + Error::UnsupportedTypeOperation => { + "An expression operation used types that are not supported" + } + Error::InvalidShiftExpression => { + "The shift value in an expression must be a non-negative integer." + } + Error::UnknownCallFrameInstruction(_) => "An unknown DW_CFA_* instructiion", + Error::InvalidAddressRange => { + "The end of an address range must not be before the beginning." + } + Error::InvalidLocationAddressRange => { + "The end offset of a location list entry must not be before the beginning." + } + Error::CfiInstructionInInvalidContext => { + "Encountered a call frame instruction in a context in which it is not valid." + } + Error::PopWithEmptyStack => { + "When evaluating call frame instructions, found a `DW_CFA_restore_state` stack pop \ + instruction, but the stack was empty, and had nothing to pop." + } + Error::NoUnwindInfoForAddress => "Do not have unwind info for the given address.", + Error::UnsupportedOffset => { + "An offset value was larger than the maximum supported value." + } + Error::UnknownPointerEncoding => { + "The given pointer encoding is either unknown or invalid." + } + Error::NoEntryAtGivenOffset => "Did not find an entry at the given offset.", + Error::OffsetOutOfBounds => "The given offset is out of bounds.", + Error::UnknownAugmentation => "Found an unknown CFI augmentation.", + Error::UnsupportedPointerEncoding => { + "We do not support the given pointer encoding yet." + } + Error::UnsupportedRegister(_) => "Registers larger than `u16` are not supported.", + Error::TooManyRegisterRules => { + "The CFI program defined more register rules than we have storage for." + } + Error::StackFull => { + "Attempted to push onto the CFI stack, but it was already at full capacity." + } + Error::VariableLengthSearchTable => { + "The `.eh_frame_hdr` binary search table claims to be variable-length encoded, \ + which makes binary search impossible." + } + Error::UnsupportedUnitType => "The `DW_UT_*` value for this unit is not supported yet", + Error::UnsupportedAddressIndex => "Ranges involving AddressIndex are not supported yet", + Error::UnsupportedSegmentSize => "Nonzero segment size not supported yet", + Error::MissingUnitDie => { + "A compilation unit or type unit is missing its top level DIE." + } + Error::UnsupportedAttributeForm => "A DIE attribute used an unsupported form.", + Error::MissingFileEntryFormatPath => "Missing DW_LNCT_path in file entry format.", + Error::ExpectedStringAttributeValue => { + "Expected an attribute value to be a string form." + } + Error::InvalidImplicitConst => "DW_FORM_implicit_const used in an invalid context.", + Error::InvalidIndexSectionCount => "Invalid section count in `.dwp` index.", + Error::InvalidIndexSlotCount => "Invalid slot count in `.dwp` index.", + Error::InvalidIndexRow => "Invalid hash row in `.dwp` index.", + Error::UnknownIndexSection => "Unknown section type in `.dwp` index.", + } + } +} + +#[cfg(feature = "std")] +impl error::Error for Error {} + +#[cfg(feature = "std")] +impl From<io::Error> for Error { + fn from(_: io::Error) -> Self { + Error::Io + } +} + +/// The result of a parse. +pub type Result<T> = result::Result<T, Error>; + +/// A convenience trait for loading DWARF sections from object files. To be +/// used like: +/// +/// ``` +/// use gimli::{DebugInfo, EndianSlice, LittleEndian, Reader, Section}; +/// +/// let buf = [0x00, 0x01, 0x02, 0x03]; +/// let reader = EndianSlice::new(&buf, LittleEndian); +/// let loader = |name| -> Result<_, ()> { Ok(reader) }; +/// +/// let debug_info: DebugInfo<_> = Section::load(loader).unwrap(); +/// ``` +pub trait Section<R>: From<R> { + /// Returns the section id for this type. + fn id() -> SectionId; + + /// Returns the ELF section name for this type. + fn section_name() -> &'static str { + Self::id().name() + } + + /// Returns the ELF section name (if any) for this type when used in a dwo + /// file. + fn dwo_section_name() -> Option<&'static str> { + Self::id().dwo_name() + } + + /// Returns the XCOFF section name (if any) for this type when used in a XCOFF + /// file. + fn xcoff_section_name() -> Option<&'static str> { + Self::id().xcoff_name() + } + + /// Try to load the section using the given loader function. + fn load<F, E>(f: F) -> core::result::Result<Self, E> + where + F: FnOnce(SectionId) -> core::result::Result<R, E>, + { + f(Self::id()).map(From::from) + } + + /// Returns the `Reader` for this section. + fn reader(&self) -> &R + where + R: Reader; + + /// Returns the subrange of the section that is the contribution of + /// a unit in a `.dwp` file. + fn dwp_range(&self, offset: u32, size: u32) -> Result<Self> + where + R: Reader, + { + let mut data = self.reader().clone(); + data.skip(R::Offset::from_u32(offset))?; + data.truncate(R::Offset::from_u32(size))?; + Ok(data.into()) + } + + /// Returns the `Reader` for this section. + fn lookup_offset_id(&self, id: ReaderOffsetId) -> Option<(SectionId, R::Offset)> + where + R: Reader, + { + self.reader() + .lookup_offset_id(id) + .map(|offset| (Self::id(), offset)) + } +} + +impl Register { + pub(crate) fn from_u64(x: u64) -> Result<Register> { + let y = x as u16; + if u64::from(y) == x { + Ok(Register(y)) + } else { + Err(Error::UnsupportedRegister(x)) + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::common::Format; + use crate::endianity::LittleEndian; + use test_assembler::{Endian, Section}; + + #[test] + fn test_parse_initial_length_32_ok() { + let section = Section::with_endian(Endian::Little).L32(0x7856_3412); + let buf = section.get_contents().unwrap(); + + let input = &mut EndianSlice::new(&buf, LittleEndian); + match input.read_initial_length() { + Ok((length, format)) => { + assert_eq!(input.len(), 0); + assert_eq!(format, Format::Dwarf32); + assert_eq!(0x7856_3412, length); + } + otherwise => panic!("Unexpected result: {:?}", otherwise), + } + } + + #[test] + fn test_parse_initial_length_64_ok() { + let section = Section::with_endian(Endian::Little) + // Dwarf_64_INITIAL_UNIT_LENGTH + .L32(0xffff_ffff) + // Actual length + .L64(0xffde_bc9a_7856_3412); + let buf = section.get_contents().unwrap(); + let input = &mut EndianSlice::new(&buf, LittleEndian); + + #[cfg(target_pointer_width = "64")] + match input.read_initial_length() { + Ok((length, format)) => { + assert_eq!(input.len(), 0); + assert_eq!(format, Format::Dwarf64); + assert_eq!(0xffde_bc9a_7856_3412, length); + } + otherwise => panic!("Unexpected result: {:?}", otherwise), + } + + #[cfg(target_pointer_width = "32")] + match input.read_initial_length() { + Err(Error::UnsupportedOffset) => {} + otherwise => panic!("Unexpected result: {:?}", otherwise), + }; + } + + #[test] + fn test_parse_initial_length_unknown_reserved_value() { + let section = Section::with_endian(Endian::Little).L32(0xffff_fffe); + let buf = section.get_contents().unwrap(); + + let input = &mut EndianSlice::new(&buf, LittleEndian); + match input.read_initial_length() { + Err(Error::UnknownReservedLength) => assert!(true), + otherwise => panic!("Unexpected result: {:?}", otherwise), + }; + } + + #[test] + fn test_parse_initial_length_incomplete() { + let buf = [0xff, 0xff, 0xff]; // Need at least 4 bytes. + + let input = &mut EndianSlice::new(&buf, LittleEndian); + match input.read_initial_length() { + Err(Error::UnexpectedEof(_)) => assert!(true), + otherwise => panic!("Unexpected result: {:?}", otherwise), + }; + } + + #[test] + fn test_parse_initial_length_64_incomplete() { + let section = Section::with_endian(Endian::Little) + // Dwarf_64_INITIAL_UNIT_LENGTH + .L32(0xffff_ffff) + // Actual length is not long enough. + .L32(0x7856_3412); + let buf = section.get_contents().unwrap(); + + let input = &mut EndianSlice::new(&buf, LittleEndian); + match input.read_initial_length() { + Err(Error::UnexpectedEof(_)) => assert!(true), + otherwise => panic!("Unexpected result: {:?}", otherwise), + }; + } + + #[test] + fn test_parse_offset_32() { + let section = Section::with_endian(Endian::Little).L32(0x0123_4567); + let buf = section.get_contents().unwrap(); + + let input = &mut EndianSlice::new(&buf, LittleEndian); + match input.read_offset(Format::Dwarf32) { + Ok(val) => { + assert_eq!(input.len(), 0); + assert_eq!(val, 0x0123_4567); + } + otherwise => panic!("Unexpected result: {:?}", otherwise), + }; + } + + #[test] + fn test_parse_offset_64_small() { + let section = Section::with_endian(Endian::Little).L64(0x0123_4567); + let buf = section.get_contents().unwrap(); + + let input = &mut EndianSlice::new(&buf, LittleEndian); + match input.read_offset(Format::Dwarf64) { + Ok(val) => { + assert_eq!(input.len(), 0); + assert_eq!(val, 0x0123_4567); + } + otherwise => panic!("Unexpected result: {:?}", otherwise), + }; + } + + #[test] + #[cfg(target_pointer_width = "64")] + fn test_parse_offset_64_large() { + let section = Section::with_endian(Endian::Little).L64(0x0123_4567_89ab_cdef); + let buf = section.get_contents().unwrap(); + + let input = &mut EndianSlice::new(&buf, LittleEndian); + match input.read_offset(Format::Dwarf64) { + Ok(val) => { + assert_eq!(input.len(), 0); + assert_eq!(val, 0x0123_4567_89ab_cdef); + } + otherwise => panic!("Unexpected result: {:?}", otherwise), + }; + } + + #[test] + #[cfg(target_pointer_width = "32")] + fn test_parse_offset_64_large() { + let section = Section::with_endian(Endian::Little).L64(0x0123_4567_89ab_cdef); + let buf = section.get_contents().unwrap(); + + let input = &mut EndianSlice::new(&buf, LittleEndian); + match input.read_offset(Format::Dwarf64) { + Err(Error::UnsupportedOffset) => assert!(true), + otherwise => panic!("Unexpected result: {:?}", otherwise), + }; + } +} diff --git a/vendor/gimli/src/read/op.rs b/vendor/gimli/src/read/op.rs new file mode 100644 index 0000000..4cb681e --- /dev/null +++ b/vendor/gimli/src/read/op.rs @@ -0,0 +1,4140 @@ +//! Functions for parsing and evaluating DWARF expressions. + +#[cfg(feature = "read")] +use alloc::vec::Vec; +use core::mem; + +use super::util::{ArrayLike, ArrayVec}; +use crate::common::{DebugAddrIndex, DebugInfoOffset, Encoding, Register}; +use crate::constants; +use crate::read::{Error, Reader, ReaderOffset, Result, StoreOnHeap, UnitOffset, Value, ValueType}; + +/// A reference to a DIE, either relative to the current CU or +/// relative to the section. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum DieReference<T = usize> { + /// A CU-relative reference. + UnitRef(UnitOffset<T>), + /// A section-relative reference. + DebugInfoRef(DebugInfoOffset<T>), +} + +/// A single decoded DWARF expression operation. +/// +/// DWARF expression evaluation is done in two parts: first the raw +/// bytes of the next part of the expression are decoded; and then the +/// decoded operation is evaluated. This approach lets other +/// consumers inspect the DWARF expression without reimplementing the +/// decoding operation. +/// +/// Multiple DWARF opcodes may decode into a single `Operation`. For +/// example, both `DW_OP_deref` and `DW_OP_xderef` are represented +/// using `Operation::Deref`. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum Operation<R, Offset = <R as Reader>::Offset> +where + R: Reader<Offset = Offset>, + Offset: ReaderOffset, +{ + /// Dereference the topmost value of the stack. + Deref { + /// The DIE of the base type or 0 to indicate the generic type + base_type: UnitOffset<Offset>, + /// The size of the data to dereference. + size: u8, + /// True if the dereference operation takes an address space + /// argument from the stack; false otherwise. + space: bool, + }, + /// Drop an item from the stack. + Drop, + /// Pick an item from the stack and push it on top of the stack. + /// This operation handles `DW_OP_pick`, `DW_OP_dup`, and + /// `DW_OP_over`. + Pick { + /// The index, from the top of the stack, of the item to copy. + index: u8, + }, + /// Swap the top two stack items. + Swap, + /// Rotate the top three stack items. + Rot, + /// Take the absolute value of the top of the stack. + Abs, + /// Bitwise `and` of the top two values on the stack. + And, + /// Divide the top two values on the stack. + Div, + /// Subtract the top two values on the stack. + Minus, + /// Modulus of the top two values on the stack. + Mod, + /// Multiply the top two values on the stack. + Mul, + /// Negate the top of the stack. + Neg, + /// Bitwise `not` of the top of the stack. + Not, + /// Bitwise `or` of the top two values on the stack. + Or, + /// Add the top two values on the stack. + Plus, + /// Add a constant to the topmost value on the stack. + PlusConstant { + /// The value to add. + value: u64, + }, + /// Logical left shift of the 2nd value on the stack by the number + /// of bits given by the topmost value on the stack. + Shl, + /// Right shift of the 2nd value on the stack by the number of + /// bits given by the topmost value on the stack. + Shr, + /// Arithmetic left shift of the 2nd value on the stack by the + /// number of bits given by the topmost value on the stack. + Shra, + /// Bitwise `xor` of the top two values on the stack. + Xor, + /// Branch to the target location if the top of stack is nonzero. + Bra { + /// The relative offset to the target bytecode. + target: i16, + }, + /// Compare the top two stack values for equality. + Eq, + /// Compare the top two stack values using `>=`. + Ge, + /// Compare the top two stack values using `>`. + Gt, + /// Compare the top two stack values using `<=`. + Le, + /// Compare the top two stack values using `<`. + Lt, + /// Compare the top two stack values using `!=`. + Ne, + /// Unconditional branch to the target location. + Skip { + /// The relative offset to the target bytecode. + target: i16, + }, + /// Push an unsigned constant value on the stack. This handles multiple + /// DWARF opcodes. + UnsignedConstant { + /// The value to push. + value: u64, + }, + /// Push a signed constant value on the stack. This handles multiple + /// DWARF opcodes. + SignedConstant { + /// The value to push. + value: i64, + }, + /// Indicate that this piece's location is in the given register. + /// + /// Completes the piece or expression. + Register { + /// The register number. + register: Register, + }, + /// Find the value of the given register, add the offset, and then + /// push the resulting sum on the stack. + RegisterOffset { + /// The register number. + register: Register, + /// The offset to add. + offset: i64, + /// The DIE of the base type or 0 to indicate the generic type + base_type: UnitOffset<Offset>, + }, + /// Compute the frame base (using `DW_AT_frame_base`), add the + /// given offset, and then push the resulting sum on the stack. + FrameOffset { + /// The offset to add. + offset: i64, + }, + /// No operation. + Nop, + /// Push the object address on the stack. + PushObjectAddress, + /// Evaluate a DWARF expression as a subroutine. The expression + /// comes from the `DW_AT_location` attribute of the indicated + /// DIE. + Call { + /// The DIE to use. + offset: DieReference<Offset>, + }, + /// Compute the address of a thread-local variable and push it on + /// the stack. + TLS, + /// Compute the call frame CFA and push it on the stack. + CallFrameCFA, + /// Terminate a piece. + Piece { + /// The size of this piece in bits. + size_in_bits: u64, + /// The bit offset of this piece. If `None`, then this piece + /// was specified using `DW_OP_piece` and should start at the + /// next byte boundary. + bit_offset: Option<u64>, + }, + /// The object has no location, but has a known constant value. + /// + /// Represents `DW_OP_implicit_value`. + /// Completes the piece or expression. + ImplicitValue { + /// The implicit value to use. + data: R, + }, + /// The object has no location, but its value is at the top of the stack. + /// + /// Represents `DW_OP_stack_value`. + /// Completes the piece or expression. + StackValue, + /// The object is a pointer to a value which has no actual location, + /// such as an implicit value or a stack value. + /// + /// Represents `DW_OP_implicit_pointer`. + /// Completes the piece or expression. + ImplicitPointer { + /// The `.debug_info` offset of the value that this is an implicit pointer into. + value: DebugInfoOffset<Offset>, + /// The byte offset into the value that the implicit pointer points to. + byte_offset: i64, + }, + /// Evaluate an expression at the entry to the current subprogram, and push it on the stack. + /// + /// Represents `DW_OP_entry_value`. + EntryValue { + /// The expression to be evaluated. + expression: R, + }, + /// This represents a parameter that was optimized out. + /// + /// The offset points to the definition of the parameter, and is + /// matched to the `DW_TAG_GNU_call_site_parameter` in the caller that also + /// points to the same definition of the parameter. + /// + /// Represents `DW_OP_GNU_parameter_ref`. + ParameterRef { + /// The DIE to use. + offset: UnitOffset<Offset>, + }, + /// Relocate the address if needed, and push it on the stack. + /// + /// Represents `DW_OP_addr`. + Address { + /// The offset to add. + address: u64, + }, + /// Read the address at the given index in `.debug_addr, relocate the address if needed, + /// and push it on the stack. + /// + /// Represents `DW_OP_addrx`. + AddressIndex { + /// The index of the address in `.debug_addr`. + index: DebugAddrIndex<Offset>, + }, + /// Read the address at the given index in `.debug_addr, and push it on the stack. + /// Do not relocate the address. + /// + /// Represents `DW_OP_constx`. + ConstantIndex { + /// The index of the address in `.debug_addr`. + index: DebugAddrIndex<Offset>, + }, + /// Interpret the value bytes as a constant of a given type, and push it on the stack. + /// + /// Represents `DW_OP_const_type`. + TypedLiteral { + /// The DIE of the base type. + base_type: UnitOffset<Offset>, + /// The value bytes. + value: R, + }, + /// Pop the top stack entry, convert it to a different type, and push it on the stack. + /// + /// Represents `DW_OP_convert`. + Convert { + /// The DIE of the base type. + base_type: UnitOffset<Offset>, + }, + /// Pop the top stack entry, reinterpret the bits in its value as a different type, + /// and push it on the stack. + /// + /// Represents `DW_OP_reinterpret`. + Reinterpret { + /// The DIE of the base type. + base_type: UnitOffset<Offset>, + }, + /// The index of a local in the currently executing function. + /// + /// Represents `DW_OP_WASM_location 0x00`. + /// Completes the piece or expression. + WasmLocal { + /// The index of the local. + index: u32, + }, + /// The index of a global. + /// + /// Represents `DW_OP_WASM_location 0x01` or `DW_OP_WASM_location 0x03`. + /// Completes the piece or expression. + WasmGlobal { + /// The index of the global. + index: u32, + }, + /// The index of an item on the operand stack. + /// + /// Represents `DW_OP_WASM_location 0x02`. + /// Completes the piece or expression. + WasmStack { + /// The index of the stack item. 0 is the bottom of the operand stack. + index: u32, + }, +} + +#[derive(Debug)] +enum OperationEvaluationResult<R: Reader> { + Piece, + Incomplete, + Complete { location: Location<R> }, + Waiting(EvaluationWaiting<R>, EvaluationResult<R>), +} + +/// A single location of a piece of the result of a DWARF expression. +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum Location<R, Offset = <R as Reader>::Offset> +where + R: Reader<Offset = Offset>, + Offset: ReaderOffset, +{ + /// The piece is empty. Ordinarily this means the piece has been + /// optimized away. + Empty, + /// The piece is found in a register. + Register { + /// The register number. + register: Register, + }, + /// The piece is found in memory. + Address { + /// The address. + address: u64, + }, + /// The piece has no location but its value is known. + Value { + /// The value. + value: Value, + }, + /// The piece is represented by some constant bytes. + Bytes { + /// The value. + value: R, + }, + /// The piece is a pointer to a value which has no actual location. + ImplicitPointer { + /// The `.debug_info` offset of the value that this is an implicit pointer into. + value: DebugInfoOffset<Offset>, + /// The byte offset into the value that the implicit pointer points to. + byte_offset: i64, + }, +} + +impl<R, Offset> Location<R, Offset> +where + R: Reader<Offset = Offset>, + Offset: ReaderOffset, +{ + /// Return true if the piece is empty. + pub fn is_empty(&self) -> bool { + matches!(*self, Location::Empty) + } +} + +/// The description of a single piece of the result of a DWARF +/// expression. +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct Piece<R, Offset = <R as Reader>::Offset> +where + R: Reader<Offset = Offset>, + Offset: ReaderOffset, +{ + /// If given, the size of the piece in bits. If `None`, there + /// must be only one piece whose size is all of the object. + pub size_in_bits: Option<u64>, + /// If given, the bit offset of the piece within the location. + /// If the location is a `Location::Register` or `Location::Value`, + /// then this offset is from the least significant bit end of + /// the register or value. + /// If the location is a `Location::Address` then the offset uses + /// the bit numbering and direction conventions of the language + /// and target system. + /// + /// If `None`, the piece starts at the location. If the + /// location is a register whose size is larger than the piece, + /// then placement within the register is defined by the ABI. + pub bit_offset: Option<u64>, + /// Where this piece is to be found. + pub location: Location<R, Offset>, +} + +// A helper function to handle branch offsets. +fn compute_pc<R: Reader>(pc: &R, bytecode: &R, offset: i16) -> Result<R> { + let pc_offset = pc.offset_from(bytecode); + let new_pc_offset = pc_offset.wrapping_add(R::Offset::from_i16(offset)); + if new_pc_offset > bytecode.len() { + Err(Error::BadBranchTarget(new_pc_offset.into_u64())) + } else { + let mut new_pc = bytecode.clone(); + new_pc.skip(new_pc_offset)?; + Ok(new_pc) + } +} + +fn generic_type<O: ReaderOffset>() -> UnitOffset<O> { + UnitOffset(O::from_u64(0).unwrap()) +} + +impl<R, Offset> Operation<R, Offset> +where + R: Reader<Offset = Offset>, + Offset: ReaderOffset, +{ + /// Parse a single DWARF expression operation. + /// + /// This is useful when examining a DWARF expression for reasons other + /// than direct evaluation. + /// + /// `bytes` points to a the operation to decode. It should point into + /// the same array as `bytecode`, which should be the entire + /// expression. + pub fn parse(bytes: &mut R, encoding: Encoding) -> Result<Operation<R, Offset>> { + let opcode = bytes.read_u8()?; + let name = constants::DwOp(opcode); + match name { + constants::DW_OP_addr => { + let address = bytes.read_address(encoding.address_size)?; + Ok(Operation::Address { address }) + } + constants::DW_OP_deref => Ok(Operation::Deref { + base_type: generic_type(), + size: encoding.address_size, + space: false, + }), + constants::DW_OP_const1u => { + let value = bytes.read_u8()?; + Ok(Operation::UnsignedConstant { + value: u64::from(value), + }) + } + constants::DW_OP_const1s => { + let value = bytes.read_i8()?; + Ok(Operation::SignedConstant { + value: i64::from(value), + }) + } + constants::DW_OP_const2u => { + let value = bytes.read_u16()?; + Ok(Operation::UnsignedConstant { + value: u64::from(value), + }) + } + constants::DW_OP_const2s => { + let value = bytes.read_i16()?; + Ok(Operation::SignedConstant { + value: i64::from(value), + }) + } + constants::DW_OP_const4u => { + let value = bytes.read_u32()?; + Ok(Operation::UnsignedConstant { + value: u64::from(value), + }) + } + constants::DW_OP_const4s => { + let value = bytes.read_i32()?; + Ok(Operation::SignedConstant { + value: i64::from(value), + }) + } + constants::DW_OP_const8u => { + let value = bytes.read_u64()?; + Ok(Operation::UnsignedConstant { value }) + } + constants::DW_OP_const8s => { + let value = bytes.read_i64()?; + Ok(Operation::SignedConstant { value }) + } + constants::DW_OP_constu => { + let value = bytes.read_uleb128()?; + Ok(Operation::UnsignedConstant { value }) + } + constants::DW_OP_consts => { + let value = bytes.read_sleb128()?; + Ok(Operation::SignedConstant { value }) + } + constants::DW_OP_dup => Ok(Operation::Pick { index: 0 }), + constants::DW_OP_drop => Ok(Operation::Drop), + constants::DW_OP_over => Ok(Operation::Pick { index: 1 }), + constants::DW_OP_pick => { + let value = bytes.read_u8()?; + Ok(Operation::Pick { index: value }) + } + constants::DW_OP_swap => Ok(Operation::Swap), + constants::DW_OP_rot => Ok(Operation::Rot), + constants::DW_OP_xderef => Ok(Operation::Deref { + base_type: generic_type(), + size: encoding.address_size, + space: true, + }), + constants::DW_OP_abs => Ok(Operation::Abs), + constants::DW_OP_and => Ok(Operation::And), + constants::DW_OP_div => Ok(Operation::Div), + constants::DW_OP_minus => Ok(Operation::Minus), + constants::DW_OP_mod => Ok(Operation::Mod), + constants::DW_OP_mul => Ok(Operation::Mul), + constants::DW_OP_neg => Ok(Operation::Neg), + constants::DW_OP_not => Ok(Operation::Not), + constants::DW_OP_or => Ok(Operation::Or), + constants::DW_OP_plus => Ok(Operation::Plus), + constants::DW_OP_plus_uconst => { + let value = bytes.read_uleb128()?; + Ok(Operation::PlusConstant { value }) + } + constants::DW_OP_shl => Ok(Operation::Shl), + constants::DW_OP_shr => Ok(Operation::Shr), + constants::DW_OP_shra => Ok(Operation::Shra), + constants::DW_OP_xor => Ok(Operation::Xor), + constants::DW_OP_bra => { + let target = bytes.read_i16()?; + Ok(Operation::Bra { target }) + } + constants::DW_OP_eq => Ok(Operation::Eq), + constants::DW_OP_ge => Ok(Operation::Ge), + constants::DW_OP_gt => Ok(Operation::Gt), + constants::DW_OP_le => Ok(Operation::Le), + constants::DW_OP_lt => Ok(Operation::Lt), + constants::DW_OP_ne => Ok(Operation::Ne), + constants::DW_OP_skip => { + let target = bytes.read_i16()?; + Ok(Operation::Skip { target }) + } + constants::DW_OP_lit0 + | constants::DW_OP_lit1 + | constants::DW_OP_lit2 + | constants::DW_OP_lit3 + | constants::DW_OP_lit4 + | constants::DW_OP_lit5 + | constants::DW_OP_lit6 + | constants::DW_OP_lit7 + | constants::DW_OP_lit8 + | constants::DW_OP_lit9 + | constants::DW_OP_lit10 + | constants::DW_OP_lit11 + | constants::DW_OP_lit12 + | constants::DW_OP_lit13 + | constants::DW_OP_lit14 + | constants::DW_OP_lit15 + | constants::DW_OP_lit16 + | constants::DW_OP_lit17 + | constants::DW_OP_lit18 + | constants::DW_OP_lit19 + | constants::DW_OP_lit20 + | constants::DW_OP_lit21 + | constants::DW_OP_lit22 + | constants::DW_OP_lit23 + | constants::DW_OP_lit24 + | constants::DW_OP_lit25 + | constants::DW_OP_lit26 + | constants::DW_OP_lit27 + | constants::DW_OP_lit28 + | constants::DW_OP_lit29 + | constants::DW_OP_lit30 + | constants::DW_OP_lit31 => Ok(Operation::UnsignedConstant { + value: (opcode - constants::DW_OP_lit0.0).into(), + }), + constants::DW_OP_reg0 + | constants::DW_OP_reg1 + | constants::DW_OP_reg2 + | constants::DW_OP_reg3 + | constants::DW_OP_reg4 + | constants::DW_OP_reg5 + | constants::DW_OP_reg6 + | constants::DW_OP_reg7 + | constants::DW_OP_reg8 + | constants::DW_OP_reg9 + | constants::DW_OP_reg10 + | constants::DW_OP_reg11 + | constants::DW_OP_reg12 + | constants::DW_OP_reg13 + | constants::DW_OP_reg14 + | constants::DW_OP_reg15 + | constants::DW_OP_reg16 + | constants::DW_OP_reg17 + | constants::DW_OP_reg18 + | constants::DW_OP_reg19 + | constants::DW_OP_reg20 + | constants::DW_OP_reg21 + | constants::DW_OP_reg22 + | constants::DW_OP_reg23 + | constants::DW_OP_reg24 + | constants::DW_OP_reg25 + | constants::DW_OP_reg26 + | constants::DW_OP_reg27 + | constants::DW_OP_reg28 + | constants::DW_OP_reg29 + | constants::DW_OP_reg30 + | constants::DW_OP_reg31 => Ok(Operation::Register { + register: Register((opcode - constants::DW_OP_reg0.0).into()), + }), + constants::DW_OP_breg0 + | constants::DW_OP_breg1 + | constants::DW_OP_breg2 + | constants::DW_OP_breg3 + | constants::DW_OP_breg4 + | constants::DW_OP_breg5 + | constants::DW_OP_breg6 + | constants::DW_OP_breg7 + | constants::DW_OP_breg8 + | constants::DW_OP_breg9 + | constants::DW_OP_breg10 + | constants::DW_OP_breg11 + | constants::DW_OP_breg12 + | constants::DW_OP_breg13 + | constants::DW_OP_breg14 + | constants::DW_OP_breg15 + | constants::DW_OP_breg16 + | constants::DW_OP_breg17 + | constants::DW_OP_breg18 + | constants::DW_OP_breg19 + | constants::DW_OP_breg20 + | constants::DW_OP_breg21 + | constants::DW_OP_breg22 + | constants::DW_OP_breg23 + | constants::DW_OP_breg24 + | constants::DW_OP_breg25 + | constants::DW_OP_breg26 + | constants::DW_OP_breg27 + | constants::DW_OP_breg28 + | constants::DW_OP_breg29 + | constants::DW_OP_breg30 + | constants::DW_OP_breg31 => { + let value = bytes.read_sleb128()?; + Ok(Operation::RegisterOffset { + register: Register((opcode - constants::DW_OP_breg0.0).into()), + offset: value, + base_type: generic_type(), + }) + } + constants::DW_OP_regx => { + let register = bytes.read_uleb128().and_then(Register::from_u64)?; + Ok(Operation::Register { register }) + } + constants::DW_OP_fbreg => { + let value = bytes.read_sleb128()?; + Ok(Operation::FrameOffset { offset: value }) + } + constants::DW_OP_bregx => { + let register = bytes.read_uleb128().and_then(Register::from_u64)?; + let offset = bytes.read_sleb128()?; + Ok(Operation::RegisterOffset { + register, + offset, + base_type: generic_type(), + }) + } + constants::DW_OP_piece => { + let size = bytes.read_uleb128()?; + Ok(Operation::Piece { + size_in_bits: 8 * size, + bit_offset: None, + }) + } + constants::DW_OP_deref_size => { + let size = bytes.read_u8()?; + Ok(Operation::Deref { + base_type: generic_type(), + size, + space: false, + }) + } + constants::DW_OP_xderef_size => { + let size = bytes.read_u8()?; + Ok(Operation::Deref { + base_type: generic_type(), + size, + space: true, + }) + } + constants::DW_OP_nop => Ok(Operation::Nop), + constants::DW_OP_push_object_address => Ok(Operation::PushObjectAddress), + constants::DW_OP_call2 => { + let value = bytes.read_u16().map(R::Offset::from_u16)?; + Ok(Operation::Call { + offset: DieReference::UnitRef(UnitOffset(value)), + }) + } + constants::DW_OP_call4 => { + let value = bytes.read_u32().map(R::Offset::from_u32)?; + Ok(Operation::Call { + offset: DieReference::UnitRef(UnitOffset(value)), + }) + } + constants::DW_OP_call_ref => { + let value = bytes.read_offset(encoding.format)?; + Ok(Operation::Call { + offset: DieReference::DebugInfoRef(DebugInfoOffset(value)), + }) + } + constants::DW_OP_form_tls_address | constants::DW_OP_GNU_push_tls_address => { + Ok(Operation::TLS) + } + constants::DW_OP_call_frame_cfa => Ok(Operation::CallFrameCFA), + constants::DW_OP_bit_piece => { + let size = bytes.read_uleb128()?; + let offset = bytes.read_uleb128()?; + Ok(Operation::Piece { + size_in_bits: size, + bit_offset: Some(offset), + }) + } + constants::DW_OP_implicit_value => { + let len = bytes.read_uleb128().and_then(R::Offset::from_u64)?; + let data = bytes.split(len)?; + Ok(Operation::ImplicitValue { data }) + } + constants::DW_OP_stack_value => Ok(Operation::StackValue), + constants::DW_OP_implicit_pointer | constants::DW_OP_GNU_implicit_pointer => { + let value = if encoding.version == 2 { + bytes + .read_address(encoding.address_size) + .and_then(Offset::from_u64)? + } else { + bytes.read_offset(encoding.format)? + }; + let byte_offset = bytes.read_sleb128()?; + Ok(Operation::ImplicitPointer { + value: DebugInfoOffset(value), + byte_offset, + }) + } + constants::DW_OP_addrx | constants::DW_OP_GNU_addr_index => { + let index = bytes.read_uleb128().and_then(R::Offset::from_u64)?; + Ok(Operation::AddressIndex { + index: DebugAddrIndex(index), + }) + } + constants::DW_OP_constx | constants::DW_OP_GNU_const_index => { + let index = bytes.read_uleb128().and_then(R::Offset::from_u64)?; + Ok(Operation::ConstantIndex { + index: DebugAddrIndex(index), + }) + } + constants::DW_OP_entry_value | constants::DW_OP_GNU_entry_value => { + let len = bytes.read_uleb128().and_then(R::Offset::from_u64)?; + let expression = bytes.split(len)?; + Ok(Operation::EntryValue { expression }) + } + constants::DW_OP_GNU_parameter_ref => { + let value = bytes.read_u32().map(R::Offset::from_u32)?; + Ok(Operation::ParameterRef { + offset: UnitOffset(value), + }) + } + constants::DW_OP_const_type | constants::DW_OP_GNU_const_type => { + let base_type = bytes.read_uleb128().and_then(R::Offset::from_u64)?; + let len = bytes.read_u8()?; + let value = bytes.split(R::Offset::from_u8(len))?; + Ok(Operation::TypedLiteral { + base_type: UnitOffset(base_type), + value, + }) + } + constants::DW_OP_regval_type | constants::DW_OP_GNU_regval_type => { + let register = bytes.read_uleb128().and_then(Register::from_u64)?; + let base_type = bytes.read_uleb128().and_then(R::Offset::from_u64)?; + Ok(Operation::RegisterOffset { + register, + offset: 0, + base_type: UnitOffset(base_type), + }) + } + constants::DW_OP_deref_type | constants::DW_OP_GNU_deref_type => { + let size = bytes.read_u8()?; + let base_type = bytes.read_uleb128().and_then(R::Offset::from_u64)?; + Ok(Operation::Deref { + base_type: UnitOffset(base_type), + size, + space: false, + }) + } + constants::DW_OP_xderef_type => { + let size = bytes.read_u8()?; + let base_type = bytes.read_uleb128().and_then(R::Offset::from_u64)?; + Ok(Operation::Deref { + base_type: UnitOffset(base_type), + size, + space: true, + }) + } + constants::DW_OP_convert | constants::DW_OP_GNU_convert => { + let base_type = bytes.read_uleb128().and_then(R::Offset::from_u64)?; + Ok(Operation::Convert { + base_type: UnitOffset(base_type), + }) + } + constants::DW_OP_reinterpret | constants::DW_OP_GNU_reinterpret => { + let base_type = bytes.read_uleb128().and_then(R::Offset::from_u64)?; + Ok(Operation::Reinterpret { + base_type: UnitOffset(base_type), + }) + } + constants::DW_OP_WASM_location => match bytes.read_u8()? { + 0x0 => { + let index = bytes.read_uleb128_u32()?; + Ok(Operation::WasmLocal { index }) + } + 0x1 => { + let index = bytes.read_uleb128_u32()?; + Ok(Operation::WasmGlobal { index }) + } + 0x2 => { + let index = bytes.read_uleb128_u32()?; + Ok(Operation::WasmStack { index }) + } + 0x3 => { + let index = bytes.read_u32()?; + Ok(Operation::WasmGlobal { index }) + } + _ => Err(Error::InvalidExpression(name)), + }, + _ => Err(Error::InvalidExpression(name)), + } + } +} + +#[derive(Debug)] +enum EvaluationState<R: Reader> { + Start(Option<u64>), + Ready, + Error(Error), + Complete, + Waiting(EvaluationWaiting<R>), +} + +#[derive(Debug)] +enum EvaluationWaiting<R: Reader> { + Memory, + Register { offset: i64 }, + FrameBase { offset: i64 }, + Tls, + Cfa, + AtLocation, + EntryValue, + ParameterRef, + RelocatedAddress, + IndexedAddress, + TypedLiteral { value: R }, + Convert, + Reinterpret, +} + +/// The state of an `Evaluation` after evaluating a DWARF expression. +/// The evaluation is either `Complete`, or it requires more data +/// to continue, as described by the variant. +#[derive(Debug, PartialEq)] +pub enum EvaluationResult<R: Reader> { + /// The `Evaluation` is complete, and `Evaluation::result()` can be called. + Complete, + /// The `Evaluation` needs a value from memory to proceed further. Once the + /// caller determines what value to provide it should resume the `Evaluation` + /// by calling `Evaluation::resume_with_memory`. + RequiresMemory { + /// The address of the value required. + address: u64, + /// The size of the value required. This is guaranteed to be at most the + /// word size of the target architecture. + size: u8, + /// If not `None`, a target-specific address space value. + space: Option<u64>, + /// The DIE of the base type or 0 to indicate the generic type + base_type: UnitOffset<R::Offset>, + }, + /// The `Evaluation` needs a value from a register to proceed further. Once + /// the caller determines what value to provide it should resume the + /// `Evaluation` by calling `Evaluation::resume_with_register`. + RequiresRegister { + /// The register number. + register: Register, + /// The DIE of the base type or 0 to indicate the generic type + base_type: UnitOffset<R::Offset>, + }, + /// The `Evaluation` needs the frame base address to proceed further. Once + /// the caller determines what value to provide it should resume the + /// `Evaluation` by calling `Evaluation::resume_with_frame_base`. The frame + /// base address is the address produced by the location description in the + /// `DW_AT_frame_base` attribute of the current function. + RequiresFrameBase, + /// The `Evaluation` needs a value from TLS to proceed further. Once the + /// caller determines what value to provide it should resume the + /// `Evaluation` by calling `Evaluation::resume_with_tls`. + RequiresTls(u64), + /// The `Evaluation` needs the CFA to proceed further. Once the caller + /// determines what value to provide it should resume the `Evaluation` by + /// calling `Evaluation::resume_with_call_frame_cfa`. + RequiresCallFrameCfa, + /// The `Evaluation` needs the DWARF expression at the given location to + /// proceed further. Once the caller determines what value to provide it + /// should resume the `Evaluation` by calling + /// `Evaluation::resume_with_at_location`. + RequiresAtLocation(DieReference<R::Offset>), + /// The `Evaluation` needs the value produced by evaluating a DWARF + /// expression at the entry point of the current subprogram. Once the + /// caller determines what value to provide it should resume the + /// `Evaluation` by calling `Evaluation::resume_with_entry_value`. + RequiresEntryValue(Expression<R>), + /// The `Evaluation` needs the value of the parameter at the given location + /// in the current function's caller. Once the caller determines what value + /// to provide it should resume the `Evaluation` by calling + /// `Evaluation::resume_with_parameter_ref`. + RequiresParameterRef(UnitOffset<R::Offset>), + /// The `Evaluation` needs an address to be relocated to proceed further. + /// Once the caller determines what value to provide it should resume the + /// `Evaluation` by calling `Evaluation::resume_with_relocated_address`. + RequiresRelocatedAddress(u64), + /// The `Evaluation` needs an address from the `.debug_addr` section. + /// This address may also need to be relocated. + /// Once the caller determines what value to provide it should resume the + /// `Evaluation` by calling `Evaluation::resume_with_indexed_address`. + RequiresIndexedAddress { + /// The index of the address in the `.debug_addr` section, + /// relative to the `DW_AT_addr_base` of the compilation unit. + index: DebugAddrIndex<R::Offset>, + /// Whether the address also needs to be relocated. + relocate: bool, + }, + /// The `Evaluation` needs the `ValueType` for the base type DIE at + /// the give unit offset. Once the caller determines what value to provide it + /// should resume the `Evaluation` by calling + /// `Evaluation::resume_with_base_type`. + RequiresBaseType(UnitOffset<R::Offset>), +} + +/// The bytecode for a DWARF expression or location description. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct Expression<R: Reader>(pub R); + +impl<R: Reader> Expression<R> { + /// Create an evaluation for this expression. + /// + /// The `encoding` is determined by the + /// [`CompilationUnitHeader`](struct.CompilationUnitHeader.html) or + /// [`TypeUnitHeader`](struct.TypeUnitHeader.html) that this expression + /// relates to. + /// + /// # Examples + /// ```rust,no_run + /// use gimli::Expression; + /// # let endian = gimli::LittleEndian; + /// # let debug_info = gimli::DebugInfo::from(gimli::EndianSlice::new(&[], endian)); + /// # let unit = debug_info.units().next().unwrap().unwrap(); + /// # let bytecode = gimli::EndianSlice::new(&[], endian); + /// let expression = gimli::Expression(bytecode); + /// let mut eval = expression.evaluation(unit.encoding()); + /// let mut result = eval.evaluate().unwrap(); + /// ``` + #[cfg(feature = "read")] + #[inline] + pub fn evaluation(self, encoding: Encoding) -> Evaluation<R> { + Evaluation::new(self.0, encoding) + } + + /// Return an iterator for the operations in the expression. + pub fn operations(self, encoding: Encoding) -> OperationIter<R> { + OperationIter { + input: self.0, + encoding, + } + } +} + +/// An iterator for the operations in an expression. +#[derive(Debug, Clone, Copy)] +pub struct OperationIter<R: Reader> { + input: R, + encoding: Encoding, +} + +impl<R: Reader> OperationIter<R> { + /// Read the next operation in an expression. + pub fn next(&mut self) -> Result<Option<Operation<R>>> { + if self.input.is_empty() { + return Ok(None); + } + match Operation::parse(&mut self.input, self.encoding) { + Ok(op) => Ok(Some(op)), + Err(e) => { + self.input.empty(); + Err(e) + } + } + } + + /// Return the current byte offset of the iterator. + pub fn offset_from(&self, expression: &Expression<R>) -> R::Offset { + self.input.offset_from(&expression.0) + } +} + +#[cfg(feature = "fallible-iterator")] +impl<R: Reader> fallible_iterator::FallibleIterator for OperationIter<R> { + type Item = Operation<R>; + type Error = Error; + + fn next(&mut self) -> ::core::result::Result<Option<Self::Item>, Self::Error> { + OperationIter::next(self) + } +} + +/// Specification of what storage should be used for [`Evaluation`]. +/// +#[cfg_attr( + feature = "read", + doc = " +Normally you would only need to use [`StoreOnHeap`], which places the stacks and the results +on the heap using [`Vec`]. This is the default storage type parameter for [`Evaluation`]. +" +)] +/// +/// If you need to avoid [`Evaluation`] from allocating memory, e.g. for signal safety, +/// you can provide you own storage specification: +/// ```rust,no_run +/// # use gimli::*; +/// # let bytecode = EndianSlice::new(&[], LittleEndian); +/// # let encoding = unimplemented!(); +/// # let get_register_value = |_, _| Value::Generic(42); +/// # let get_frame_base = || 0xdeadbeef; +/// # +/// struct StoreOnStack; +/// +/// impl<R: Reader> EvaluationStorage<R> for StoreOnStack { +/// type Stack = [Value; 64]; +/// type ExpressionStack = [(R, R); 4]; +/// type Result = [Piece<R>; 1]; +/// } +/// +/// let mut eval = Evaluation::<_, StoreOnStack>::new_in(bytecode, encoding); +/// let mut result = eval.evaluate().unwrap(); +/// while result != EvaluationResult::Complete { +/// match result { +/// EvaluationResult::RequiresRegister { register, base_type } => { +/// let value = get_register_value(register, base_type); +/// result = eval.resume_with_register(value).unwrap(); +/// }, +/// EvaluationResult::RequiresFrameBase => { +/// let frame_base = get_frame_base(); +/// result = eval.resume_with_frame_base(frame_base).unwrap(); +/// }, +/// _ => unimplemented!(), +/// }; +/// } +/// +/// let result = eval.as_result(); +/// println!("{:?}", result); +/// ``` +pub trait EvaluationStorage<R: Reader> { + /// The storage used for the evaluation stack. + type Stack: ArrayLike<Item = Value>; + /// The storage used for the expression stack. + type ExpressionStack: ArrayLike<Item = (R, R)>; + /// The storage used for the results. + type Result: ArrayLike<Item = Piece<R>>; +} + +#[cfg(feature = "read")] +impl<R: Reader> EvaluationStorage<R> for StoreOnHeap { + type Stack = Vec<Value>; + type ExpressionStack = Vec<(R, R)>; + type Result = Vec<Piece<R>>; +} + +/// A DWARF expression evaluator. +/// +/// # Usage +/// A DWARF expression may require additional data to produce a final result, +/// such as the value of a register or a memory location. Once initial setup +/// is complete (i.e. `set_initial_value()`, `set_object_address()`) the +/// consumer calls the `evaluate()` method. That returns an `EvaluationResult`, +/// which is either `EvaluationResult::Complete` or a value indicating what +/// data is needed to resume the `Evaluation`. The consumer is responsible for +/// producing that data and resuming the computation with the correct method, +/// as documented for `EvaluationResult`. Only once an `EvaluationResult::Complete` +/// is returned can the consumer call `result()`. +/// +/// This design allows the consumer of `Evaluation` to decide how and when to +/// produce the required data and resume the computation. The `Evaluation` can +/// be driven synchronously (as shown below) or by some asynchronous mechanism +/// such as futures. +/// +/// # Examples +/// ```rust,no_run +/// use gimli::{Evaluation, EvaluationResult, Expression}; +/// # let bytecode = gimli::EndianSlice::new(&[], gimli::LittleEndian); +/// # let encoding = unimplemented!(); +/// # let get_register_value = |_, _| gimli::Value::Generic(42); +/// # let get_frame_base = || 0xdeadbeef; +/// +/// let mut eval = Evaluation::new(bytecode, encoding); +/// let mut result = eval.evaluate().unwrap(); +/// while result != EvaluationResult::Complete { +/// match result { +/// EvaluationResult::RequiresRegister { register, base_type } => { +/// let value = get_register_value(register, base_type); +/// result = eval.resume_with_register(value).unwrap(); +/// }, +/// EvaluationResult::RequiresFrameBase => { +/// let frame_base = get_frame_base(); +/// result = eval.resume_with_frame_base(frame_base).unwrap(); +/// }, +/// _ => unimplemented!(), +/// }; +/// } +/// +/// let result = eval.result(); +/// println!("{:?}", result); +/// ``` +#[derive(Debug)] +pub struct Evaluation<R: Reader, S: EvaluationStorage<R> = StoreOnHeap> { + bytecode: R, + encoding: Encoding, + object_address: Option<u64>, + max_iterations: Option<u32>, + iteration: u32, + state: EvaluationState<R>, + + // Stack operations are done on word-sized values. We do all + // operations on 64-bit values, and then mask the results + // appropriately when popping. + addr_mask: u64, + + // The stack. + stack: ArrayVec<S::Stack>, + + // The next operation to decode and evaluate. + pc: R, + + // If we see a DW_OP_call* operation, the previous PC and bytecode + // is stored here while evaluating the subroutine. + expression_stack: ArrayVec<S::ExpressionStack>, + + value_result: Option<Value>, + result: ArrayVec<S::Result>, +} + +#[cfg(feature = "read")] +impl<R: Reader> Evaluation<R> { + /// Create a new DWARF expression evaluator. + /// + /// The new evaluator is created without an initial value, without + /// an object address, and without a maximum number of iterations. + pub fn new(bytecode: R, encoding: Encoding) -> Self { + Self::new_in(bytecode, encoding) + } + + /// Get the result of this `Evaluation`. + /// + /// # Panics + /// Panics if this `Evaluation` has not been driven to completion. + pub fn result(self) -> Vec<Piece<R>> { + match self.state { + EvaluationState::Complete => self.result.into_vec(), + _ => { + panic!("Called `Evaluation::result` on an `Evaluation` that has not been completed") + } + } + } +} + +impl<R: Reader, S: EvaluationStorage<R>> Evaluation<R, S> { + /// Create a new DWARF expression evaluator. + /// + /// The new evaluator is created without an initial value, without + /// an object address, and without a maximum number of iterations. + pub fn new_in(bytecode: R, encoding: Encoding) -> Self { + let pc = bytecode.clone(); + Evaluation { + bytecode, + encoding, + object_address: None, + max_iterations: None, + iteration: 0, + state: EvaluationState::Start(None), + addr_mask: if encoding.address_size == 8 { + !0u64 + } else { + (1 << (8 * u64::from(encoding.address_size))) - 1 + }, + stack: Default::default(), + expression_stack: Default::default(), + pc, + value_result: None, + result: Default::default(), + } + } + + /// Set an initial value to be pushed on the DWARF expression + /// evaluator's stack. This can be used in cases like + /// `DW_AT_vtable_elem_location`, which require a value on the + /// stack before evaluation commences. If no initial value is + /// set, and the expression uses an opcode requiring the initial + /// value, then evaluation will fail with an error. + /// + /// # Panics + /// Panics if `set_initial_value()` has already been called, or if + /// `evaluate()` has already been called. + pub fn set_initial_value(&mut self, value: u64) { + match self.state { + EvaluationState::Start(None) => { + self.state = EvaluationState::Start(Some(value)); + } + _ => panic!( + "`Evaluation::set_initial_value` was called twice, or after evaluation began." + ), + }; + } + + /// Set the enclosing object's address, as used by + /// `DW_OP_push_object_address`. If no object address is set, and + /// the expression uses an opcode requiring the object address, + /// then evaluation will fail with an error. + pub fn set_object_address(&mut self, value: u64) { + self.object_address = Some(value); + } + + /// Set the maximum number of iterations to be allowed by the + /// expression evaluator. + /// + /// An iteration corresponds approximately to the evaluation of a + /// single operation in an expression ("approximately" because the + /// implementation may allow two such operations in some cases). + /// The default is not to have a maximum; once set, it's not + /// possible to go back to this default state. This value can be + /// set to avoid denial of service attacks by bad DWARF bytecode. + pub fn set_max_iterations(&mut self, value: u32) { + self.max_iterations = Some(value); + } + + fn pop(&mut self) -> Result<Value> { + match self.stack.pop() { + Some(value) => Ok(value), + None => Err(Error::NotEnoughStackItems), + } + } + + fn push(&mut self, value: Value) -> Result<()> { + self.stack.try_push(value).map_err(|_| Error::StackFull) + } + + fn evaluate_one_operation(&mut self) -> Result<OperationEvaluationResult<R>> { + let operation = Operation::parse(&mut self.pc, self.encoding)?; + + match operation { + Operation::Deref { + base_type, + size, + space, + } => { + let entry = self.pop()?; + let addr = entry.to_u64(self.addr_mask)?; + let addr_space = if space { + let entry = self.pop()?; + let value = entry.to_u64(self.addr_mask)?; + Some(value) + } else { + None + }; + return Ok(OperationEvaluationResult::Waiting( + EvaluationWaiting::Memory, + EvaluationResult::RequiresMemory { + address: addr, + size, + space: addr_space, + base_type, + }, + )); + } + + Operation::Drop => { + self.pop()?; + } + Operation::Pick { index } => { + let len = self.stack.len(); + let index = index as usize; + if index >= len { + return Err(Error::NotEnoughStackItems); + } + let value = self.stack[len - index - 1]; + self.push(value)?; + } + Operation::Swap => { + let top = self.pop()?; + let next = self.pop()?; + self.push(top)?; + self.push(next)?; + } + Operation::Rot => { + let one = self.pop()?; + let two = self.pop()?; + let three = self.pop()?; + self.push(one)?; + self.push(three)?; + self.push(two)?; + } + + Operation::Abs => { + let value = self.pop()?; + let result = value.abs(self.addr_mask)?; + self.push(result)?; + } + Operation::And => { + let rhs = self.pop()?; + let lhs = self.pop()?; + let result = lhs.and(rhs, self.addr_mask)?; + self.push(result)?; + } + Operation::Div => { + let rhs = self.pop()?; + let lhs = self.pop()?; + let result = lhs.div(rhs, self.addr_mask)?; + self.push(result)?; + } + Operation::Minus => { + let rhs = self.pop()?; + let lhs = self.pop()?; + let result = lhs.sub(rhs, self.addr_mask)?; + self.push(result)?; + } + Operation::Mod => { + let rhs = self.pop()?; + let lhs = self.pop()?; + let result = lhs.rem(rhs, self.addr_mask)?; + self.push(result)?; + } + Operation::Mul => { + let rhs = self.pop()?; + let lhs = self.pop()?; + let result = lhs.mul(rhs, self.addr_mask)?; + self.push(result)?; + } + Operation::Neg => { + let v = self.pop()?; + let result = v.neg(self.addr_mask)?; + self.push(result)?; + } + Operation::Not => { + let value = self.pop()?; + let result = value.not(self.addr_mask)?; + self.push(result)?; + } + Operation::Or => { + let rhs = self.pop()?; + let lhs = self.pop()?; + let result = lhs.or(rhs, self.addr_mask)?; + self.push(result)?; + } + Operation::Plus => { + let rhs = self.pop()?; + let lhs = self.pop()?; + let result = lhs.add(rhs, self.addr_mask)?; + self.push(result)?; + } + Operation::PlusConstant { value } => { + let lhs = self.pop()?; + let rhs = Value::from_u64(lhs.value_type(), value)?; + let result = lhs.add(rhs, self.addr_mask)?; + self.push(result)?; + } + Operation::Shl => { + let rhs = self.pop()?; + let lhs = self.pop()?; + let result = lhs.shl(rhs, self.addr_mask)?; + self.push(result)?; + } + Operation::Shr => { + let rhs = self.pop()?; + let lhs = self.pop()?; + let result = lhs.shr(rhs, self.addr_mask)?; + self.push(result)?; + } + Operation::Shra => { + let rhs = self.pop()?; + let lhs = self.pop()?; + let result = lhs.shra(rhs, self.addr_mask)?; + self.push(result)?; + } + Operation::Xor => { + let rhs = self.pop()?; + let lhs = self.pop()?; + let result = lhs.xor(rhs, self.addr_mask)?; + self.push(result)?; + } + + Operation::Bra { target } => { + let entry = self.pop()?; + let v = entry.to_u64(self.addr_mask)?; + if v != 0 { + self.pc = compute_pc(&self.pc, &self.bytecode, target)?; + } + } + + Operation::Eq => { + let rhs = self.pop()?; + let lhs = self.pop()?; + let result = lhs.eq(rhs, self.addr_mask)?; + self.push(result)?; + } + Operation::Ge => { + let rhs = self.pop()?; + let lhs = self.pop()?; + let result = lhs.ge(rhs, self.addr_mask)?; + self.push(result)?; + } + Operation::Gt => { + let rhs = self.pop()?; + let lhs = self.pop()?; + let result = lhs.gt(rhs, self.addr_mask)?; + self.push(result)?; + } + Operation::Le => { + let rhs = self.pop()?; + let lhs = self.pop()?; + let result = lhs.le(rhs, self.addr_mask)?; + self.push(result)?; + } + Operation::Lt => { + let rhs = self.pop()?; + let lhs = self.pop()?; + let result = lhs.lt(rhs, self.addr_mask)?; + self.push(result)?; + } + Operation::Ne => { + let rhs = self.pop()?; + let lhs = self.pop()?; + let result = lhs.ne(rhs, self.addr_mask)?; + self.push(result)?; + } + + Operation::Skip { target } => { + self.pc = compute_pc(&self.pc, &self.bytecode, target)?; + } + + Operation::UnsignedConstant { value } => { + self.push(Value::Generic(value))?; + } + + Operation::SignedConstant { value } => { + self.push(Value::Generic(value as u64))?; + } + + Operation::RegisterOffset { + register, + offset, + base_type, + } => { + return Ok(OperationEvaluationResult::Waiting( + EvaluationWaiting::Register { offset }, + EvaluationResult::RequiresRegister { + register, + base_type, + }, + )); + } + + Operation::FrameOffset { offset } => { + return Ok(OperationEvaluationResult::Waiting( + EvaluationWaiting::FrameBase { offset }, + EvaluationResult::RequiresFrameBase, + )); + } + + Operation::Nop => {} + + Operation::PushObjectAddress => { + if let Some(value) = self.object_address { + self.push(Value::Generic(value))?; + } else { + return Err(Error::InvalidPushObjectAddress); + } + } + + Operation::Call { offset } => { + return Ok(OperationEvaluationResult::Waiting( + EvaluationWaiting::AtLocation, + EvaluationResult::RequiresAtLocation(offset), + )); + } + + Operation::TLS => { + let entry = self.pop()?; + let index = entry.to_u64(self.addr_mask)?; + return Ok(OperationEvaluationResult::Waiting( + EvaluationWaiting::Tls, + EvaluationResult::RequiresTls(index), + )); + } + + Operation::CallFrameCFA => { + return Ok(OperationEvaluationResult::Waiting( + EvaluationWaiting::Cfa, + EvaluationResult::RequiresCallFrameCfa, + )); + } + + Operation::Register { register } => { + let location = Location::Register { register }; + return Ok(OperationEvaluationResult::Complete { location }); + } + + Operation::ImplicitValue { ref data } => { + let location = Location::Bytes { + value: data.clone(), + }; + return Ok(OperationEvaluationResult::Complete { location }); + } + + Operation::StackValue => { + let value = self.pop()?; + let location = Location::Value { value }; + return Ok(OperationEvaluationResult::Complete { location }); + } + + Operation::ImplicitPointer { value, byte_offset } => { + let location = Location::ImplicitPointer { value, byte_offset }; + return Ok(OperationEvaluationResult::Complete { location }); + } + + Operation::EntryValue { ref expression } => { + return Ok(OperationEvaluationResult::Waiting( + EvaluationWaiting::EntryValue, + EvaluationResult::RequiresEntryValue(Expression(expression.clone())), + )); + } + + Operation::ParameterRef { offset } => { + return Ok(OperationEvaluationResult::Waiting( + EvaluationWaiting::ParameterRef, + EvaluationResult::RequiresParameterRef(offset), + )); + } + + Operation::Address { address } => { + return Ok(OperationEvaluationResult::Waiting( + EvaluationWaiting::RelocatedAddress, + EvaluationResult::RequiresRelocatedAddress(address), + )); + } + + Operation::AddressIndex { index } => { + return Ok(OperationEvaluationResult::Waiting( + EvaluationWaiting::IndexedAddress, + EvaluationResult::RequiresIndexedAddress { + index, + relocate: true, + }, + )); + } + + Operation::ConstantIndex { index } => { + return Ok(OperationEvaluationResult::Waiting( + EvaluationWaiting::IndexedAddress, + EvaluationResult::RequiresIndexedAddress { + index, + relocate: false, + }, + )); + } + + Operation::Piece { + size_in_bits, + bit_offset, + } => { + let location = if self.stack.is_empty() { + Location::Empty + } else { + let entry = self.pop()?; + let address = entry.to_u64(self.addr_mask)?; + Location::Address { address } + }; + self.result + .try_push(Piece { + size_in_bits: Some(size_in_bits), + bit_offset, + location, + }) + .map_err(|_| Error::StackFull)?; + return Ok(OperationEvaluationResult::Piece); + } + + Operation::TypedLiteral { base_type, value } => { + return Ok(OperationEvaluationResult::Waiting( + EvaluationWaiting::TypedLiteral { value }, + EvaluationResult::RequiresBaseType(base_type), + )); + } + Operation::Convert { base_type } => { + return Ok(OperationEvaluationResult::Waiting( + EvaluationWaiting::Convert, + EvaluationResult::RequiresBaseType(base_type), + )); + } + Operation::Reinterpret { base_type } => { + return Ok(OperationEvaluationResult::Waiting( + EvaluationWaiting::Reinterpret, + EvaluationResult::RequiresBaseType(base_type), + )); + } + Operation::WasmLocal { .. } + | Operation::WasmGlobal { .. } + | Operation::WasmStack { .. } => { + return Err(Error::UnsupportedEvaluation); + } + } + + Ok(OperationEvaluationResult::Incomplete) + } + + /// Get the result if this is an evaluation for a value. + /// + /// Returns `None` if the evaluation contained operations that are only + /// valid for location descriptions. + /// + /// # Panics + /// Panics if this `Evaluation` has not been driven to completion. + pub fn value_result(&self) -> Option<Value> { + match self.state { + EvaluationState::Complete => self.value_result, + _ => { + panic!("Called `Evaluation::value_result` on an `Evaluation` that has not been completed") + } + } + } + + /// Get the result of this `Evaluation`. + /// + /// # Panics + /// Panics if this `Evaluation` has not been driven to completion. + pub fn as_result(&self) -> &[Piece<R>] { + match self.state { + EvaluationState::Complete => &self.result, + _ => { + panic!( + "Called `Evaluation::as_result` on an `Evaluation` that has not been completed" + ) + } + } + } + + /// Evaluate a DWARF expression. This method should only ever be called + /// once. If the returned `EvaluationResult` is not + /// `EvaluationResult::Complete`, the caller should provide the required + /// value and resume the evaluation by calling the appropriate resume_with + /// method on `Evaluation`. + pub fn evaluate(&mut self) -> Result<EvaluationResult<R>> { + match self.state { + EvaluationState::Start(initial_value) => { + if let Some(value) = initial_value { + self.push(Value::Generic(value))?; + } + self.state = EvaluationState::Ready; + } + EvaluationState::Ready => {} + EvaluationState::Error(err) => return Err(err), + EvaluationState::Complete => return Ok(EvaluationResult::Complete), + EvaluationState::Waiting(_) => panic!(), + }; + + match self.evaluate_internal() { + Ok(r) => Ok(r), + Err(e) => { + self.state = EvaluationState::Error(e); + Err(e) + } + } + } + + /// Resume the `Evaluation` with the provided memory `value`. This will apply + /// the provided memory value to the evaluation and continue evaluating + /// opcodes until the evaluation is completed, reaches an error, or needs + /// more information again. + /// + /// # Panics + /// Panics if this `Evaluation` did not previously stop with `EvaluationResult::RequiresMemory`. + pub fn resume_with_memory(&mut self, value: Value) -> Result<EvaluationResult<R>> { + match self.state { + EvaluationState::Error(err) => return Err(err), + EvaluationState::Waiting(EvaluationWaiting::Memory) => { + self.push(value)?; + } + _ => panic!( + "Called `Evaluation::resume_with_memory` without a preceding `EvaluationResult::RequiresMemory`" + ), + }; + + self.evaluate_internal() + } + + /// Resume the `Evaluation` with the provided `register` value. This will apply + /// the provided register value to the evaluation and continue evaluating + /// opcodes until the evaluation is completed, reaches an error, or needs + /// more information again. + /// + /// # Panics + /// Panics if this `Evaluation` did not previously stop with `EvaluationResult::RequiresRegister`. + pub fn resume_with_register(&mut self, value: Value) -> Result<EvaluationResult<R>> { + match self.state { + EvaluationState::Error(err) => return Err(err), + EvaluationState::Waiting(EvaluationWaiting::Register { offset }) => { + let offset = Value::from_u64(value.value_type(), offset as u64)?; + let value = value.add(offset, self.addr_mask)?; + self.push(value)?; + } + _ => panic!( + "Called `Evaluation::resume_with_register` without a preceding `EvaluationResult::RequiresRegister`" + ), + }; + + self.evaluate_internal() + } + + /// Resume the `Evaluation` with the provided `frame_base`. This will + /// apply the provided frame base value to the evaluation and continue + /// evaluating opcodes until the evaluation is completed, reaches an error, + /// or needs more information again. + /// + /// # Panics + /// Panics if this `Evaluation` did not previously stop with `EvaluationResult::RequiresFrameBase`. + pub fn resume_with_frame_base(&mut self, frame_base: u64) -> Result<EvaluationResult<R>> { + match self.state { + EvaluationState::Error(err) => return Err(err), + EvaluationState::Waiting(EvaluationWaiting::FrameBase { offset }) => { + self.push(Value::Generic(frame_base.wrapping_add(offset as u64)))?; + } + _ => panic!( + "Called `Evaluation::resume_with_frame_base` without a preceding `EvaluationResult::RequiresFrameBase`" + ), + }; + + self.evaluate_internal() + } + + /// Resume the `Evaluation` with the provided `value`. This will apply + /// the provided TLS value to the evaluation and continue evaluating + /// opcodes until the evaluation is completed, reaches an error, or needs + /// more information again. + /// + /// # Panics + /// Panics if this `Evaluation` did not previously stop with `EvaluationResult::RequiresTls`. + pub fn resume_with_tls(&mut self, value: u64) -> Result<EvaluationResult<R>> { + match self.state { + EvaluationState::Error(err) => return Err(err), + EvaluationState::Waiting(EvaluationWaiting::Tls) => { + self.push(Value::Generic(value))?; + } + _ => panic!( + "Called `Evaluation::resume_with_tls` without a preceding `EvaluationResult::RequiresTls`" + ), + }; + + self.evaluate_internal() + } + + /// Resume the `Evaluation` with the provided `cfa`. This will + /// apply the provided CFA value to the evaluation and continue evaluating + /// opcodes until the evaluation is completed, reaches an error, or needs + /// more information again. + /// + /// # Panics + /// Panics if this `Evaluation` did not previously stop with `EvaluationResult::RequiresCallFrameCfa`. + pub fn resume_with_call_frame_cfa(&mut self, cfa: u64) -> Result<EvaluationResult<R>> { + match self.state { + EvaluationState::Error(err) => return Err(err), + EvaluationState::Waiting(EvaluationWaiting::Cfa) => { + self.push(Value::Generic(cfa))?; + } + _ => panic!( + "Called `Evaluation::resume_with_call_frame_cfa` without a preceding `EvaluationResult::RequiresCallFrameCfa`" + ), + }; + + self.evaluate_internal() + } + + /// Resume the `Evaluation` with the provided `bytes`. This will + /// continue processing the evaluation with the new expression provided + /// until the evaluation is completed, reaches an error, or needs more + /// information again. + /// + /// # Panics + /// Panics if this `Evaluation` did not previously stop with `EvaluationResult::RequiresAtLocation`. + pub fn resume_with_at_location(&mut self, mut bytes: R) -> Result<EvaluationResult<R>> { + match self.state { + EvaluationState::Error(err) => return Err(err), + EvaluationState::Waiting(EvaluationWaiting::AtLocation) => { + if !bytes.is_empty() { + let mut pc = bytes.clone(); + mem::swap(&mut pc, &mut self.pc); + mem::swap(&mut bytes, &mut self.bytecode); + self.expression_stack.try_push((pc, bytes)).map_err(|_| Error::StackFull)?; + } + } + _ => panic!( + "Called `Evaluation::resume_with_at_location` without a precedeing `EvaluationResult::RequiresAtLocation`" + ), + }; + + self.evaluate_internal() + } + + /// Resume the `Evaluation` with the provided `entry_value`. This will + /// apply the provided entry value to the evaluation and continue evaluating + /// opcodes until the evaluation is completed, reaches an error, or needs + /// more information again. + /// + /// # Panics + /// Panics if this `Evaluation` did not previously stop with `EvaluationResult::RequiresEntryValue`. + pub fn resume_with_entry_value(&mut self, entry_value: Value) -> Result<EvaluationResult<R>> { + match self.state { + EvaluationState::Error(err) => return Err(err), + EvaluationState::Waiting(EvaluationWaiting::EntryValue) => { + self.push(entry_value)?; + } + _ => panic!( + "Called `Evaluation::resume_with_entry_value` without a preceding `EvaluationResult::RequiresEntryValue`" + ), + }; + + self.evaluate_internal() + } + + /// Resume the `Evaluation` with the provided `parameter_value`. This will + /// apply the provided parameter value to the evaluation and continue evaluating + /// opcodes until the evaluation is completed, reaches an error, or needs + /// more information again. + /// + /// # Panics + /// Panics if this `Evaluation` did not previously stop with `EvaluationResult::RequiresParameterRef`. + pub fn resume_with_parameter_ref( + &mut self, + parameter_value: u64, + ) -> Result<EvaluationResult<R>> { + match self.state { + EvaluationState::Error(err) => return Err(err), + EvaluationState::Waiting(EvaluationWaiting::ParameterRef) => { + self.push(Value::Generic(parameter_value))?; + } + _ => panic!( + "Called `Evaluation::resume_with_parameter_ref` without a preceding `EvaluationResult::RequiresParameterRef`" + ), + }; + + self.evaluate_internal() + } + + /// Resume the `Evaluation` with the provided relocated `address`. This will use the + /// provided relocated address for the operation that required it, and continue evaluating + /// opcodes until the evaluation is completed, reaches an error, or needs + /// more information again. + /// + /// # Panics + /// Panics if this `Evaluation` did not previously stop with + /// `EvaluationResult::RequiresRelocatedAddress`. + pub fn resume_with_relocated_address(&mut self, address: u64) -> Result<EvaluationResult<R>> { + match self.state { + EvaluationState::Error(err) => return Err(err), + EvaluationState::Waiting(EvaluationWaiting::RelocatedAddress) => { + self.push(Value::Generic(address))?; + } + _ => panic!( + "Called `Evaluation::resume_with_relocated_address` without a preceding `EvaluationResult::RequiresRelocatedAddress`" + ), + }; + + self.evaluate_internal() + } + + /// Resume the `Evaluation` with the provided indexed `address`. This will use the + /// provided indexed address for the operation that required it, and continue evaluating + /// opcodes until the evaluation is completed, reaches an error, or needs + /// more information again. + /// + /// # Panics + /// Panics if this `Evaluation` did not previously stop with + /// `EvaluationResult::RequiresIndexedAddress`. + pub fn resume_with_indexed_address(&mut self, address: u64) -> Result<EvaluationResult<R>> { + match self.state { + EvaluationState::Error(err) => return Err(err), + EvaluationState::Waiting(EvaluationWaiting::IndexedAddress) => { + self.push(Value::Generic(address))?; + } + _ => panic!( + "Called `Evaluation::resume_with_indexed_address` without a preceding `EvaluationResult::RequiresIndexedAddress`" + ), + }; + + self.evaluate_internal() + } + + /// Resume the `Evaluation` with the provided `base_type`. This will use the + /// provided base type for the operation that required it, and continue evaluating + /// opcodes until the evaluation is completed, reaches an error, or needs + /// more information again. + /// + /// # Panics + /// Panics if this `Evaluation` did not previously stop with `EvaluationResult::RequiresBaseType`. + pub fn resume_with_base_type(&mut self, base_type: ValueType) -> Result<EvaluationResult<R>> { + let value = match self.state { + EvaluationState::Error(err) => return Err(err), + EvaluationState::Waiting(EvaluationWaiting::TypedLiteral { ref value }) => { + Value::parse(base_type, value.clone())? + } + EvaluationState::Waiting(EvaluationWaiting::Convert) => { + let entry = self.pop()?; + entry.convert(base_type, self.addr_mask)? + } + EvaluationState::Waiting(EvaluationWaiting::Reinterpret) => { + let entry = self.pop()?; + entry.reinterpret(base_type, self.addr_mask)? + } + _ => panic!( + "Called `Evaluation::resume_with_base_type` without a preceding `EvaluationResult::RequiresBaseType`" + ), + }; + self.push(value)?; + self.evaluate_internal() + } + + fn end_of_expression(&mut self) -> bool { + while self.pc.is_empty() { + match self.expression_stack.pop() { + Some((newpc, newbytes)) => { + self.pc = newpc; + self.bytecode = newbytes; + } + None => return true, + } + } + false + } + + fn evaluate_internal(&mut self) -> Result<EvaluationResult<R>> { + while !self.end_of_expression() { + self.iteration += 1; + if let Some(max_iterations) = self.max_iterations { + if self.iteration > max_iterations { + return Err(Error::TooManyIterations); + } + } + + let op_result = self.evaluate_one_operation()?; + match op_result { + OperationEvaluationResult::Piece => {} + OperationEvaluationResult::Incomplete => { + if self.end_of_expression() && !self.result.is_empty() { + // We saw a piece earlier and then some + // unterminated piece. It's not clear this is + // well-defined. + return Err(Error::InvalidPiece); + } + } + OperationEvaluationResult::Complete { location } => { + if self.end_of_expression() { + if !self.result.is_empty() { + // We saw a piece earlier and then some + // unterminated piece. It's not clear this is + // well-defined. + return Err(Error::InvalidPiece); + } + self.result + .try_push(Piece { + size_in_bits: None, + bit_offset: None, + location, + }) + .map_err(|_| Error::StackFull)?; + } else { + // If there are more operations, then the next operation must + // be a Piece. + match Operation::parse(&mut self.pc, self.encoding)? { + Operation::Piece { + size_in_bits, + bit_offset, + } => { + self.result + .try_push(Piece { + size_in_bits: Some(size_in_bits), + bit_offset, + location, + }) + .map_err(|_| Error::StackFull)?; + } + _ => { + let value = + self.bytecode.len().into_u64() - self.pc.len().into_u64() - 1; + return Err(Error::InvalidExpressionTerminator(value)); + } + } + } + } + OperationEvaluationResult::Waiting(waiting, result) => { + self.state = EvaluationState::Waiting(waiting); + return Ok(result); + } + } + } + + // If no pieces have been seen, use the stack top as the + // result. + if self.result.is_empty() { + let entry = self.pop()?; + self.value_result = Some(entry); + let addr = entry.to_u64(self.addr_mask)?; + self.result + .try_push(Piece { + size_in_bits: None, + bit_offset: None, + location: Location::Address { address: addr }, + }) + .map_err(|_| Error::StackFull)?; + } + + self.state = EvaluationState::Complete; + Ok(EvaluationResult::Complete) + } +} + +#[cfg(test)] +// Tests require leb128::write. +#[cfg(feature = "write")] +mod tests { + use super::*; + use crate::common::Format; + use crate::constants; + use crate::endianity::LittleEndian; + use crate::leb128; + use crate::read::{EndianSlice, Error, Result, UnitOffset}; + use crate::test_util::GimliSectionMethods; + use core::usize; + use test_assembler::{Endian, Section}; + + fn encoding4() -> Encoding { + Encoding { + format: Format::Dwarf32, + version: 4, + address_size: 4, + } + } + + fn encoding8() -> Encoding { + Encoding { + format: Format::Dwarf64, + version: 4, + address_size: 8, + } + } + + #[test] + fn test_compute_pc() { + // Contents don't matter for this test, just length. + let bytes = [0, 1, 2, 3, 4]; + let bytecode = &bytes[..]; + let ebuf = &EndianSlice::new(bytecode, LittleEndian); + + assert_eq!(compute_pc(ebuf, ebuf, 0), Ok(*ebuf)); + assert_eq!( + compute_pc(ebuf, ebuf, -1), + Err(Error::BadBranchTarget(usize::MAX as u64)) + ); + assert_eq!(compute_pc(ebuf, ebuf, 5), Ok(ebuf.range_from(5..))); + assert_eq!( + compute_pc(&ebuf.range_from(3..), ebuf, -2), + Ok(ebuf.range_from(1..)) + ); + assert_eq!( + compute_pc(&ebuf.range_from(2..), ebuf, 2), + Ok(ebuf.range_from(4..)) + ); + } + + fn check_op_parse_simple<'input>( + input: &'input [u8], + expect: &Operation<EndianSlice<'input, LittleEndian>>, + encoding: Encoding, + ) { + let buf = EndianSlice::new(input, LittleEndian); + let mut pc = buf; + let value = Operation::parse(&mut pc, encoding); + match value { + Ok(val) => { + assert_eq!(val, *expect); + assert_eq!(pc.len(), 0); + } + _ => panic!("Unexpected result"), + } + } + + fn check_op_parse_eof(input: &[u8], encoding: Encoding) { + let buf = EndianSlice::new(input, LittleEndian); + let mut pc = buf; + match Operation::parse(&mut pc, encoding) { + Err(Error::UnexpectedEof(id)) => { + assert!(buf.lookup_offset_id(id).is_some()); + } + + _ => panic!("Unexpected result"), + } + } + + fn check_op_parse<F>( + input: F, + expect: &Operation<EndianSlice<LittleEndian>>, + encoding: Encoding, + ) where + F: Fn(Section) -> Section, + { + let input = input(Section::with_endian(Endian::Little)) + .get_contents() + .unwrap(); + for i in 1..input.len() { + check_op_parse_eof(&input[..i], encoding); + } + check_op_parse_simple(&input, expect, encoding); + } + + #[test] + fn test_op_parse_onebyte() { + // Doesn't matter for this test. + let encoding = encoding4(); + + // Test all single-byte opcodes. + #[rustfmt::skip] + let inputs = [ + ( + constants::DW_OP_deref, + Operation::Deref { + base_type: generic_type(), + size: encoding.address_size, + space: false, + }, + ), + (constants::DW_OP_dup, Operation::Pick { index: 0 }), + (constants::DW_OP_drop, Operation::Drop), + (constants::DW_OP_over, Operation::Pick { index: 1 }), + (constants::DW_OP_swap, Operation::Swap), + (constants::DW_OP_rot, Operation::Rot), + ( + constants::DW_OP_xderef, + Operation::Deref { + base_type: generic_type(), + size: encoding.address_size, + space: true, + }, + ), + (constants::DW_OP_abs, Operation::Abs), + (constants::DW_OP_and, Operation::And), + (constants::DW_OP_div, Operation::Div), + (constants::DW_OP_minus, Operation::Minus), + (constants::DW_OP_mod, Operation::Mod), + (constants::DW_OP_mul, Operation::Mul), + (constants::DW_OP_neg, Operation::Neg), + (constants::DW_OP_not, Operation::Not), + (constants::DW_OP_or, Operation::Or), + (constants::DW_OP_plus, Operation::Plus), + (constants::DW_OP_shl, Operation::Shl), + (constants::DW_OP_shr, Operation::Shr), + (constants::DW_OP_shra, Operation::Shra), + (constants::DW_OP_xor, Operation::Xor), + (constants::DW_OP_eq, Operation::Eq), + (constants::DW_OP_ge, Operation::Ge), + (constants::DW_OP_gt, Operation::Gt), + (constants::DW_OP_le, Operation::Le), + (constants::DW_OP_lt, Operation::Lt), + (constants::DW_OP_ne, Operation::Ne), + (constants::DW_OP_lit0, Operation::UnsignedConstant { value: 0 }), + (constants::DW_OP_lit1, Operation::UnsignedConstant { value: 1 }), + (constants::DW_OP_lit2, Operation::UnsignedConstant { value: 2 }), + (constants::DW_OP_lit3, Operation::UnsignedConstant { value: 3 }), + (constants::DW_OP_lit4, Operation::UnsignedConstant { value: 4 }), + (constants::DW_OP_lit5, Operation::UnsignedConstant { value: 5 }), + (constants::DW_OP_lit6, Operation::UnsignedConstant { value: 6 }), + (constants::DW_OP_lit7, Operation::UnsignedConstant { value: 7 }), + (constants::DW_OP_lit8, Operation::UnsignedConstant { value: 8 }), + (constants::DW_OP_lit9, Operation::UnsignedConstant { value: 9 }), + (constants::DW_OP_lit10, Operation::UnsignedConstant { value: 10 }), + (constants::DW_OP_lit11, Operation::UnsignedConstant { value: 11 }), + (constants::DW_OP_lit12, Operation::UnsignedConstant { value: 12 }), + (constants::DW_OP_lit13, Operation::UnsignedConstant { value: 13 }), + (constants::DW_OP_lit14, Operation::UnsignedConstant { value: 14 }), + (constants::DW_OP_lit15, Operation::UnsignedConstant { value: 15 }), + (constants::DW_OP_lit16, Operation::UnsignedConstant { value: 16 }), + (constants::DW_OP_lit17, Operation::UnsignedConstant { value: 17 }), + (constants::DW_OP_lit18, Operation::UnsignedConstant { value: 18 }), + (constants::DW_OP_lit19, Operation::UnsignedConstant { value: 19 }), + (constants::DW_OP_lit20, Operation::UnsignedConstant { value: 20 }), + (constants::DW_OP_lit21, Operation::UnsignedConstant { value: 21 }), + (constants::DW_OP_lit22, Operation::UnsignedConstant { value: 22 }), + (constants::DW_OP_lit23, Operation::UnsignedConstant { value: 23 }), + (constants::DW_OP_lit24, Operation::UnsignedConstant { value: 24 }), + (constants::DW_OP_lit25, Operation::UnsignedConstant { value: 25 }), + (constants::DW_OP_lit26, Operation::UnsignedConstant { value: 26 }), + (constants::DW_OP_lit27, Operation::UnsignedConstant { value: 27 }), + (constants::DW_OP_lit28, Operation::UnsignedConstant { value: 28 }), + (constants::DW_OP_lit29, Operation::UnsignedConstant { value: 29 }), + (constants::DW_OP_lit30, Operation::UnsignedConstant { value: 30 }), + (constants::DW_OP_lit31, Operation::UnsignedConstant { value: 31 }), + (constants::DW_OP_reg0, Operation::Register { register: Register(0) }), + (constants::DW_OP_reg1, Operation::Register { register: Register(1) }), + (constants::DW_OP_reg2, Operation::Register { register: Register(2) }), + (constants::DW_OP_reg3, Operation::Register { register: Register(3) }), + (constants::DW_OP_reg4, Operation::Register { register: Register(4) }), + (constants::DW_OP_reg5, Operation::Register { register: Register(5) }), + (constants::DW_OP_reg6, Operation::Register { register: Register(6) }), + (constants::DW_OP_reg7, Operation::Register { register: Register(7) }), + (constants::DW_OP_reg8, Operation::Register { register: Register(8) }), + (constants::DW_OP_reg9, Operation::Register { register: Register(9) }), + (constants::DW_OP_reg10, Operation::Register { register: Register(10) }), + (constants::DW_OP_reg11, Operation::Register { register: Register(11) }), + (constants::DW_OP_reg12, Operation::Register { register: Register(12) }), + (constants::DW_OP_reg13, Operation::Register { register: Register(13) }), + (constants::DW_OP_reg14, Operation::Register { register: Register(14) }), + (constants::DW_OP_reg15, Operation::Register { register: Register(15) }), + (constants::DW_OP_reg16, Operation::Register { register: Register(16) }), + (constants::DW_OP_reg17, Operation::Register { register: Register(17) }), + (constants::DW_OP_reg18, Operation::Register { register: Register(18) }), + (constants::DW_OP_reg19, Operation::Register { register: Register(19) }), + (constants::DW_OP_reg20, Operation::Register { register: Register(20) }), + (constants::DW_OP_reg21, Operation::Register { register: Register(21) }), + (constants::DW_OP_reg22, Operation::Register { register: Register(22) }), + (constants::DW_OP_reg23, Operation::Register { register: Register(23) }), + (constants::DW_OP_reg24, Operation::Register { register: Register(24) }), + (constants::DW_OP_reg25, Operation::Register { register: Register(25) }), + (constants::DW_OP_reg26, Operation::Register { register: Register(26) }), + (constants::DW_OP_reg27, Operation::Register { register: Register(27) }), + (constants::DW_OP_reg28, Operation::Register { register: Register(28) }), + (constants::DW_OP_reg29, Operation::Register { register: Register(29) }), + (constants::DW_OP_reg30, Operation::Register { register: Register(30) }), + (constants::DW_OP_reg31, Operation::Register { register: Register(31) }), + (constants::DW_OP_nop, Operation::Nop), + (constants::DW_OP_push_object_address, Operation::PushObjectAddress), + (constants::DW_OP_form_tls_address, Operation::TLS), + (constants::DW_OP_GNU_push_tls_address, Operation::TLS), + (constants::DW_OP_call_frame_cfa, Operation::CallFrameCFA), + (constants::DW_OP_stack_value, Operation::StackValue), + ]; + + let input = []; + check_op_parse_eof(&input[..], encoding); + + for item in inputs.iter() { + let (opcode, ref result) = *item; + check_op_parse(|s| s.D8(opcode.0), result, encoding); + } + } + + #[test] + fn test_op_parse_twobyte() { + // Doesn't matter for this test. + let encoding = encoding4(); + + let inputs = [ + ( + constants::DW_OP_const1u, + 23, + Operation::UnsignedConstant { value: 23 }, + ), + ( + constants::DW_OP_const1s, + (-23i8) as u8, + Operation::SignedConstant { value: -23 }, + ), + (constants::DW_OP_pick, 7, Operation::Pick { index: 7 }), + ( + constants::DW_OP_deref_size, + 19, + Operation::Deref { + base_type: generic_type(), + size: 19, + space: false, + }, + ), + ( + constants::DW_OP_xderef_size, + 19, + Operation::Deref { + base_type: generic_type(), + size: 19, + space: true, + }, + ), + ]; + + for item in inputs.iter() { + let (opcode, arg, ref result) = *item; + check_op_parse(|s| s.D8(opcode.0).D8(arg), result, encoding); + } + } + + #[test] + fn test_op_parse_threebyte() { + // Doesn't matter for this test. + let encoding = encoding4(); + + // While bra and skip are 3-byte opcodes, they aren't tested here, + // but rather specially in their own function. + let inputs = [ + ( + constants::DW_OP_const2u, + 23, + Operation::UnsignedConstant { value: 23 }, + ), + ( + constants::DW_OP_const2s, + (-23i16) as u16, + Operation::SignedConstant { value: -23 }, + ), + ( + constants::DW_OP_call2, + 1138, + Operation::Call { + offset: DieReference::UnitRef(UnitOffset(1138)), + }, + ), + ( + constants::DW_OP_bra, + (-23i16) as u16, + Operation::Bra { target: -23 }, + ), + ( + constants::DW_OP_skip, + (-23i16) as u16, + Operation::Skip { target: -23 }, + ), + ]; + + for item in inputs.iter() { + let (opcode, arg, ref result) = *item; + check_op_parse(|s| s.D8(opcode.0).L16(arg), result, encoding); + } + } + + #[test] + fn test_op_parse_fivebyte() { + // There are some tests here that depend on address size. + let encoding = encoding4(); + + let inputs = [ + ( + constants::DW_OP_addr, + 0x1234_5678, + Operation::Address { + address: 0x1234_5678, + }, + ), + ( + constants::DW_OP_const4u, + 0x1234_5678, + Operation::UnsignedConstant { value: 0x1234_5678 }, + ), + ( + constants::DW_OP_const4s, + (-23i32) as u32, + Operation::SignedConstant { value: -23 }, + ), + ( + constants::DW_OP_call4, + 0x1234_5678, + Operation::Call { + offset: DieReference::UnitRef(UnitOffset(0x1234_5678)), + }, + ), + ( + constants::DW_OP_call_ref, + 0x1234_5678, + Operation::Call { + offset: DieReference::DebugInfoRef(DebugInfoOffset(0x1234_5678)), + }, + ), + ]; + + for item in inputs.iter() { + let (op, arg, ref expect) = *item; + check_op_parse(|s| s.D8(op.0).L32(arg), expect, encoding); + } + } + + #[test] + #[cfg(target_pointer_width = "64")] + fn test_op_parse_ninebyte() { + // There are some tests here that depend on address size. + let encoding = encoding8(); + + let inputs = [ + ( + constants::DW_OP_addr, + 0x1234_5678_1234_5678, + Operation::Address { + address: 0x1234_5678_1234_5678, + }, + ), + ( + constants::DW_OP_const8u, + 0x1234_5678_1234_5678, + Operation::UnsignedConstant { + value: 0x1234_5678_1234_5678, + }, + ), + ( + constants::DW_OP_const8s, + (-23i64) as u64, + Operation::SignedConstant { value: -23 }, + ), + ( + constants::DW_OP_call_ref, + 0x1234_5678_1234_5678, + Operation::Call { + offset: DieReference::DebugInfoRef(DebugInfoOffset(0x1234_5678_1234_5678)), + }, + ), + ]; + + for item in inputs.iter() { + let (op, arg, ref expect) = *item; + check_op_parse(|s| s.D8(op.0).L64(arg), expect, encoding); + } + } + + #[test] + fn test_op_parse_sleb() { + // Doesn't matter for this test. + let encoding = encoding4(); + + let values = [ + -1i64, + 0, + 1, + 0x100, + 0x1eee_eeee, + 0x7fff_ffff_ffff_ffff, + -0x100, + -0x1eee_eeee, + -0x7fff_ffff_ffff_ffff, + ]; + for value in values.iter() { + let mut inputs = vec![ + ( + constants::DW_OP_consts.0, + Operation::SignedConstant { value: *value }, + ), + ( + constants::DW_OP_fbreg.0, + Operation::FrameOffset { offset: *value }, + ), + ]; + + for i in 0..32 { + inputs.push(( + constants::DW_OP_breg0.0 + i, + Operation::RegisterOffset { + register: Register(i.into()), + offset: *value, + base_type: UnitOffset(0), + }, + )); + } + + for item in inputs.iter() { + let (op, ref expect) = *item; + check_op_parse(|s| s.D8(op).sleb(*value), expect, encoding); + } + } + } + + #[test] + fn test_op_parse_uleb() { + // Doesn't matter for this test. + let encoding = encoding4(); + + let values = [ + 0, + 1, + 0x100, + (!0u16).into(), + 0x1eee_eeee, + 0x7fff_ffff_ffff_ffff, + !0u64, + ]; + for value in values.iter() { + let mut inputs = vec![ + ( + constants::DW_OP_constu, + Operation::UnsignedConstant { value: *value }, + ), + ( + constants::DW_OP_plus_uconst, + Operation::PlusConstant { value: *value }, + ), + ]; + + if *value <= (!0u16).into() { + inputs.push(( + constants::DW_OP_regx, + Operation::Register { + register: Register::from_u64(*value).unwrap(), + }, + )); + } + + if *value <= (!0u32).into() { + inputs.extend(&[ + ( + constants::DW_OP_addrx, + Operation::AddressIndex { + index: DebugAddrIndex(*value as usize), + }, + ), + ( + constants::DW_OP_constx, + Operation::ConstantIndex { + index: DebugAddrIndex(*value as usize), + }, + ), + ]); + } + + // FIXME + if *value < !0u64 / 8 { + inputs.push(( + constants::DW_OP_piece, + Operation::Piece { + size_in_bits: 8 * value, + bit_offset: None, + }, + )); + } + + for item in inputs.iter() { + let (op, ref expect) = *item; + let input = Section::with_endian(Endian::Little) + .D8(op.0) + .uleb(*value) + .get_contents() + .unwrap(); + check_op_parse_simple(&input, expect, encoding); + } + } + } + + #[test] + fn test_op_parse_bregx() { + // Doesn't matter for this test. + let encoding = encoding4(); + + let uvalues = [0, 1, 0x100, !0u16]; + let svalues = [ + -1i64, + 0, + 1, + 0x100, + 0x1eee_eeee, + 0x7fff_ffff_ffff_ffff, + -0x100, + -0x1eee_eeee, + -0x7fff_ffff_ffff_ffff, + ]; + + for v1 in uvalues.iter() { + for v2 in svalues.iter() { + check_op_parse( + |s| s.D8(constants::DW_OP_bregx.0).uleb((*v1).into()).sleb(*v2), + &Operation::RegisterOffset { + register: Register(*v1), + offset: *v2, + base_type: UnitOffset(0), + }, + encoding, + ); + } + } + } + + #[test] + fn test_op_parse_bit_piece() { + // Doesn't matter for this test. + let encoding = encoding4(); + + let values = [0, 1, 0x100, 0x1eee_eeee, 0x7fff_ffff_ffff_ffff, !0u64]; + + for v1 in values.iter() { + for v2 in values.iter() { + let input = Section::with_endian(Endian::Little) + .D8(constants::DW_OP_bit_piece.0) + .uleb(*v1) + .uleb(*v2) + .get_contents() + .unwrap(); + check_op_parse_simple( + &input, + &Operation::Piece { + size_in_bits: *v1, + bit_offset: Some(*v2), + }, + encoding, + ); + } + } + } + + #[test] + fn test_op_parse_implicit_value() { + // Doesn't matter for this test. + let encoding = encoding4(); + + let data = b"hello"; + + check_op_parse( + |s| { + s.D8(constants::DW_OP_implicit_value.0) + .uleb(data.len() as u64) + .append_bytes(&data[..]) + }, + &Operation::ImplicitValue { + data: EndianSlice::new(&data[..], LittleEndian), + }, + encoding, + ); + } + + #[test] + fn test_op_parse_const_type() { + // Doesn't matter for this test. + let encoding = encoding4(); + + let data = b"hello"; + + check_op_parse( + |s| { + s.D8(constants::DW_OP_const_type.0) + .uleb(100) + .D8(data.len() as u8) + .append_bytes(&data[..]) + }, + &Operation::TypedLiteral { + base_type: UnitOffset(100), + value: EndianSlice::new(&data[..], LittleEndian), + }, + encoding, + ); + check_op_parse( + |s| { + s.D8(constants::DW_OP_GNU_const_type.0) + .uleb(100) + .D8(data.len() as u8) + .append_bytes(&data[..]) + }, + &Operation::TypedLiteral { + base_type: UnitOffset(100), + value: EndianSlice::new(&data[..], LittleEndian), + }, + encoding, + ); + } + + #[test] + fn test_op_parse_regval_type() { + // Doesn't matter for this test. + let encoding = encoding4(); + + check_op_parse( + |s| s.D8(constants::DW_OP_regval_type.0).uleb(1).uleb(100), + &Operation::RegisterOffset { + register: Register(1), + offset: 0, + base_type: UnitOffset(100), + }, + encoding, + ); + check_op_parse( + |s| s.D8(constants::DW_OP_GNU_regval_type.0).uleb(1).uleb(100), + &Operation::RegisterOffset { + register: Register(1), + offset: 0, + base_type: UnitOffset(100), + }, + encoding, + ); + } + + #[test] + fn test_op_parse_deref_type() { + // Doesn't matter for this test. + let encoding = encoding4(); + + check_op_parse( + |s| s.D8(constants::DW_OP_deref_type.0).D8(8).uleb(100), + &Operation::Deref { + base_type: UnitOffset(100), + size: 8, + space: false, + }, + encoding, + ); + check_op_parse( + |s| s.D8(constants::DW_OP_GNU_deref_type.0).D8(8).uleb(100), + &Operation::Deref { + base_type: UnitOffset(100), + size: 8, + space: false, + }, + encoding, + ); + check_op_parse( + |s| s.D8(constants::DW_OP_xderef_type.0).D8(8).uleb(100), + &Operation::Deref { + base_type: UnitOffset(100), + size: 8, + space: true, + }, + encoding, + ); + } + + #[test] + fn test_op_convert() { + // Doesn't matter for this test. + let encoding = encoding4(); + + check_op_parse( + |s| s.D8(constants::DW_OP_convert.0).uleb(100), + &Operation::Convert { + base_type: UnitOffset(100), + }, + encoding, + ); + check_op_parse( + |s| s.D8(constants::DW_OP_GNU_convert.0).uleb(100), + &Operation::Convert { + base_type: UnitOffset(100), + }, + encoding, + ); + } + + #[test] + fn test_op_reinterpret() { + // Doesn't matter for this test. + let encoding = encoding4(); + + check_op_parse( + |s| s.D8(constants::DW_OP_reinterpret.0).uleb(100), + &Operation::Reinterpret { + base_type: UnitOffset(100), + }, + encoding, + ); + check_op_parse( + |s| s.D8(constants::DW_OP_GNU_reinterpret.0).uleb(100), + &Operation::Reinterpret { + base_type: UnitOffset(100), + }, + encoding, + ); + } + + #[test] + fn test_op_parse_implicit_pointer() { + for op in &[ + constants::DW_OP_implicit_pointer, + constants::DW_OP_GNU_implicit_pointer, + ] { + check_op_parse( + |s| s.D8(op.0).D32(0x1234_5678).sleb(0x123), + &Operation::ImplicitPointer { + value: DebugInfoOffset(0x1234_5678), + byte_offset: 0x123, + }, + encoding4(), + ); + + check_op_parse( + |s| s.D8(op.0).D64(0x1234_5678).sleb(0x123), + &Operation::ImplicitPointer { + value: DebugInfoOffset(0x1234_5678), + byte_offset: 0x123, + }, + encoding8(), + ); + + check_op_parse( + |s| s.D8(op.0).D64(0x1234_5678).sleb(0x123), + &Operation::ImplicitPointer { + value: DebugInfoOffset(0x1234_5678), + byte_offset: 0x123, + }, + Encoding { + format: Format::Dwarf32, + version: 2, + address_size: 8, + }, + ) + } + } + + #[test] + fn test_op_parse_entry_value() { + for op in &[ + constants::DW_OP_entry_value, + constants::DW_OP_GNU_entry_value, + ] { + let data = b"hello"; + check_op_parse( + |s| s.D8(op.0).uleb(data.len() as u64).append_bytes(&data[..]), + &Operation::EntryValue { + expression: EndianSlice::new(&data[..], LittleEndian), + }, + encoding4(), + ); + } + } + + #[test] + fn test_op_parse_gnu_parameter_ref() { + check_op_parse( + |s| s.D8(constants::DW_OP_GNU_parameter_ref.0).D32(0x1234_5678), + &Operation::ParameterRef { + offset: UnitOffset(0x1234_5678), + }, + encoding4(), + ) + } + + #[test] + fn test_op_wasm() { + // Doesn't matter for this test. + let encoding = encoding4(); + + check_op_parse( + |s| s.D8(constants::DW_OP_WASM_location.0).D8(0).uleb(1000), + &Operation::WasmLocal { index: 1000 }, + encoding, + ); + check_op_parse( + |s| s.D8(constants::DW_OP_WASM_location.0).D8(1).uleb(1000), + &Operation::WasmGlobal { index: 1000 }, + encoding, + ); + check_op_parse( + |s| s.D8(constants::DW_OP_WASM_location.0).D8(2).uleb(1000), + &Operation::WasmStack { index: 1000 }, + encoding, + ); + check_op_parse( + |s| s.D8(constants::DW_OP_WASM_location.0).D8(3).D32(1000), + &Operation::WasmGlobal { index: 1000 }, + encoding, + ); + } + + enum AssemblerEntry { + Op(constants::DwOp), + Mark(u8), + Branch(u8), + U8(u8), + U16(u16), + U32(u32), + U64(u64), + Uleb(u64), + Sleb(u64), + } + + fn assemble(entries: &[AssemblerEntry]) -> Vec<u8> { + let mut result = Vec::new(); + + struct Marker(Option<usize>, Vec<usize>); + + let mut markers = Vec::new(); + for _ in 0..256 { + markers.push(Marker(None, Vec::new())); + } + + fn write(stack: &mut Vec<u8>, index: usize, mut num: u64, nbytes: u8) { + for i in 0..nbytes as usize { + stack[index + i] = (num & 0xff) as u8; + num >>= 8; + } + } + + fn push(stack: &mut Vec<u8>, num: u64, nbytes: u8) { + let index = stack.len(); + for _ in 0..nbytes { + stack.push(0); + } + write(stack, index, num, nbytes); + } + + for item in entries { + match *item { + AssemblerEntry::Op(op) => result.push(op.0), + AssemblerEntry::Mark(num) => { + assert!(markers[num as usize].0.is_none()); + markers[num as usize].0 = Some(result.len()); + } + AssemblerEntry::Branch(num) => { + markers[num as usize].1.push(result.len()); + push(&mut result, 0, 2); + } + AssemblerEntry::U8(num) => result.push(num), + AssemblerEntry::U16(num) => push(&mut result, u64::from(num), 2), + AssemblerEntry::U32(num) => push(&mut result, u64::from(num), 4), + AssemblerEntry::U64(num) => push(&mut result, num, 8), + AssemblerEntry::Uleb(num) => { + leb128::write::unsigned(&mut result, num).unwrap(); + } + AssemblerEntry::Sleb(num) => { + leb128::write::signed(&mut result, num as i64).unwrap(); + } + } + } + + // Update all the branches. + for marker in markers { + if let Some(offset) = marker.0 { + for branch_offset in marker.1 { + let delta = offset.wrapping_sub(branch_offset + 2) as u64; + write(&mut result, branch_offset, delta, 2); + } + } + } + + result + } + + fn check_eval_with_args<F>( + program: &[AssemblerEntry], + expect: Result<&[Piece<EndianSlice<LittleEndian>>]>, + encoding: Encoding, + object_address: Option<u64>, + initial_value: Option<u64>, + max_iterations: Option<u32>, + f: F, + ) where + for<'a> F: Fn( + &mut Evaluation<EndianSlice<'a, LittleEndian>>, + EvaluationResult<EndianSlice<'a, LittleEndian>>, + ) -> Result<EvaluationResult<EndianSlice<'a, LittleEndian>>>, + { + let bytes = assemble(program); + let bytes = EndianSlice::new(&bytes, LittleEndian); + + let mut eval = Evaluation::new(bytes, encoding); + + if let Some(val) = object_address { + eval.set_object_address(val); + } + if let Some(val) = initial_value { + eval.set_initial_value(val); + } + if let Some(val) = max_iterations { + eval.set_max_iterations(val); + } + + let result = match eval.evaluate() { + Err(e) => Err(e), + Ok(r) => f(&mut eval, r), + }; + + match (result, expect) { + (Ok(EvaluationResult::Complete), Ok(pieces)) => { + let vec = eval.result(); + assert_eq!(vec.len(), pieces.len()); + for i in 0..pieces.len() { + assert_eq!(vec[i], pieces[i]); + } + } + (Err(f1), Err(f2)) => { + assert_eq!(f1, f2); + } + otherwise => panic!("Unexpected result: {:?}", otherwise), + } + } + + fn check_eval( + program: &[AssemblerEntry], + expect: Result<&[Piece<EndianSlice<LittleEndian>>]>, + encoding: Encoding, + ) { + check_eval_with_args(program, expect, encoding, None, None, None, |_, result| { + Ok(result) + }); + } + + #[test] + fn test_eval_arith() { + // It's nice if an operation and its arguments can fit on a single + // line in the test program. + use self::AssemblerEntry::*; + use crate::constants::*; + + // Indices of marks in the assembly. + let done = 0; + let fail = 1; + + #[rustfmt::skip] + let program = [ + Op(DW_OP_const1u), U8(23), + Op(DW_OP_const1s), U8((-23i8) as u8), + Op(DW_OP_plus), + Op(DW_OP_bra), Branch(fail), + + Op(DW_OP_const2u), U16(23), + Op(DW_OP_const2s), U16((-23i16) as u16), + Op(DW_OP_plus), + Op(DW_OP_bra), Branch(fail), + + Op(DW_OP_const4u), U32(0x1111_2222), + Op(DW_OP_const4s), U32((-0x1111_2222i32) as u32), + Op(DW_OP_plus), + Op(DW_OP_bra), Branch(fail), + + // Plus should overflow. + Op(DW_OP_const1s), U8(0xff), + Op(DW_OP_const1u), U8(1), + Op(DW_OP_plus), + Op(DW_OP_bra), Branch(fail), + + Op(DW_OP_const1s), U8(0xff), + Op(DW_OP_plus_uconst), Uleb(1), + Op(DW_OP_bra), Branch(fail), + + // Minus should underflow. + Op(DW_OP_const1s), U8(0), + Op(DW_OP_const1u), U8(1), + Op(DW_OP_minus), + Op(DW_OP_const1s), U8(0xff), + Op(DW_OP_ne), + Op(DW_OP_bra), Branch(fail), + + Op(DW_OP_const1s), U8(0xff), + Op(DW_OP_abs), + Op(DW_OP_const1u), U8(1), + Op(DW_OP_minus), + Op(DW_OP_bra), Branch(fail), + + Op(DW_OP_const4u), U32(0xf078_fffe), + Op(DW_OP_const4u), U32(0x0f87_0001), + Op(DW_OP_and), + Op(DW_OP_bra), Branch(fail), + + Op(DW_OP_const4u), U32(0xf078_fffe), + Op(DW_OP_const4u), U32(0xf000_00fe), + Op(DW_OP_and), + Op(DW_OP_const4u), U32(0xf000_00fe), + Op(DW_OP_ne), + Op(DW_OP_bra), Branch(fail), + + // Division is signed. + Op(DW_OP_const1s), U8(0xfe), + Op(DW_OP_const1s), U8(2), + Op(DW_OP_div), + Op(DW_OP_plus_uconst), Uleb(1), + Op(DW_OP_bra), Branch(fail), + + // Mod is unsigned. + Op(DW_OP_const1s), U8(0xfd), + Op(DW_OP_const1s), U8(2), + Op(DW_OP_mod), + Op(DW_OP_neg), + Op(DW_OP_plus_uconst), Uleb(1), + Op(DW_OP_bra), Branch(fail), + + // Overflow is defined for multiplication. + Op(DW_OP_const4u), U32(0x8000_0001), + Op(DW_OP_lit2), + Op(DW_OP_mul), + Op(DW_OP_lit2), + Op(DW_OP_ne), + Op(DW_OP_bra), Branch(fail), + + Op(DW_OP_const4u), U32(0xf0f0_f0f0), + Op(DW_OP_const4u), U32(0xf0f0_f0f0), + Op(DW_OP_xor), + Op(DW_OP_bra), Branch(fail), + + Op(DW_OP_const4u), U32(0xf0f0_f0f0), + Op(DW_OP_const4u), U32(0x0f0f_0f0f), + Op(DW_OP_or), + Op(DW_OP_not), + Op(DW_OP_bra), Branch(fail), + + // In 32 bit mode, values are truncated. + Op(DW_OP_const8u), U64(0xffff_ffff_0000_0000), + Op(DW_OP_lit2), + Op(DW_OP_div), + Op(DW_OP_bra), Branch(fail), + + Op(DW_OP_const1u), U8(0xff), + Op(DW_OP_lit1), + Op(DW_OP_shl), + Op(DW_OP_const2u), U16(0x1fe), + Op(DW_OP_ne), + Op(DW_OP_bra), Branch(fail), + + Op(DW_OP_const1u), U8(0xff), + Op(DW_OP_const1u), U8(50), + Op(DW_OP_shl), + Op(DW_OP_bra), Branch(fail), + + // Absurd shift. + Op(DW_OP_const1u), U8(0xff), + Op(DW_OP_const1s), U8(0xff), + Op(DW_OP_shl), + Op(DW_OP_bra), Branch(fail), + + Op(DW_OP_const1s), U8(0xff), + Op(DW_OP_lit1), + Op(DW_OP_shr), + Op(DW_OP_const4u), U32(0x7fff_ffff), + Op(DW_OP_ne), + Op(DW_OP_bra), Branch(fail), + + Op(DW_OP_const1s), U8(0xff), + Op(DW_OP_const1u), U8(0xff), + Op(DW_OP_shr), + Op(DW_OP_bra), Branch(fail), + + Op(DW_OP_const1s), U8(0xff), + Op(DW_OP_lit1), + Op(DW_OP_shra), + Op(DW_OP_const1s), U8(0xff), + Op(DW_OP_ne), + Op(DW_OP_bra), Branch(fail), + + Op(DW_OP_const1s), U8(0xff), + Op(DW_OP_const1u), U8(0xff), + Op(DW_OP_shra), + Op(DW_OP_const1s), U8(0xff), + Op(DW_OP_ne), + Op(DW_OP_bra), Branch(fail), + + // Success. + Op(DW_OP_lit0), + Op(DW_OP_nop), + Op(DW_OP_skip), Branch(done), + + Mark(fail), + Op(DW_OP_lit1), + + Mark(done), + Op(DW_OP_stack_value), + ]; + + let result = [Piece { + size_in_bits: None, + bit_offset: None, + location: Location::Value { + value: Value::Generic(0), + }, + }]; + + check_eval(&program, Ok(&result), encoding4()); + } + + #[test] + fn test_eval_arith64() { + // It's nice if an operation and its arguments can fit on a single + // line in the test program. + use self::AssemblerEntry::*; + use crate::constants::*; + + // Indices of marks in the assembly. + let done = 0; + let fail = 1; + + #[rustfmt::skip] + let program = [ + Op(DW_OP_const8u), U64(0x1111_2222_3333_4444), + Op(DW_OP_const8s), U64((-0x1111_2222_3333_4444i64) as u64), + Op(DW_OP_plus), + Op(DW_OP_bra), Branch(fail), + + Op(DW_OP_constu), Uleb(0x1111_2222_3333_4444), + Op(DW_OP_consts), Sleb((-0x1111_2222_3333_4444i64) as u64), + Op(DW_OP_plus), + Op(DW_OP_bra), Branch(fail), + + Op(DW_OP_lit1), + Op(DW_OP_plus_uconst), Uleb(!0u64), + Op(DW_OP_bra), Branch(fail), + + Op(DW_OP_lit1), + Op(DW_OP_neg), + Op(DW_OP_not), + Op(DW_OP_bra), Branch(fail), + + Op(DW_OP_const8u), U64(0x8000_0000_0000_0000), + Op(DW_OP_const1u), U8(63), + Op(DW_OP_shr), + Op(DW_OP_lit1), + Op(DW_OP_ne), + Op(DW_OP_bra), Branch(fail), + + Op(DW_OP_const8u), U64(0x8000_0000_0000_0000), + Op(DW_OP_const1u), U8(62), + Op(DW_OP_shra), + Op(DW_OP_plus_uconst), Uleb(2), + Op(DW_OP_bra), Branch(fail), + + Op(DW_OP_lit1), + Op(DW_OP_const1u), U8(63), + Op(DW_OP_shl), + Op(DW_OP_const8u), U64(0x8000_0000_0000_0000), + Op(DW_OP_ne), + Op(DW_OP_bra), Branch(fail), + + // Success. + Op(DW_OP_lit0), + Op(DW_OP_nop), + Op(DW_OP_skip), Branch(done), + + Mark(fail), + Op(DW_OP_lit1), + + Mark(done), + Op(DW_OP_stack_value), + ]; + + let result = [Piece { + size_in_bits: None, + bit_offset: None, + location: Location::Value { + value: Value::Generic(0), + }, + }]; + + check_eval(&program, Ok(&result), encoding8()); + } + + #[test] + fn test_eval_compare() { + // It's nice if an operation and its arguments can fit on a single + // line in the test program. + use self::AssemblerEntry::*; + use crate::constants::*; + + // Indices of marks in the assembly. + let done = 0; + let fail = 1; + + #[rustfmt::skip] + let program = [ + // Comparisons are signed. + Op(DW_OP_const1s), U8(1), + Op(DW_OP_const1s), U8(0xff), + Op(DW_OP_lt), + Op(DW_OP_bra), Branch(fail), + + Op(DW_OP_const1s), U8(0xff), + Op(DW_OP_const1s), U8(1), + Op(DW_OP_gt), + Op(DW_OP_bra), Branch(fail), + + Op(DW_OP_const1s), U8(1), + Op(DW_OP_const1s), U8(0xff), + Op(DW_OP_le), + Op(DW_OP_bra), Branch(fail), + + Op(DW_OP_const1s), U8(0xff), + Op(DW_OP_const1s), U8(1), + Op(DW_OP_ge), + Op(DW_OP_bra), Branch(fail), + + Op(DW_OP_const1s), U8(0xff), + Op(DW_OP_const1s), U8(1), + Op(DW_OP_eq), + Op(DW_OP_bra), Branch(fail), + + Op(DW_OP_const4s), U32(1), + Op(DW_OP_const1s), U8(1), + Op(DW_OP_ne), + Op(DW_OP_bra), Branch(fail), + + // Success. + Op(DW_OP_lit0), + Op(DW_OP_nop), + Op(DW_OP_skip), Branch(done), + + Mark(fail), + Op(DW_OP_lit1), + + Mark(done), + Op(DW_OP_stack_value), + ]; + + let result = [Piece { + size_in_bits: None, + bit_offset: None, + location: Location::Value { + value: Value::Generic(0), + }, + }]; + + check_eval(&program, Ok(&result), encoding4()); + } + + #[test] + fn test_eval_stack() { + // It's nice if an operation and its arguments can fit on a single + // line in the test program. + use self::AssemblerEntry::*; + use crate::constants::*; + + #[rustfmt::skip] + let program = [ + Op(DW_OP_lit17), // -- 17 + Op(DW_OP_dup), // -- 17 17 + Op(DW_OP_over), // -- 17 17 17 + Op(DW_OP_minus), // -- 17 0 + Op(DW_OP_swap), // -- 0 17 + Op(DW_OP_dup), // -- 0 17 17 + Op(DW_OP_plus_uconst), Uleb(1), // -- 0 17 18 + Op(DW_OP_rot), // -- 18 0 17 + Op(DW_OP_pick), U8(2), // -- 18 0 17 18 + Op(DW_OP_pick), U8(3), // -- 18 0 17 18 18 + Op(DW_OP_minus), // -- 18 0 17 0 + Op(DW_OP_drop), // -- 18 0 17 + Op(DW_OP_swap), // -- 18 17 0 + Op(DW_OP_drop), // -- 18 17 + Op(DW_OP_minus), // -- 1 + Op(DW_OP_stack_value), + ]; + + let result = [Piece { + size_in_bits: None, + bit_offset: None, + location: Location::Value { + value: Value::Generic(1), + }, + }]; + + check_eval(&program, Ok(&result), encoding4()); + } + + #[test] + fn test_eval_lit_and_reg() { + // It's nice if an operation and its arguments can fit on a single + // line in the test program. + use self::AssemblerEntry::*; + use crate::constants::*; + + let mut program = Vec::new(); + program.push(Op(DW_OP_lit0)); + for i in 0..32 { + program.push(Op(DwOp(DW_OP_lit0.0 + i))); + program.push(Op(DwOp(DW_OP_breg0.0 + i))); + program.push(Sleb(u64::from(i))); + program.push(Op(DW_OP_plus)); + program.push(Op(DW_OP_plus)); + } + + program.push(Op(DW_OP_bregx)); + program.push(Uleb(0x1234)); + program.push(Sleb(0x1234)); + program.push(Op(DW_OP_plus)); + + program.push(Op(DW_OP_stack_value)); + + let result = [Piece { + size_in_bits: None, + bit_offset: None, + location: Location::Value { + value: Value::Generic(496), + }, + }]; + + check_eval_with_args( + &program, + Ok(&result), + encoding4(), + None, + None, + None, + |eval, mut result| { + while result != EvaluationResult::Complete { + result = eval.resume_with_register(match result { + EvaluationResult::RequiresRegister { + register, + base_type, + } => { + assert_eq!(base_type, UnitOffset(0)); + Value::Generic(u64::from(register.0).wrapping_neg()) + } + _ => panic!(), + })?; + } + Ok(result) + }, + ); + } + + #[test] + fn test_eval_memory() { + // It's nice if an operation and its arguments can fit on a single + // line in the test program. + use self::AssemblerEntry::*; + use crate::constants::*; + + // Indices of marks in the assembly. + let done = 0; + let fail = 1; + + #[rustfmt::skip] + let program = [ + Op(DW_OP_addr), U32(0x7fff_ffff), + Op(DW_OP_deref), + Op(DW_OP_const4u), U32(0xffff_fffc), + Op(DW_OP_ne), + Op(DW_OP_bra), Branch(fail), + + Op(DW_OP_addr), U32(0x7fff_ffff), + Op(DW_OP_deref_size), U8(2), + Op(DW_OP_const4u), U32(0xfffc), + Op(DW_OP_ne), + Op(DW_OP_bra), Branch(fail), + + Op(DW_OP_lit1), + Op(DW_OP_addr), U32(0x7fff_ffff), + Op(DW_OP_xderef), + Op(DW_OP_const4u), U32(0xffff_fffd), + Op(DW_OP_ne), + Op(DW_OP_bra), Branch(fail), + + Op(DW_OP_lit1), + Op(DW_OP_addr), U32(0x7fff_ffff), + Op(DW_OP_xderef_size), U8(2), + Op(DW_OP_const4u), U32(0xfffd), + Op(DW_OP_ne), + Op(DW_OP_bra), Branch(fail), + + Op(DW_OP_lit17), + Op(DW_OP_form_tls_address), + Op(DW_OP_constu), Uleb(!17), + Op(DW_OP_ne), + Op(DW_OP_bra), Branch(fail), + + Op(DW_OP_lit17), + Op(DW_OP_GNU_push_tls_address), + Op(DW_OP_constu), Uleb(!17), + Op(DW_OP_ne), + Op(DW_OP_bra), Branch(fail), + + Op(DW_OP_addrx), Uleb(0x10), + Op(DW_OP_deref), + Op(DW_OP_const4u), U32(0x4040), + Op(DW_OP_ne), + Op(DW_OP_bra), Branch(fail), + + Op(DW_OP_constx), Uleb(17), + Op(DW_OP_form_tls_address), + Op(DW_OP_constu), Uleb(!27), + Op(DW_OP_ne), + Op(DW_OP_bra), Branch(fail), + + // Success. + Op(DW_OP_lit0), + Op(DW_OP_nop), + Op(DW_OP_skip), Branch(done), + + Mark(fail), + Op(DW_OP_lit1), + + Mark(done), + Op(DW_OP_stack_value), + ]; + + let result = [Piece { + size_in_bits: None, + bit_offset: None, + location: Location::Value { + value: Value::Generic(0), + }, + }]; + + check_eval_with_args( + &program, + Ok(&result), + encoding4(), + None, + None, + None, + |eval, mut result| { + while result != EvaluationResult::Complete { + result = match result { + EvaluationResult::RequiresMemory { + address, + size, + space, + base_type, + } => { + assert_eq!(base_type, UnitOffset(0)); + let mut v = address << 2; + if let Some(value) = space { + v += value; + } + v &= (1u64 << (8 * size)) - 1; + eval.resume_with_memory(Value::Generic(v))? + } + EvaluationResult::RequiresTls(slot) => eval.resume_with_tls(!slot)?, + EvaluationResult::RequiresRelocatedAddress(address) => { + eval.resume_with_relocated_address(address)? + } + EvaluationResult::RequiresIndexedAddress { index, relocate } => { + if relocate { + eval.resume_with_indexed_address(0x1000 + index.0 as u64)? + } else { + eval.resume_with_indexed_address(10 + index.0 as u64)? + } + } + _ => panic!(), + }; + } + + Ok(result) + }, + ); + } + + #[test] + fn test_eval_register() { + // It's nice if an operation and its arguments can fit on a single + // line in the test program. + use self::AssemblerEntry::*; + use crate::constants::*; + + for i in 0..32 { + #[rustfmt::skip] + let program = [ + Op(DwOp(DW_OP_reg0.0 + i)), + // Included only in the "bad" run. + Op(DW_OP_lit23), + ]; + let ok_result = [Piece { + size_in_bits: None, + bit_offset: None, + location: Location::Register { + register: Register(i.into()), + }, + }]; + + check_eval(&program[..1], Ok(&ok_result), encoding4()); + + check_eval( + &program, + Err(Error::InvalidExpressionTerminator(1)), + encoding4(), + ); + } + + #[rustfmt::skip] + let program = [ + Op(DW_OP_regx), Uleb(0x1234) + ]; + + let result = [Piece { + size_in_bits: None, + bit_offset: None, + location: Location::Register { + register: Register(0x1234), + }, + }]; + + check_eval(&program, Ok(&result), encoding4()); + } + + #[test] + fn test_eval_context() { + // It's nice if an operation and its arguments can fit on a single + // line in the test program. + use self::AssemblerEntry::*; + use crate::constants::*; + + // Test `frame_base` and `call_frame_cfa` callbacks. + #[rustfmt::skip] + let program = [ + Op(DW_OP_fbreg), Sleb((-8i8) as u64), + Op(DW_OP_call_frame_cfa), + Op(DW_OP_plus), + Op(DW_OP_neg), + Op(DW_OP_stack_value) + ]; + + let result = [Piece { + size_in_bits: None, + bit_offset: None, + location: Location::Value { + value: Value::Generic(9), + }, + }]; + + check_eval_with_args( + &program, + Ok(&result), + encoding8(), + None, + None, + None, + |eval, result| { + match result { + EvaluationResult::RequiresFrameBase => {} + _ => panic!(), + }; + match eval.resume_with_frame_base(0x0123_4567_89ab_cdef)? { + EvaluationResult::RequiresCallFrameCfa => {} + _ => panic!(), + }; + eval.resume_with_call_frame_cfa(0xfedc_ba98_7654_3210) + }, + ); + + // Test `evaluate_entry_value` callback. + #[rustfmt::skip] + let program = [ + Op(DW_OP_entry_value), Uleb(8), U64(0x1234_5678), + Op(DW_OP_stack_value) + ]; + + let result = [Piece { + size_in_bits: None, + bit_offset: None, + location: Location::Value { + value: Value::Generic(0x1234_5678), + }, + }]; + + check_eval_with_args( + &program, + Ok(&result), + encoding8(), + None, + None, + None, + |eval, result| { + let entry_value = match result { + EvaluationResult::RequiresEntryValue(mut expression) => { + expression.0.read_u64()? + } + _ => panic!(), + }; + eval.resume_with_entry_value(Value::Generic(entry_value)) + }, + ); + + // Test missing `object_address` field. + #[rustfmt::skip] + let program = [ + Op(DW_OP_push_object_address), + ]; + + check_eval_with_args( + &program, + Err(Error::InvalidPushObjectAddress), + encoding4(), + None, + None, + None, + |_, _| panic!(), + ); + + // Test `object_address` field. + #[rustfmt::skip] + let program = [ + Op(DW_OP_push_object_address), + Op(DW_OP_stack_value), + ]; + + let result = [Piece { + size_in_bits: None, + bit_offset: None, + location: Location::Value { + value: Value::Generic(0xff), + }, + }]; + + check_eval_with_args( + &program, + Ok(&result), + encoding8(), + Some(0xff), + None, + None, + |_, result| Ok(result), + ); + + // Test `initial_value` field. + #[rustfmt::skip] + let program = [ + ]; + + let result = [Piece { + size_in_bits: None, + bit_offset: None, + location: Location::Address { + address: 0x1234_5678, + }, + }]; + + check_eval_with_args( + &program, + Ok(&result), + encoding8(), + None, + Some(0x1234_5678), + None, + |_, result| Ok(result), + ); + } + + #[test] + fn test_eval_empty_stack() { + // It's nice if an operation and its arguments can fit on a single + // line in the test program. + use self::AssemblerEntry::*; + use crate::constants::*; + + #[rustfmt::skip] + let program = [ + Op(DW_OP_stack_value) + ]; + + check_eval(&program, Err(Error::NotEnoughStackItems), encoding4()); + } + + #[test] + fn test_eval_call() { + // It's nice if an operation and its arguments can fit on a single + // line in the test program. + use self::AssemblerEntry::*; + use crate::constants::*; + + #[rustfmt::skip] + let program = [ + Op(DW_OP_lit23), + Op(DW_OP_call2), U16(0x7755), + Op(DW_OP_call4), U32(0x7755_aaee), + Op(DW_OP_call_ref), U32(0x7755_aaee), + Op(DW_OP_stack_value) + ]; + + let result = [Piece { + size_in_bits: None, + bit_offset: None, + location: Location::Value { + value: Value::Generic(23), + }, + }]; + + check_eval_with_args( + &program, + Ok(&result), + encoding4(), + None, + None, + None, + |eval, result| { + let buf = EndianSlice::new(&[], LittleEndian); + match result { + EvaluationResult::RequiresAtLocation(_) => {} + _ => panic!(), + }; + + eval.resume_with_at_location(buf)?; + + match result { + EvaluationResult::RequiresAtLocation(_) => {} + _ => panic!(), + }; + + eval.resume_with_at_location(buf)?; + + match result { + EvaluationResult::RequiresAtLocation(_) => {} + _ => panic!(), + }; + + eval.resume_with_at_location(buf) + }, + ); + + // DW_OP_lit2 DW_OP_mul + const SUBR: &[u8] = &[0x32, 0x1e]; + + let result = [Piece { + size_in_bits: None, + bit_offset: None, + location: Location::Value { + value: Value::Generic(184), + }, + }]; + + check_eval_with_args( + &program, + Ok(&result), + encoding4(), + None, + None, + None, + |eval, result| { + let buf = EndianSlice::new(SUBR, LittleEndian); + match result { + EvaluationResult::RequiresAtLocation(_) => {} + _ => panic!(), + }; + + eval.resume_with_at_location(buf)?; + + match result { + EvaluationResult::RequiresAtLocation(_) => {} + _ => panic!(), + }; + + eval.resume_with_at_location(buf)?; + + match result { + EvaluationResult::RequiresAtLocation(_) => {} + _ => panic!(), + }; + + eval.resume_with_at_location(buf) + }, + ); + } + + #[test] + fn test_eval_pieces() { + // It's nice if an operation and its arguments can fit on a single + // line in the test program. + use self::AssemblerEntry::*; + use crate::constants::*; + + // Example from DWARF 2.6.1.3. + #[rustfmt::skip] + let program = [ + Op(DW_OP_reg3), + Op(DW_OP_piece), Uleb(4), + Op(DW_OP_reg4), + Op(DW_OP_piece), Uleb(2), + ]; + + let result = [ + Piece { + size_in_bits: Some(32), + bit_offset: None, + location: Location::Register { + register: Register(3), + }, + }, + Piece { + size_in_bits: Some(16), + bit_offset: None, + location: Location::Register { + register: Register(4), + }, + }, + ]; + + check_eval(&program, Ok(&result), encoding4()); + + // Example from DWARF 2.6.1.3 (but hacked since dealing with fbreg + // in the tests is a pain). + #[rustfmt::skip] + let program = [ + Op(DW_OP_reg0), + Op(DW_OP_piece), Uleb(4), + Op(DW_OP_piece), Uleb(4), + Op(DW_OP_addr), U32(0x7fff_ffff), + Op(DW_OP_piece), Uleb(4), + ]; + + let result = [ + Piece { + size_in_bits: Some(32), + bit_offset: None, + location: Location::Register { + register: Register(0), + }, + }, + Piece { + size_in_bits: Some(32), + bit_offset: None, + location: Location::Empty, + }, + Piece { + size_in_bits: Some(32), + bit_offset: None, + location: Location::Address { + address: 0x7fff_ffff, + }, + }, + ]; + + check_eval_with_args( + &program, + Ok(&result), + encoding4(), + None, + None, + None, + |eval, mut result| { + while result != EvaluationResult::Complete { + result = match result { + EvaluationResult::RequiresRelocatedAddress(address) => { + eval.resume_with_relocated_address(address)? + } + _ => panic!(), + }; + } + + Ok(result) + }, + ); + + #[rustfmt::skip] + let program = [ + Op(DW_OP_implicit_value), Uleb(5), + U8(23), U8(24), U8(25), U8(26), U8(0), + ]; + + const BYTES: &[u8] = &[23, 24, 25, 26, 0]; + + let result = [Piece { + size_in_bits: None, + bit_offset: None, + location: Location::Bytes { + value: EndianSlice::new(BYTES, LittleEndian), + }, + }]; + + check_eval(&program, Ok(&result), encoding4()); + + #[rustfmt::skip] + let program = [ + Op(DW_OP_lit7), + Op(DW_OP_stack_value), + Op(DW_OP_bit_piece), Uleb(5), Uleb(0), + Op(DW_OP_bit_piece), Uleb(3), Uleb(0), + ]; + + let result = [ + Piece { + size_in_bits: Some(5), + bit_offset: Some(0), + location: Location::Value { + value: Value::Generic(7), + }, + }, + Piece { + size_in_bits: Some(3), + bit_offset: Some(0), + location: Location::Empty, + }, + ]; + + check_eval(&program, Ok(&result), encoding4()); + + #[rustfmt::skip] + let program = [ + Op(DW_OP_lit7), + ]; + + let result = [Piece { + size_in_bits: None, + bit_offset: None, + location: Location::Address { address: 7 }, + }]; + + check_eval(&program, Ok(&result), encoding4()); + + #[rustfmt::skip] + let program = [ + Op(DW_OP_implicit_pointer), U32(0x1234_5678), Sleb(0x123), + ]; + + let result = [Piece { + size_in_bits: None, + bit_offset: None, + location: Location::ImplicitPointer { + value: DebugInfoOffset(0x1234_5678), + byte_offset: 0x123, + }, + }]; + + check_eval(&program, Ok(&result), encoding4()); + + #[rustfmt::skip] + let program = [ + Op(DW_OP_reg3), + Op(DW_OP_piece), Uleb(4), + Op(DW_OP_reg4), + ]; + + check_eval(&program, Err(Error::InvalidPiece), encoding4()); + + #[rustfmt::skip] + let program = [ + Op(DW_OP_reg3), + Op(DW_OP_piece), Uleb(4), + Op(DW_OP_lit0), + ]; + + check_eval(&program, Err(Error::InvalidPiece), encoding4()); + } + + #[test] + fn test_eval_max_iterations() { + // It's nice if an operation and its arguments can fit on a single + // line in the test program. + use self::AssemblerEntry::*; + use crate::constants::*; + + #[rustfmt::skip] + let program = [ + Mark(1), + Op(DW_OP_skip), Branch(1), + ]; + + check_eval_with_args( + &program, + Err(Error::TooManyIterations), + encoding4(), + None, + None, + Some(150), + |_, _| panic!(), + ); + } + + #[test] + fn test_eval_typed_stack() { + use self::AssemblerEntry::*; + use crate::constants::*; + + let base_types = [ + ValueType::Generic, + ValueType::U16, + ValueType::U32, + ValueType::F32, + ]; + + // TODO: convert, reinterpret + #[rustfmt::skip] + let tests = [ + ( + &[ + Op(DW_OP_const_type), Uleb(1), U8(2), U16(0x1234), + Op(DW_OP_stack_value), + ][..], + Value::U16(0x1234), + ), + ( + &[ + Op(DW_OP_regval_type), Uleb(0x1234), Uleb(1), + Op(DW_OP_stack_value), + ][..], + Value::U16(0x2340), + ), + ( + &[ + Op(DW_OP_addr), U32(0x7fff_ffff), + Op(DW_OP_deref_type), U8(2), Uleb(1), + Op(DW_OP_stack_value), + ][..], + Value::U16(0xfff0), + ), + ( + &[ + Op(DW_OP_lit1), + Op(DW_OP_addr), U32(0x7fff_ffff), + Op(DW_OP_xderef_type), U8(2), Uleb(1), + Op(DW_OP_stack_value), + ][..], + Value::U16(0xfff1), + ), + ( + &[ + Op(DW_OP_const_type), Uleb(1), U8(2), U16(0x1234), + Op(DW_OP_convert), Uleb(2), + Op(DW_OP_stack_value), + ][..], + Value::U32(0x1234), + ), + ( + &[ + Op(DW_OP_const_type), Uleb(2), U8(4), U32(0x3f80_0000), + Op(DW_OP_reinterpret), Uleb(3), + Op(DW_OP_stack_value), + ][..], + Value::F32(1.0), + ), + ]; + for &(program, value) in &tests { + let result = [Piece { + size_in_bits: None, + bit_offset: None, + location: Location::Value { value }, + }]; + + check_eval_with_args( + program, + Ok(&result), + encoding4(), + None, + None, + None, + |eval, mut result| { + while result != EvaluationResult::Complete { + result = match result { + EvaluationResult::RequiresMemory { + address, + size, + space, + base_type, + } => { + let mut v = address << 4; + if let Some(value) = space { + v += value; + } + v &= (1u64 << (8 * size)) - 1; + let v = Value::from_u64(base_types[base_type.0], v)?; + eval.resume_with_memory(v)? + } + EvaluationResult::RequiresRegister { + register, + base_type, + } => { + let v = Value::from_u64( + base_types[base_type.0], + u64::from(register.0) << 4, + )?; + eval.resume_with_register(v)? + } + EvaluationResult::RequiresBaseType(offset) => { + eval.resume_with_base_type(base_types[offset.0])? + } + EvaluationResult::RequiresRelocatedAddress(address) => { + eval.resume_with_relocated_address(address)? + } + _ => panic!("Unexpected result {:?}", result), + } + } + Ok(result) + }, + ); + } + } +} diff --git a/vendor/gimli/src/read/pubnames.rs b/vendor/gimli/src/read/pubnames.rs new file mode 100644 index 0000000..e8b7e55 --- /dev/null +++ b/vendor/gimli/src/read/pubnames.rs @@ -0,0 +1,141 @@ +use crate::common::{DebugInfoOffset, SectionId}; +use crate::endianity::Endianity; +use crate::read::lookup::{DebugLookup, LookupEntryIter, PubStuffEntry, PubStuffParser}; +use crate::read::{EndianSlice, Reader, Result, Section, UnitOffset}; + +/// A single parsed pubname. +#[derive(Debug, Clone)] +pub struct PubNamesEntry<R: Reader> { + unit_header_offset: DebugInfoOffset<R::Offset>, + die_offset: UnitOffset<R::Offset>, + name: R, +} + +impl<R: Reader> PubNamesEntry<R> { + /// Returns the name this entry refers to. + pub fn name(&self) -> &R { + &self.name + } + + /// Returns the offset into the .debug_info section for the header of the compilation unit + /// which contains this name. + pub fn unit_header_offset(&self) -> DebugInfoOffset<R::Offset> { + self.unit_header_offset + } + + /// Returns the offset into the compilation unit for the debugging information entry which + /// has this name. + pub fn die_offset(&self) -> UnitOffset<R::Offset> { + self.die_offset + } +} + +impl<R: Reader> PubStuffEntry<R> for PubNamesEntry<R> { + fn new( + die_offset: UnitOffset<R::Offset>, + name: R, + unit_header_offset: DebugInfoOffset<R::Offset>, + ) -> Self { + PubNamesEntry { + unit_header_offset, + die_offset, + name, + } + } +} + +/// The `DebugPubNames` struct represents the DWARF public names information +/// found in the `.debug_pubnames` section. +#[derive(Debug, Clone)] +pub struct DebugPubNames<R: Reader>(DebugLookup<R, PubStuffParser<R, PubNamesEntry<R>>>); + +impl<'input, Endian> DebugPubNames<EndianSlice<'input, Endian>> +where + Endian: Endianity, +{ + /// Construct a new `DebugPubNames` instance from the data in the `.debug_pubnames` + /// section. + /// + /// It is the caller's responsibility to read the `.debug_pubnames` section and + /// present it as a `&[u8]` slice. That means using some ELF loader on + /// Linux, a Mach-O loader on macOS, etc. + /// + /// ``` + /// use gimli::{DebugPubNames, LittleEndian}; + /// + /// # let buf = []; + /// # let read_debug_pubnames_section_somehow = || &buf; + /// let debug_pubnames = + /// DebugPubNames::new(read_debug_pubnames_section_somehow(), LittleEndian); + /// ``` + pub fn new(debug_pubnames_section: &'input [u8], endian: Endian) -> Self { + Self::from(EndianSlice::new(debug_pubnames_section, endian)) + } +} + +impl<R: Reader> DebugPubNames<R> { + /// Iterate the pubnames in the `.debug_pubnames` section. + /// + /// ``` + /// use gimli::{DebugPubNames, EndianSlice, LittleEndian}; + /// + /// # let buf = []; + /// # let read_debug_pubnames_section_somehow = || &buf; + /// let debug_pubnames = + /// DebugPubNames::new(read_debug_pubnames_section_somehow(), LittleEndian); + /// + /// let mut iter = debug_pubnames.items(); + /// while let Some(pubname) = iter.next().unwrap() { + /// println!("pubname {} found!", pubname.name().to_string_lossy()); + /// } + /// ``` + pub fn items(&self) -> PubNamesEntryIter<R> { + PubNamesEntryIter(self.0.items()) + } +} + +impl<R: Reader> Section<R> for DebugPubNames<R> { + fn id() -> SectionId { + SectionId::DebugPubNames + } + + fn reader(&self) -> &R { + self.0.reader() + } +} + +impl<R: Reader> From<R> for DebugPubNames<R> { + fn from(debug_pubnames_section: R) -> Self { + DebugPubNames(DebugLookup::from(debug_pubnames_section)) + } +} + +/// An iterator over the pubnames from a `.debug_pubnames` section. +/// +/// Can be [used with +/// `FallibleIterator`](./index.html#using-with-fallibleiterator). +#[derive(Debug, Clone)] +pub struct PubNamesEntryIter<R: Reader>(LookupEntryIter<R, PubStuffParser<R, PubNamesEntry<R>>>); + +impl<R: Reader> PubNamesEntryIter<R> { + /// Advance the iterator and return the next pubname. + /// + /// Returns the newly parsed pubname as `Ok(Some(pubname))`. Returns + /// `Ok(None)` when iteration is complete and all pubnames have already been + /// parsed and yielded. If an error occurs while parsing the next pubname, + /// then this error is returned as `Err(e)`, and all subsequent calls return + /// `Ok(None)`. + pub fn next(&mut self) -> Result<Option<PubNamesEntry<R>>> { + self.0.next() + } +} + +#[cfg(feature = "fallible-iterator")] +impl<R: Reader> fallible_iterator::FallibleIterator for PubNamesEntryIter<R> { + type Item = PubNamesEntry<R>; + type Error = crate::read::Error; + + fn next(&mut self) -> ::core::result::Result<Option<Self::Item>, Self::Error> { + self.0.next() + } +} diff --git a/vendor/gimli/src/read/pubtypes.rs b/vendor/gimli/src/read/pubtypes.rs new file mode 100644 index 0000000..6723b42 --- /dev/null +++ b/vendor/gimli/src/read/pubtypes.rs @@ -0,0 +1,141 @@ +use crate::common::{DebugInfoOffset, SectionId}; +use crate::endianity::Endianity; +use crate::read::lookup::{DebugLookup, LookupEntryIter, PubStuffEntry, PubStuffParser}; +use crate::read::{EndianSlice, Reader, Result, Section, UnitOffset}; + +/// A single parsed pubtype. +#[derive(Debug, Clone)] +pub struct PubTypesEntry<R: Reader> { + unit_header_offset: DebugInfoOffset<R::Offset>, + die_offset: UnitOffset<R::Offset>, + name: R, +} + +impl<R: Reader> PubTypesEntry<R> { + /// Returns the name of the type this entry refers to. + pub fn name(&self) -> &R { + &self.name + } + + /// Returns the offset into the .debug_info section for the header of the compilation unit + /// which contains the type with this name. + pub fn unit_header_offset(&self) -> DebugInfoOffset<R::Offset> { + self.unit_header_offset + } + + /// Returns the offset into the compilation unit for the debugging information entry which + /// the type with this name. + pub fn die_offset(&self) -> UnitOffset<R::Offset> { + self.die_offset + } +} + +impl<R: Reader> PubStuffEntry<R> for PubTypesEntry<R> { + fn new( + die_offset: UnitOffset<R::Offset>, + name: R, + unit_header_offset: DebugInfoOffset<R::Offset>, + ) -> Self { + PubTypesEntry { + unit_header_offset, + die_offset, + name, + } + } +} + +/// The `DebugPubTypes` struct represents the DWARF public types information +/// found in the `.debug_info` section. +#[derive(Debug, Clone)] +pub struct DebugPubTypes<R: Reader>(DebugLookup<R, PubStuffParser<R, PubTypesEntry<R>>>); + +impl<'input, Endian> DebugPubTypes<EndianSlice<'input, Endian>> +where + Endian: Endianity, +{ + /// Construct a new `DebugPubTypes` instance from the data in the `.debug_pubtypes` + /// section. + /// + /// It is the caller's responsibility to read the `.debug_pubtypes` section and + /// present it as a `&[u8]` slice. That means using some ELF loader on + /// Linux, a Mach-O loader on macOS, etc. + /// + /// ``` + /// use gimli::{DebugPubTypes, LittleEndian}; + /// + /// # let buf = []; + /// # let read_debug_pubtypes_somehow = || &buf; + /// let debug_pubtypes = + /// DebugPubTypes::new(read_debug_pubtypes_somehow(), LittleEndian); + /// ``` + pub fn new(debug_pubtypes_section: &'input [u8], endian: Endian) -> Self { + Self::from(EndianSlice::new(debug_pubtypes_section, endian)) + } +} + +impl<R: Reader> DebugPubTypes<R> { + /// Iterate the pubtypes in the `.debug_pubtypes` section. + /// + /// ``` + /// use gimli::{DebugPubTypes, EndianSlice, LittleEndian}; + /// + /// # let buf = []; + /// # let read_debug_pubtypes_section_somehow = || &buf; + /// let debug_pubtypes = + /// DebugPubTypes::new(read_debug_pubtypes_section_somehow(), LittleEndian); + /// + /// let mut iter = debug_pubtypes.items(); + /// while let Some(pubtype) = iter.next().unwrap() { + /// println!("pubtype {} found!", pubtype.name().to_string_lossy()); + /// } + /// ``` + pub fn items(&self) -> PubTypesEntryIter<R> { + PubTypesEntryIter(self.0.items()) + } +} + +impl<R: Reader> Section<R> for DebugPubTypes<R> { + fn id() -> SectionId { + SectionId::DebugPubTypes + } + + fn reader(&self) -> &R { + self.0.reader() + } +} + +impl<R: Reader> From<R> for DebugPubTypes<R> { + fn from(debug_pubtypes_section: R) -> Self { + DebugPubTypes(DebugLookup::from(debug_pubtypes_section)) + } +} + +/// An iterator over the pubtypes from a `.debug_pubtypes` section. +/// +/// Can be [used with +/// `FallibleIterator`](./index.html#using-with-fallibleiterator). +#[derive(Debug, Clone)] +pub struct PubTypesEntryIter<R: Reader>(LookupEntryIter<R, PubStuffParser<R, PubTypesEntry<R>>>); + +impl<R: Reader> PubTypesEntryIter<R> { + /// Advance the iterator and return the next pubtype. + /// + /// Returns the newly parsed pubtype as `Ok(Some(pubtype))`. Returns + /// `Ok(None)` when iteration is complete and all pubtypes have already been + /// parsed and yielded. If an error occurs while parsing the next pubtype, + /// then this error is returned as `Err(e)`, and all subsequent calls return + /// `Ok(None)`. + pub fn next(&mut self) -> Result<Option<PubTypesEntry<R>>> { + self.0.next() + } +} + +#[cfg(feature = "fallible-iterator")] +impl<R: Reader> fallible_iterator::FallibleIterator for PubTypesEntryIter<R> { + type Item = PubTypesEntry<R>; + type Error = crate::read::Error; + + fn next(&mut self) -> ::core::result::Result<Option<Self::Item>, Self::Error> { + self.0.next() + } +} diff --git a/vendor/gimli/src/read/reader.rs b/vendor/gimli/src/read/reader.rs new file mode 100644 index 0000000..e7dd477 --- /dev/null +++ b/vendor/gimli/src/read/reader.rs @@ -0,0 +1,502 @@ +#[cfg(feature = "read")] +use alloc::borrow::Cow; +use core::convert::TryInto; +use core::fmt::Debug; +use core::hash::Hash; +use core::ops::{Add, AddAssign, Sub}; + +use crate::common::Format; +use crate::endianity::Endianity; +use crate::leb128; +use crate::read::{Error, Result}; + +/// An identifier for an offset within a section reader. +/// +/// This is used for error reporting. The meaning of this value is specific to +/// each reader implementation. The values should be chosen to be unique amongst +/// all readers. If values are not unique then errors may point to the wrong reader. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct ReaderOffsetId(pub u64); + +/// A trait for offsets with a DWARF section. +/// +/// This allows consumers to choose a size that is appropriate for their address space. +pub trait ReaderOffset: + Debug + Copy + Eq + Ord + Hash + Add<Output = Self> + AddAssign + Sub<Output = Self> +{ + /// Convert a u8 to an offset. + fn from_u8(offset: u8) -> Self; + + /// Convert a u16 to an offset. + fn from_u16(offset: u16) -> Self; + + /// Convert an i16 to an offset. + fn from_i16(offset: i16) -> Self; + + /// Convert a u32 to an offset. + fn from_u32(offset: u32) -> Self; + + /// Convert a u64 to an offset. + /// + /// Returns `Error::UnsupportedOffset` if the value is too large. + fn from_u64(offset: u64) -> Result<Self>; + + /// Convert an offset to a u64. + fn into_u64(self) -> u64; + + /// Wrapping (modular) addition. Computes `self + other`. + fn wrapping_add(self, other: Self) -> Self; + + /// Checked subtraction. Computes `self - other`. + fn checked_sub(self, other: Self) -> Option<Self>; +} + +impl ReaderOffset for u64 { + #[inline] + fn from_u8(offset: u8) -> Self { + u64::from(offset) + } + + #[inline] + fn from_u16(offset: u16) -> Self { + u64::from(offset) + } + + #[inline] + fn from_i16(offset: i16) -> Self { + offset as u64 + } + + #[inline] + fn from_u32(offset: u32) -> Self { + u64::from(offset) + } + + #[inline] + fn from_u64(offset: u64) -> Result<Self> { + Ok(offset) + } + + #[inline] + fn into_u64(self) -> u64 { + self + } + + #[inline] + fn wrapping_add(self, other: Self) -> Self { + self.wrapping_add(other) + } + + #[inline] + fn checked_sub(self, other: Self) -> Option<Self> { + self.checked_sub(other) + } +} + +impl ReaderOffset for u32 { + #[inline] + fn from_u8(offset: u8) -> Self { + u32::from(offset) + } + + #[inline] + fn from_u16(offset: u16) -> Self { + u32::from(offset) + } + + #[inline] + fn from_i16(offset: i16) -> Self { + offset as u32 + } + + #[inline] + fn from_u32(offset: u32) -> Self { + offset + } + + #[inline] + fn from_u64(offset64: u64) -> Result<Self> { + let offset = offset64 as u32; + if u64::from(offset) == offset64 { + Ok(offset) + } else { + Err(Error::UnsupportedOffset) + } + } + + #[inline] + fn into_u64(self) -> u64 { + u64::from(self) + } + + #[inline] + fn wrapping_add(self, other: Self) -> Self { + self.wrapping_add(other) + } + + #[inline] + fn checked_sub(self, other: Self) -> Option<Self> { + self.checked_sub(other) + } +} + +impl ReaderOffset for usize { + #[inline] + fn from_u8(offset: u8) -> Self { + offset as usize + } + + #[inline] + fn from_u16(offset: u16) -> Self { + offset as usize + } + + #[inline] + fn from_i16(offset: i16) -> Self { + offset as usize + } + + #[inline] + fn from_u32(offset: u32) -> Self { + offset as usize + } + + #[inline] + fn from_u64(offset64: u64) -> Result<Self> { + let offset = offset64 as usize; + if offset as u64 == offset64 { + Ok(offset) + } else { + Err(Error::UnsupportedOffset) + } + } + + #[inline] + fn into_u64(self) -> u64 { + self as u64 + } + + #[inline] + fn wrapping_add(self, other: Self) -> Self { + self.wrapping_add(other) + } + + #[inline] + fn checked_sub(self, other: Self) -> Option<Self> { + self.checked_sub(other) + } +} + +#[cfg(not(feature = "read"))] +pub(crate) mod seal_if_no_alloc { + #[derive(Debug)] + pub struct Sealed; +} + +/// A trait for reading the data from a DWARF section. +/// +/// All read operations advance the section offset of the reader +/// unless specified otherwise. +/// +/// ## Choosing a `Reader` Implementation +/// +/// `gimli` comes with a few different `Reader` implementations and lets you +/// choose the one that is right for your use case. A `Reader` is essentially a +/// view into the raw bytes that make up some DWARF, but this view might borrow +/// the underlying data or use reference counting ownership, and it might be +/// thread safe or not. +/// +/// | Implementation | Ownership | Thread Safe | Notes | +/// |:------------------|:------------------|:------------|:------| +/// | [`EndianSlice`](./struct.EndianSlice.html) | Borrowed | Yes | Fastest, but requires that all of your code work with borrows. | +/// | [`EndianRcSlice`](./struct.EndianRcSlice.html) | Reference counted | No | Shared ownership via reference counting, which alleviates the borrow restrictions of `EndianSlice` but imposes reference counting increments and decrements. Cannot be sent across threads, because the reference count is not atomic. | +/// | [`EndianArcSlice`](./struct.EndianArcSlice.html) | Reference counted | Yes | The same as `EndianRcSlice`, but uses atomic reference counting, and therefore reference counting operations are slower but `EndianArcSlice`s may be sent across threads. | +/// | [`EndianReader<T>`](./struct.EndianReader.html) | Same as `T` | Same as `T` | Escape hatch for easily defining your own type of `Reader`. | +pub trait Reader: Debug + Clone { + /// The endianity of bytes that are read. + type Endian: Endianity; + + /// The type used for offsets and lengths. + type Offset: ReaderOffset; + + /// Return the endianity of bytes that are read. + fn endian(&self) -> Self::Endian; + + /// Return the number of bytes remaining. + fn len(&self) -> Self::Offset; + + /// Set the number of bytes remaining to zero. + fn empty(&mut self); + + /// Set the number of bytes remaining to the specified length. + fn truncate(&mut self, len: Self::Offset) -> Result<()>; + + /// Return the offset of this reader's data relative to the start of + /// the given base reader's data. + /// + /// May panic if this reader's data is not contained within the given + /// base reader's data. + fn offset_from(&self, base: &Self) -> Self::Offset; + + /// Return an identifier for the current reader offset. + fn offset_id(&self) -> ReaderOffsetId; + + /// Return the offset corresponding to the given `id` if + /// it is associated with this reader. + fn lookup_offset_id(&self, id: ReaderOffsetId) -> Option<Self::Offset>; + + /// Find the index of the first occurrence of the given byte. + /// The offset of the reader is not changed. + fn find(&self, byte: u8) -> Result<Self::Offset>; + + /// Discard the specified number of bytes. + fn skip(&mut self, len: Self::Offset) -> Result<()>; + + /// Split a reader in two. + /// + /// A new reader is returned that can be used to read the next + /// `len` bytes, and `self` is advanced so that it reads the remainder. + fn split(&mut self, len: Self::Offset) -> Result<Self>; + + /// This trait cannot be implemented if "read" feature is not enabled. + /// + /// `Reader` trait has a few methods that depend on `alloc` crate. + /// Disallowing `Reader` trait implementation prevents a crate that only depends on + /// "read-core" from being broken if another crate depending on `gimli` enables + /// "read" feature. + #[cfg(not(feature = "read"))] + fn cannot_implement() -> seal_if_no_alloc::Sealed; + + /// Return all remaining data as a clone-on-write slice. + /// + /// The slice will be borrowed where possible, but some readers may + /// always return an owned vector. + /// + /// Does not advance the reader. + #[cfg(feature = "read")] + fn to_slice(&self) -> Result<Cow<[u8]>>; + + /// Convert all remaining data to a clone-on-write string. + /// + /// The string will be borrowed where possible, but some readers may + /// always return an owned string. + /// + /// Does not advance the reader. + /// + /// Returns an error if the data contains invalid characters. + #[cfg(feature = "read")] + fn to_string(&self) -> Result<Cow<str>>; + + /// Convert all remaining data to a clone-on-write string, including invalid characters. + /// + /// The string will be borrowed where possible, but some readers may + /// always return an owned string. + /// + /// Does not advance the reader. + #[cfg(feature = "read")] + fn to_string_lossy(&self) -> Result<Cow<str>>; + + /// Read exactly `buf.len()` bytes into `buf`. + fn read_slice(&mut self, buf: &mut [u8]) -> Result<()>; + + /// Read a u8 array. + #[inline] + fn read_u8_array<A>(&mut self) -> Result<A> + where + A: Sized + Default + AsMut<[u8]>, + { + let mut val = Default::default(); + self.read_slice(<A as AsMut<[u8]>>::as_mut(&mut val))?; + Ok(val) + } + + /// Return true if the number of bytes remaining is zero. + #[inline] + fn is_empty(&self) -> bool { + self.len() == Self::Offset::from_u8(0) + } + + /// Read a u8. + #[inline] + fn read_u8(&mut self) -> Result<u8> { + let a: [u8; 1] = self.read_u8_array()?; + Ok(a[0]) + } + + /// Read an i8. + #[inline] + fn read_i8(&mut self) -> Result<i8> { + let a: [u8; 1] = self.read_u8_array()?; + Ok(a[0] as i8) + } + + /// Read a u16. + #[inline] + fn read_u16(&mut self) -> Result<u16> { + let a: [u8; 2] = self.read_u8_array()?; + Ok(self.endian().read_u16(&a)) + } + + /// Read an i16. + #[inline] + fn read_i16(&mut self) -> Result<i16> { + let a: [u8; 2] = self.read_u8_array()?; + Ok(self.endian().read_i16(&a)) + } + + /// Read a u32. + #[inline] + fn read_u32(&mut self) -> Result<u32> { + let a: [u8; 4] = self.read_u8_array()?; + Ok(self.endian().read_u32(&a)) + } + + /// Read an i32. + #[inline] + fn read_i32(&mut self) -> Result<i32> { + let a: [u8; 4] = self.read_u8_array()?; + Ok(self.endian().read_i32(&a)) + } + + /// Read a u64. + #[inline] + fn read_u64(&mut self) -> Result<u64> { + let a: [u8; 8] = self.read_u8_array()?; + Ok(self.endian().read_u64(&a)) + } + + /// Read an i64. + #[inline] + fn read_i64(&mut self) -> Result<i64> { + let a: [u8; 8] = self.read_u8_array()?; + Ok(self.endian().read_i64(&a)) + } + + /// Read a f32. + #[inline] + fn read_f32(&mut self) -> Result<f32> { + let a: [u8; 4] = self.read_u8_array()?; + Ok(self.endian().read_f32(&a)) + } + + /// Read a f64. + #[inline] + fn read_f64(&mut self) -> Result<f64> { + let a: [u8; 8] = self.read_u8_array()?; + Ok(self.endian().read_f64(&a)) + } + + /// Read an unsigned n-bytes integer u64. + /// + /// # Panics + /// + /// Panics when nbytes < 1 or nbytes > 8 + #[inline] + fn read_uint(&mut self, n: usize) -> Result<u64> { + let mut buf = [0; 8]; + self.read_slice(&mut buf[..n])?; + Ok(self.endian().read_uint(&buf[..n])) + } + + /// Read a null-terminated slice, and return it (excluding the null). + fn read_null_terminated_slice(&mut self) -> Result<Self> { + let idx = self.find(0)?; + let val = self.split(idx)?; + self.skip(Self::Offset::from_u8(1))?; + Ok(val) + } + + /// Skip a LEB128 encoded integer. + fn skip_leb128(&mut self) -> Result<()> { + leb128::read::skip(self) + } + + /// Read an unsigned LEB128 encoded integer. + fn read_uleb128(&mut self) -> Result<u64> { + leb128::read::unsigned(self) + } + + /// Read an unsigned LEB128 encoded u32. + fn read_uleb128_u32(&mut self) -> Result<u32> { + leb128::read::unsigned(self)? + .try_into() + .map_err(|_| Error::BadUnsignedLeb128) + } + + /// Read an unsigned LEB128 encoded u16. + fn read_uleb128_u16(&mut self) -> Result<u16> { + leb128::read::u16(self) + } + + /// Read a signed LEB128 encoded integer. + fn read_sleb128(&mut self) -> Result<i64> { + leb128::read::signed(self) + } + + /// Read an initial length field. + /// + /// This field is encoded as either a 32-bit length or + /// a 64-bit length, and the returned `Format` indicates which. + fn read_initial_length(&mut self) -> Result<(Self::Offset, Format)> { + const MAX_DWARF_32_UNIT_LENGTH: u32 = 0xffff_fff0; + const DWARF_64_INITIAL_UNIT_LENGTH: u32 = 0xffff_ffff; + + let val = self.read_u32()?; + if val < MAX_DWARF_32_UNIT_LENGTH { + Ok((Self::Offset::from_u32(val), Format::Dwarf32)) + } else if val == DWARF_64_INITIAL_UNIT_LENGTH { + let val = self.read_u64().and_then(Self::Offset::from_u64)?; + Ok((val, Format::Dwarf64)) + } else { + Err(Error::UnknownReservedLength) + } + } + + /// Read an address-sized integer, and return it as a `u64`. + fn read_address(&mut self, address_size: u8) -> Result<u64> { + match address_size { + 1 => self.read_u8().map(u64::from), + 2 => self.read_u16().map(u64::from), + 4 => self.read_u32().map(u64::from), + 8 => self.read_u64(), + otherwise => Err(Error::UnsupportedAddressSize(otherwise)), + } + } + + /// Parse a word-sized integer according to the DWARF format. + /// + /// These are always used to encode section offsets or lengths, + /// and so have a type of `Self::Offset`. + fn read_word(&mut self, format: Format) -> Result<Self::Offset> { + match format { + Format::Dwarf32 => self.read_u32().map(Self::Offset::from_u32), + Format::Dwarf64 => self.read_u64().and_then(Self::Offset::from_u64), + } + } + + /// Parse a word-sized section length according to the DWARF format. + #[inline] + fn read_length(&mut self, format: Format) -> Result<Self::Offset> { + self.read_word(format) + } + + /// Parse a word-sized section offset according to the DWARF format. + #[inline] + fn read_offset(&mut self, format: Format) -> Result<Self::Offset> { + self.read_word(format) + } + + /// Parse a section offset of the given size. + /// + /// This is used for `DW_FORM_ref_addr` values in DWARF version 2. + fn read_sized_offset(&mut self, size: u8) -> Result<Self::Offset> { + match size { + 1 => self.read_u8().map(u64::from), + 2 => self.read_u16().map(u64::from), + 4 => self.read_u32().map(u64::from), + 8 => self.read_u64(), + otherwise => Err(Error::UnsupportedOffsetSize(otherwise)), + } + .and_then(Self::Offset::from_u64) + } +} diff --git a/vendor/gimli/src/read/rnglists.rs b/vendor/gimli/src/read/rnglists.rs new file mode 100644 index 0000000..12e3e04 --- /dev/null +++ b/vendor/gimli/src/read/rnglists.rs @@ -0,0 +1,1458 @@ +use crate::common::{ + DebugAddrBase, DebugAddrIndex, DebugRngListsBase, DebugRngListsIndex, DwarfFileType, Encoding, + RangeListsOffset, SectionId, +}; +use crate::constants; +use crate::endianity::Endianity; +use crate::read::{ + lists::ListsHeader, DebugAddr, EndianSlice, Error, Reader, ReaderOffset, ReaderOffsetId, + Result, Section, +}; + +/// The raw contents of the `.debug_ranges` section. +#[derive(Debug, Default, Clone, Copy)] +pub struct DebugRanges<R> { + pub(crate) section: R, +} + +impl<'input, Endian> DebugRanges<EndianSlice<'input, Endian>> +where + Endian: Endianity, +{ + /// Construct a new `DebugRanges` instance from the data in the `.debug_ranges` + /// section. + /// + /// It is the caller's responsibility to read the `.debug_ranges` section and + /// present it as a `&[u8]` slice. That means using some ELF loader on + /// Linux, a Mach-O loader on macOS, etc. + /// + /// ``` + /// use gimli::{DebugRanges, LittleEndian}; + /// + /// # let buf = [0x00, 0x01, 0x02, 0x03]; + /// # let read_debug_ranges_section_somehow = || &buf; + /// let debug_ranges = DebugRanges::new(read_debug_ranges_section_somehow(), LittleEndian); + /// ``` + pub fn new(section: &'input [u8], endian: Endian) -> Self { + Self::from(EndianSlice::new(section, endian)) + } +} + +impl<R> Section<R> for DebugRanges<R> { + fn id() -> SectionId { + SectionId::DebugRanges + } + + fn reader(&self) -> &R { + &self.section + } +} + +impl<R> From<R> for DebugRanges<R> { + fn from(section: R) -> Self { + DebugRanges { section } + } +} + +/// The `DebugRngLists` struct represents the contents of the +/// `.debug_rnglists` section. +#[derive(Debug, Default, Clone, Copy)] +pub struct DebugRngLists<R> { + section: R, +} + +impl<'input, Endian> DebugRngLists<EndianSlice<'input, Endian>> +where + Endian: Endianity, +{ + /// Construct a new `DebugRngLists` instance from the data in the + /// `.debug_rnglists` section. + /// + /// It is the caller's responsibility to read the `.debug_rnglists` + /// section and present it as a `&[u8]` slice. That means using some ELF + /// loader on Linux, a Mach-O loader on macOS, etc. + /// + /// ``` + /// use gimli::{DebugRngLists, LittleEndian}; + /// + /// # let buf = [0x00, 0x01, 0x02, 0x03]; + /// # let read_debug_rnglists_section_somehow = || &buf; + /// let debug_rnglists = + /// DebugRngLists::new(read_debug_rnglists_section_somehow(), LittleEndian); + /// ``` + pub fn new(section: &'input [u8], endian: Endian) -> Self { + Self::from(EndianSlice::new(section, endian)) + } +} + +impl<R> Section<R> for DebugRngLists<R> { + fn id() -> SectionId { + SectionId::DebugRngLists + } + + fn reader(&self) -> &R { + &self.section + } +} + +impl<R> From<R> for DebugRngLists<R> { + fn from(section: R) -> Self { + DebugRngLists { section } + } +} + +#[allow(unused)] +pub(crate) type RngListsHeader = ListsHeader; + +impl<Offset> DebugRngListsBase<Offset> +where + Offset: ReaderOffset, +{ + /// Returns a `DebugRngListsBase` with the default value of DW_AT_rnglists_base + /// for the given `Encoding` and `DwarfFileType`. + pub fn default_for_encoding_and_file( + encoding: Encoding, + file_type: DwarfFileType, + ) -> DebugRngListsBase<Offset> { + if encoding.version >= 5 && file_type == DwarfFileType::Dwo { + // In .dwo files, the compiler omits the DW_AT_rnglists_base attribute (because there is + // only a single unit in the file) but we must skip past the header, which the attribute + // would normally do for us. + DebugRngListsBase(Offset::from_u8(RngListsHeader::size_for_encoding(encoding))) + } else { + DebugRngListsBase(Offset::from_u8(0)) + } + } +} + +/// The DWARF data found in `.debug_ranges` and `.debug_rnglists` sections. +#[derive(Debug, Default, Clone, Copy)] +pub struct RangeLists<R> { + debug_ranges: DebugRanges<R>, + debug_rnglists: DebugRngLists<R>, +} + +impl<R> RangeLists<R> { + /// Construct a new `RangeLists` instance from the data in the `.debug_ranges` and + /// `.debug_rnglists` sections. + pub fn new(debug_ranges: DebugRanges<R>, debug_rnglists: DebugRngLists<R>) -> RangeLists<R> { + RangeLists { + debug_ranges, + debug_rnglists, + } + } + + /// Return the `.debug_ranges` section. + pub fn debug_ranges(&self) -> &DebugRanges<R> { + &self.debug_ranges + } + + /// Replace the `.debug_ranges` section. + /// + /// This is useful for `.dwo` files when using the GNU split-dwarf extension to DWARF 4. + pub fn set_debug_ranges(&mut self, debug_ranges: DebugRanges<R>) { + self.debug_ranges = debug_ranges; + } + + /// Return the `.debug_rnglists` section. + pub fn debug_rnglists(&self) -> &DebugRngLists<R> { + &self.debug_rnglists + } +} + +impl<T> RangeLists<T> { + /// Create a `RangeLists` that references the data in `self`. + /// + /// This is useful when `R` implements `Reader` but `T` does not. + /// + /// ## Example Usage + /// + /// ```rust,no_run + /// # let load_section = || unimplemented!(); + /// // Read the DWARF section into a `Vec` with whatever object loader you're using. + /// let owned_section: gimli::RangeLists<Vec<u8>> = load_section(); + /// // Create a reference to the DWARF section. + /// let section = owned_section.borrow(|section| { + /// gimli::EndianSlice::new(§ion, gimli::LittleEndian) + /// }); + /// ``` + pub fn borrow<'a, F, R>(&'a self, mut borrow: F) -> RangeLists<R> + where + F: FnMut(&'a T) -> R, + { + RangeLists { + debug_ranges: borrow(&self.debug_ranges.section).into(), + debug_rnglists: borrow(&self.debug_rnglists.section).into(), + } + } +} + +impl<R: Reader> RangeLists<R> { + /// Iterate over the `Range` list entries starting at the given offset. + /// + /// The `unit_version` and `address_size` must match the compilation unit that the + /// offset was contained in. + /// + /// The `base_address` should be obtained from the `DW_AT_low_pc` attribute in the + /// `DW_TAG_compile_unit` entry for the compilation unit that contains this range list. + /// + /// Can be [used with + /// `FallibleIterator`](./index.html#using-with-fallibleiterator). + pub fn ranges( + &self, + offset: RangeListsOffset<R::Offset>, + unit_encoding: Encoding, + base_address: u64, + debug_addr: &DebugAddr<R>, + debug_addr_base: DebugAddrBase<R::Offset>, + ) -> Result<RngListIter<R>> { + Ok(RngListIter::new( + self.raw_ranges(offset, unit_encoding)?, + base_address, + debug_addr.clone(), + debug_addr_base, + )) + } + + /// Iterate over the `RawRngListEntry`ies starting at the given offset. + /// + /// The `unit_encoding` must match the compilation unit that the + /// offset was contained in. + /// + /// This iterator does not perform any processing of the range entries, + /// such as handling base addresses. + /// + /// Can be [used with + /// `FallibleIterator`](./index.html#using-with-fallibleiterator). + pub fn raw_ranges( + &self, + offset: RangeListsOffset<R::Offset>, + unit_encoding: Encoding, + ) -> Result<RawRngListIter<R>> { + let (mut input, format) = if unit_encoding.version <= 4 { + (self.debug_ranges.section.clone(), RangeListsFormat::Bare) + } else { + (self.debug_rnglists.section.clone(), RangeListsFormat::Rle) + }; + input.skip(offset.0)?; + Ok(RawRngListIter::new(input, unit_encoding, format)) + } + + /// Returns the `.debug_rnglists` offset at the given `base` and `index`. + /// + /// The `base` must be the `DW_AT_rnglists_base` value from the compilation unit DIE. + /// This is an offset that points to the first entry following the header. + /// + /// The `index` is the value of a `DW_FORM_rnglistx` attribute. + /// + /// The `unit_encoding` must match the compilation unit that the + /// index was contained in. + pub fn get_offset( + &self, + unit_encoding: Encoding, + base: DebugRngListsBase<R::Offset>, + index: DebugRngListsIndex<R::Offset>, + ) -> Result<RangeListsOffset<R::Offset>> { + let format = unit_encoding.format; + let input = &mut self.debug_rnglists.section.clone(); + input.skip(base.0)?; + input.skip(R::Offset::from_u64( + index.0.into_u64() * u64::from(format.word_size()), + )?)?; + input + .read_offset(format) + .map(|x| RangeListsOffset(base.0 + x)) + } + + /// Call `Reader::lookup_offset_id` for each section, and return the first match. + pub fn lookup_offset_id(&self, id: ReaderOffsetId) -> Option<(SectionId, R::Offset)> { + self.debug_ranges + .lookup_offset_id(id) + .or_else(|| self.debug_rnglists.lookup_offset_id(id)) + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum RangeListsFormat { + /// The bare range list format used before DWARF 5. + Bare, + /// The DW_RLE encoded range list format used in DWARF 5. + Rle, +} + +/// A raw iterator over an address range list. +/// +/// This iterator does not perform any processing of the range entries, +/// such as handling base addresses. +#[derive(Debug)] +pub struct RawRngListIter<R: Reader> { + input: R, + encoding: Encoding, + format: RangeListsFormat, +} + +/// A raw entry in .debug_rnglists +#[derive(Clone, Debug)] +pub enum RawRngListEntry<T> { + /// A range from DWARF version <= 4. + AddressOrOffsetPair { + /// Start of range. May be an address or an offset. + begin: u64, + /// End of range. May be an address or an offset. + end: u64, + }, + /// DW_RLE_base_address + BaseAddress { + /// base address + addr: u64, + }, + /// DW_RLE_base_addressx + BaseAddressx { + /// base address + addr: DebugAddrIndex<T>, + }, + /// DW_RLE_startx_endx + StartxEndx { + /// start of range + begin: DebugAddrIndex<T>, + /// end of range + end: DebugAddrIndex<T>, + }, + /// DW_RLE_startx_length + StartxLength { + /// start of range + begin: DebugAddrIndex<T>, + /// length of range + length: u64, + }, + /// DW_RLE_offset_pair + OffsetPair { + /// start of range + begin: u64, + /// end of range + end: u64, + }, + /// DW_RLE_start_end + StartEnd { + /// start of range + begin: u64, + /// end of range + end: u64, + }, + /// DW_RLE_start_length + StartLength { + /// start of range + begin: u64, + /// length of range + length: u64, + }, +} + +impl<T: ReaderOffset> RawRngListEntry<T> { + /// Parse a range entry from `.debug_rnglists` + fn parse<R: Reader<Offset = T>>( + input: &mut R, + encoding: Encoding, + format: RangeListsFormat, + ) -> Result<Option<Self>> { + Ok(match format { + RangeListsFormat::Bare => { + let range = RawRange::parse(input, encoding.address_size)?; + if range.is_end() { + None + } else if range.is_base_address(encoding.address_size) { + Some(RawRngListEntry::BaseAddress { addr: range.end }) + } else { + Some(RawRngListEntry::AddressOrOffsetPair { + begin: range.begin, + end: range.end, + }) + } + } + RangeListsFormat::Rle => match constants::DwRle(input.read_u8()?) { + constants::DW_RLE_end_of_list => None, + constants::DW_RLE_base_addressx => Some(RawRngListEntry::BaseAddressx { + addr: DebugAddrIndex(input.read_uleb128().and_then(R::Offset::from_u64)?), + }), + constants::DW_RLE_startx_endx => Some(RawRngListEntry::StartxEndx { + begin: DebugAddrIndex(input.read_uleb128().and_then(R::Offset::from_u64)?), + end: DebugAddrIndex(input.read_uleb128().and_then(R::Offset::from_u64)?), + }), + constants::DW_RLE_startx_length => Some(RawRngListEntry::StartxLength { + begin: DebugAddrIndex(input.read_uleb128().and_then(R::Offset::from_u64)?), + length: input.read_uleb128()?, + }), + constants::DW_RLE_offset_pair => Some(RawRngListEntry::OffsetPair { + begin: input.read_uleb128()?, + end: input.read_uleb128()?, + }), + constants::DW_RLE_base_address => Some(RawRngListEntry::BaseAddress { + addr: input.read_address(encoding.address_size)?, + }), + constants::DW_RLE_start_end => Some(RawRngListEntry::StartEnd { + begin: input.read_address(encoding.address_size)?, + end: input.read_address(encoding.address_size)?, + }), + constants::DW_RLE_start_length => Some(RawRngListEntry::StartLength { + begin: input.read_address(encoding.address_size)?, + length: input.read_uleb128()?, + }), + _ => { + return Err(Error::InvalidAddressRange); + } + }, + }) + } +} + +impl<R: Reader> RawRngListIter<R> { + /// Construct a `RawRngListIter`. + fn new(input: R, encoding: Encoding, format: RangeListsFormat) -> RawRngListIter<R> { + RawRngListIter { + input, + encoding, + format, + } + } + + /// Advance the iterator to the next range. + pub fn next(&mut self) -> Result<Option<RawRngListEntry<R::Offset>>> { + if self.input.is_empty() { + return Ok(None); + } + + match RawRngListEntry::parse(&mut self.input, self.encoding, self.format) { + Ok(range) => { + if range.is_none() { + self.input.empty(); + } + Ok(range) + } + Err(e) => { + self.input.empty(); + Err(e) + } + } + } +} + +#[cfg(feature = "fallible-iterator")] +impl<R: Reader> fallible_iterator::FallibleIterator for RawRngListIter<R> { + type Item = RawRngListEntry<R::Offset>; + type Error = Error; + + fn next(&mut self) -> ::core::result::Result<Option<Self::Item>, Self::Error> { + RawRngListIter::next(self) + } +} + +/// An iterator over an address range list. +/// +/// This iterator internally handles processing of base addresses and different +/// entry types. Thus, it only returns range entries that are valid +/// and already adjusted for the base address. +#[derive(Debug)] +pub struct RngListIter<R: Reader> { + raw: RawRngListIter<R>, + base_address: u64, + debug_addr: DebugAddr<R>, + debug_addr_base: DebugAddrBase<R::Offset>, +} + +impl<R: Reader> RngListIter<R> { + /// Construct a `RngListIter`. + fn new( + raw: RawRngListIter<R>, + base_address: u64, + debug_addr: DebugAddr<R>, + debug_addr_base: DebugAddrBase<R::Offset>, + ) -> RngListIter<R> { + RngListIter { + raw, + base_address, + debug_addr, + debug_addr_base, + } + } + + #[inline] + fn get_address(&self, index: DebugAddrIndex<R::Offset>) -> Result<u64> { + self.debug_addr + .get_address(self.raw.encoding.address_size, self.debug_addr_base, index) + } + + /// Advance the iterator to the next range. + pub fn next(&mut self) -> Result<Option<Range>> { + loop { + let raw_range = match self.raw.next()? { + Some(range) => range, + None => return Ok(None), + }; + + let range = self.convert_raw(raw_range)?; + if range.is_some() { + return Ok(range); + } + } + } + + /// Return the next raw range. + /// + /// The raw range should be passed to `convert_range`. + #[doc(hidden)] + pub fn next_raw(&mut self) -> Result<Option<RawRngListEntry<R::Offset>>> { + self.raw.next() + } + + /// Convert a raw range into a range, and update the state of the iterator. + /// + /// The raw range should have been obtained from `next_raw`. + #[doc(hidden)] + pub fn convert_raw(&mut self, raw_range: RawRngListEntry<R::Offset>) -> Result<Option<Range>> { + let mask = !0 >> (64 - self.raw.encoding.address_size * 8); + let tombstone = if self.raw.encoding.version <= 4 { + mask - 1 + } else { + mask + }; + + let range = match raw_range { + RawRngListEntry::BaseAddress { addr } => { + self.base_address = addr; + return Ok(None); + } + RawRngListEntry::BaseAddressx { addr } => { + self.base_address = self.get_address(addr)?; + return Ok(None); + } + RawRngListEntry::StartxEndx { begin, end } => { + let begin = self.get_address(begin)?; + let end = self.get_address(end)?; + Range { begin, end } + } + RawRngListEntry::StartxLength { begin, length } => { + let begin = self.get_address(begin)?; + let end = begin.wrapping_add(length) & mask; + Range { begin, end } + } + RawRngListEntry::AddressOrOffsetPair { begin, end } + | RawRngListEntry::OffsetPair { begin, end } => { + if self.base_address == tombstone { + return Ok(None); + } + let mut range = Range { begin, end }; + range.add_base_address(self.base_address, self.raw.encoding.address_size); + range + } + RawRngListEntry::StartEnd { begin, end } => Range { begin, end }, + RawRngListEntry::StartLength { begin, length } => { + let end = begin.wrapping_add(length) & mask; + Range { begin, end } + } + }; + + if range.begin == tombstone { + return Ok(None); + } + + if range.begin > range.end { + self.raw.input.empty(); + return Err(Error::InvalidAddressRange); + } + + Ok(Some(range)) + } +} + +#[cfg(feature = "fallible-iterator")] +impl<R: Reader> fallible_iterator::FallibleIterator for RngListIter<R> { + type Item = Range; + type Error = Error; + + fn next(&mut self) -> ::core::result::Result<Option<Self::Item>, Self::Error> { + RngListIter::next(self) + } +} + +/// A raw address range from the `.debug_ranges` section. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub(crate) struct RawRange { + /// The beginning address of the range. + pub begin: u64, + + /// The first address past the end of the range. + pub end: u64, +} + +impl RawRange { + /// Check if this is a range end entry. + #[inline] + pub fn is_end(&self) -> bool { + self.begin == 0 && self.end == 0 + } + + /// Check if this is a base address selection entry. + /// + /// A base address selection entry changes the base address that subsequent + /// range entries are relative to. + #[inline] + pub fn is_base_address(&self, address_size: u8) -> bool { + self.begin == !0 >> (64 - address_size * 8) + } + + /// Parse an address range entry from `.debug_ranges` or `.debug_loc`. + #[inline] + pub fn parse<R: Reader>(input: &mut R, address_size: u8) -> Result<RawRange> { + let begin = input.read_address(address_size)?; + let end = input.read_address(address_size)?; + let range = RawRange { begin, end }; + Ok(range) + } +} + +/// An address range from the `.debug_ranges`, `.debug_rnglists`, or `.debug_aranges` sections. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct Range { + /// The beginning address of the range. + pub begin: u64, + + /// The first address past the end of the range. + pub end: u64, +} + +impl Range { + /// Add a base address to this range. + #[inline] + pub(crate) fn add_base_address(&mut self, base_address: u64, address_size: u8) { + let mask = !0 >> (64 - address_size * 8); + self.begin = base_address.wrapping_add(self.begin) & mask; + self.end = base_address.wrapping_add(self.end) & mask; + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::common::Format; + use crate::endianity::LittleEndian; + use crate::test_util::GimliSectionMethods; + use test_assembler::{Endian, Label, LabelMaker, Section}; + + #[test] + fn test_rnglists_32() { + let tombstone = !0u32; + let encoding = Encoding { + format: Format::Dwarf32, + version: 5, + address_size: 4, + }; + let section = Section::with_endian(Endian::Little) + .L32(0x0300_0000) + .L32(0x0301_0300) + .L32(0x0301_0400) + .L32(0x0301_0500) + .L32(tombstone) + .L32(0x0301_0600); + let buf = section.get_contents().unwrap(); + let debug_addr = &DebugAddr::from(EndianSlice::new(&buf, LittleEndian)); + let debug_addr_base = DebugAddrBase(0); + + let start = Label::new(); + let first = Label::new(); + let size = Label::new(); + #[rustfmt::skip] + let section = Section::with_endian(Endian::Little) + // Header + .mark(&start) + .L32(&size) + .L16(encoding.version) + .L8(encoding.address_size) + .L8(0) + .L32(0) + .mark(&first) + // An OffsetPair using the unit base address. + .L8(4).uleb(0x10200).uleb(0x10300) + // A base address selection followed by an OffsetPair. + .L8(5).L32(0x0200_0000) + .L8(4).uleb(0x10400).uleb(0x10500) + // An empty OffsetPair followed by a normal OffsetPair. + .L8(4).uleb(0x10600).uleb(0x10600) + .L8(4).uleb(0x10800).uleb(0x10900) + // A StartEnd + .L8(6).L32(0x201_0a00).L32(0x201_0b00) + // A StartLength + .L8(7).L32(0x201_0c00).uleb(0x100) + // An OffsetPair that starts at 0. + .L8(4).uleb(0).uleb(1) + // An OffsetPair that starts and ends at 0. + .L8(4).uleb(0).uleb(0) + // An OffsetPair that ends at -1. + .L8(5).L32(0) + .L8(4).uleb(0).uleb(0xffff_ffff) + // A BaseAddressx + OffsetPair + .L8(1).uleb(0) + .L8(4).uleb(0x10100).uleb(0x10200) + // A StartxEndx + .L8(2).uleb(1).uleb(2) + // A StartxLength + .L8(3).uleb(3).uleb(0x100) + + // Tombstone entries, all of which should be ignored. + // A BaseAddressx that is a tombstone. + .L8(1).uleb(4) + .L8(4).uleb(0x11100).uleb(0x11200) + // A BaseAddress that is a tombstone. + .L8(5).L32(tombstone) + .L8(4).uleb(0x11300).uleb(0x11400) + // A StartxEndx that is a tombstone. + .L8(2).uleb(4).uleb(5) + // A StartxLength that is a tombstone. + .L8(3).uleb(4).uleb(0x100) + // A StartEnd that is a tombstone. + .L8(6).L32(tombstone).L32(0x201_1500) + // A StartLength that is a tombstone. + .L8(7).L32(tombstone).uleb(0x100) + // A StartEnd (not ignored) + .L8(6).L32(0x201_1600).L32(0x201_1700) + + // A range end. + .L8(0) + // Some extra data. + .L32(0xffff_ffff); + size.set_const((§ion.here() - &start - 4) as u64); + + let buf = section.get_contents().unwrap(); + let debug_ranges = DebugRanges::new(&[], LittleEndian); + let debug_rnglists = DebugRngLists::new(&buf, LittleEndian); + let rnglists = RangeLists::new(debug_ranges, debug_rnglists); + let offset = RangeListsOffset((&first - &start) as usize); + let mut ranges = rnglists + .ranges(offset, encoding, 0x0100_0000, debug_addr, debug_addr_base) + .unwrap(); + + // A normal range. + assert_eq!( + ranges.next(), + Ok(Some(Range { + begin: 0x0101_0200, + end: 0x0101_0300, + })) + ); + + // A base address selection followed by a normal range. + assert_eq!( + ranges.next(), + Ok(Some(Range { + begin: 0x0201_0400, + end: 0x0201_0500, + })) + ); + + // An empty range followed by a normal range. + assert_eq!( + ranges.next(), + Ok(Some(Range { + begin: 0x0201_0600, + end: 0x0201_0600, + })) + ); + assert_eq!( + ranges.next(), + Ok(Some(Range { + begin: 0x0201_0800, + end: 0x0201_0900, + })) + ); + + // A normal range. + assert_eq!( + ranges.next(), + Ok(Some(Range { + begin: 0x0201_0a00, + end: 0x0201_0b00, + })) + ); + + // A normal range. + assert_eq!( + ranges.next(), + Ok(Some(Range { + begin: 0x0201_0c00, + end: 0x0201_0d00, + })) + ); + + // A range that starts at 0. + assert_eq!( + ranges.next(), + Ok(Some(Range { + begin: 0x0200_0000, + end: 0x0200_0001, + })) + ); + + // A range that starts and ends at 0. + assert_eq!( + ranges.next(), + Ok(Some(Range { + begin: 0x0200_0000, + end: 0x0200_0000, + })) + ); + + // A range that ends at -1. + assert_eq!( + ranges.next(), + Ok(Some(Range { + begin: 0x0000_0000, + end: 0xffff_ffff, + })) + ); + + // A BaseAddressx + OffsetPair + assert_eq!( + ranges.next(), + Ok(Some(Range { + begin: 0x0301_0100, + end: 0x0301_0200, + })) + ); + + // A StartxEndx + assert_eq!( + ranges.next(), + Ok(Some(Range { + begin: 0x0301_0300, + end: 0x0301_0400, + })) + ); + + // A StartxLength + assert_eq!( + ranges.next(), + Ok(Some(Range { + begin: 0x0301_0500, + end: 0x0301_0600, + })) + ); + + // A StartEnd range following the tombstones + assert_eq!( + ranges.next(), + Ok(Some(Range { + begin: 0x0201_1600, + end: 0x0201_1700, + })) + ); + + // A range end. + assert_eq!(ranges.next(), Ok(None)); + + // An offset at the end of buf. + let mut ranges = rnglists + .ranges( + RangeListsOffset(buf.len()), + encoding, + 0x0100_0000, + debug_addr, + debug_addr_base, + ) + .unwrap(); + assert_eq!(ranges.next(), Ok(None)); + } + + #[test] + fn test_rnglists_64() { + let tombstone = !0u64; + let encoding = Encoding { + format: Format::Dwarf64, + version: 5, + address_size: 8, + }; + let section = Section::with_endian(Endian::Little) + .L64(0x0300_0000) + .L64(0x0301_0300) + .L64(0x0301_0400) + .L64(0x0301_0500) + .L64(tombstone) + .L64(0x0301_0600); + let buf = section.get_contents().unwrap(); + let debug_addr = &DebugAddr::from(EndianSlice::new(&buf, LittleEndian)); + let debug_addr_base = DebugAddrBase(0); + + let start = Label::new(); + let first = Label::new(); + let size = Label::new(); + #[rustfmt::skip] + let section = Section::with_endian(Endian::Little) + // Header + .mark(&start) + .L32(0xffff_ffff) + .L64(&size) + .L16(encoding.version) + .L8(encoding.address_size) + .L8(0) + .L32(0) + .mark(&first) + // An OffsetPair using the unit base address. + .L8(4).uleb(0x10200).uleb(0x10300) + // A base address selection followed by an OffsetPair. + .L8(5).L64(0x0200_0000) + .L8(4).uleb(0x10400).uleb(0x10500) + // An empty OffsetPair followed by a normal OffsetPair. + .L8(4).uleb(0x10600).uleb(0x10600) + .L8(4).uleb(0x10800).uleb(0x10900) + // A StartEnd + .L8(6).L64(0x201_0a00).L64(0x201_0b00) + // A StartLength + .L8(7).L64(0x201_0c00).uleb(0x100) + // An OffsetPair that starts at 0. + .L8(4).uleb(0).uleb(1) + // An OffsetPair that starts and ends at 0. + .L8(4).uleb(0).uleb(0) + // An OffsetPair that ends at -1. + .L8(5).L64(0) + .L8(4).uleb(0).uleb(0xffff_ffff) + // A BaseAddressx + OffsetPair + .L8(1).uleb(0) + .L8(4).uleb(0x10100).uleb(0x10200) + // A StartxEndx + .L8(2).uleb(1).uleb(2) + // A StartxLength + .L8(3).uleb(3).uleb(0x100) + + // Tombstone entries, all of which should be ignored. + // A BaseAddressx that is a tombstone. + .L8(1).uleb(4) + .L8(4).uleb(0x11100).uleb(0x11200) + // A BaseAddress that is a tombstone. + .L8(5).L64(tombstone) + .L8(4).uleb(0x11300).uleb(0x11400) + // A StartxEndx that is a tombstone. + .L8(2).uleb(4).uleb(5) + // A StartxLength that is a tombstone. + .L8(3).uleb(4).uleb(0x100) + // A StartEnd that is a tombstone. + .L8(6).L64(tombstone).L64(0x201_1500) + // A StartLength that is a tombstone. + .L8(7).L64(tombstone).uleb(0x100) + // A StartEnd (not ignored) + .L8(6).L64(0x201_1600).L64(0x201_1700) + + // A range end. + .L8(0) + // Some extra data. + .L32(0xffff_ffff); + size.set_const((§ion.here() - &start - 12) as u64); + + let buf = section.get_contents().unwrap(); + let debug_ranges = DebugRanges::new(&[], LittleEndian); + let debug_rnglists = DebugRngLists::new(&buf, LittleEndian); + let rnglists = RangeLists::new(debug_ranges, debug_rnglists); + let offset = RangeListsOffset((&first - &start) as usize); + let mut ranges = rnglists + .ranges(offset, encoding, 0x0100_0000, debug_addr, debug_addr_base) + .unwrap(); + + // A normal range. + assert_eq!( + ranges.next(), + Ok(Some(Range { + begin: 0x0101_0200, + end: 0x0101_0300, + })) + ); + + // A base address selection followed by a normal range. + assert_eq!( + ranges.next(), + Ok(Some(Range { + begin: 0x0201_0400, + end: 0x0201_0500, + })) + ); + + // An empty range followed by a normal range. + assert_eq!( + ranges.next(), + Ok(Some(Range { + begin: 0x0201_0600, + end: 0x0201_0600, + })) + ); + assert_eq!( + ranges.next(), + Ok(Some(Range { + begin: 0x0201_0800, + end: 0x0201_0900, + })) + ); + + // A normal range. + assert_eq!( + ranges.next(), + Ok(Some(Range { + begin: 0x0201_0a00, + end: 0x0201_0b00, + })) + ); + + // A normal range. + assert_eq!( + ranges.next(), + Ok(Some(Range { + begin: 0x0201_0c00, + end: 0x0201_0d00, + })) + ); + + // A range that starts at 0. + assert_eq!( + ranges.next(), + Ok(Some(Range { + begin: 0x0200_0000, + end: 0x0200_0001, + })) + ); + + // A range that starts and ends at 0. + assert_eq!( + ranges.next(), + Ok(Some(Range { + begin: 0x0200_0000, + end: 0x0200_0000, + })) + ); + + // A range that ends at -1. + assert_eq!( + ranges.next(), + Ok(Some(Range { + begin: 0x0000_0000, + end: 0xffff_ffff, + })) + ); + + // A BaseAddressx + OffsetPair + assert_eq!( + ranges.next(), + Ok(Some(Range { + begin: 0x0301_0100, + end: 0x0301_0200, + })) + ); + + // A StartxEndx + assert_eq!( + ranges.next(), + Ok(Some(Range { + begin: 0x0301_0300, + end: 0x0301_0400, + })) + ); + + // A StartxLength + assert_eq!( + ranges.next(), + Ok(Some(Range { + begin: 0x0301_0500, + end: 0x0301_0600, + })) + ); + + // A StartEnd range following the tombstones + assert_eq!( + ranges.next(), + Ok(Some(Range { + begin: 0x0201_1600, + end: 0x0201_1700, + })) + ); + + // A range end. + assert_eq!(ranges.next(), Ok(None)); + + // An offset at the end of buf. + let mut ranges = rnglists + .ranges( + RangeListsOffset(buf.len()), + encoding, + 0x0100_0000, + debug_addr, + debug_addr_base, + ) + .unwrap(); + assert_eq!(ranges.next(), Ok(None)); + } + + #[test] + fn test_raw_range() { + let range = RawRange { + begin: 0, + end: 0xffff_ffff, + }; + assert!(!range.is_end()); + assert!(!range.is_base_address(4)); + assert!(!range.is_base_address(8)); + + let range = RawRange { begin: 0, end: 0 }; + assert!(range.is_end()); + assert!(!range.is_base_address(4)); + assert!(!range.is_base_address(8)); + + let range = RawRange { + begin: 0xffff_ffff, + end: 0, + }; + assert!(!range.is_end()); + assert!(range.is_base_address(4)); + assert!(!range.is_base_address(8)); + + let range = RawRange { + begin: 0xffff_ffff_ffff_ffff, + end: 0, + }; + assert!(!range.is_end()); + assert!(!range.is_base_address(4)); + assert!(range.is_base_address(8)); + } + + #[test] + fn test_ranges_32() { + let tombstone = !0u32 - 1; + let start = Label::new(); + let first = Label::new(); + #[rustfmt::skip] + let section = Section::with_endian(Endian::Little) + // A range before the offset. + .mark(&start) + .L32(0x10000).L32(0x10100) + .mark(&first) + // A normal range. + .L32(0x10200).L32(0x10300) + // A base address selection followed by a normal range. + .L32(0xffff_ffff).L32(0x0200_0000) + .L32(0x10400).L32(0x10500) + // An empty range followed by a normal range. + .L32(0x10600).L32(0x10600) + .L32(0x10800).L32(0x10900) + // A range that starts at 0. + .L32(0).L32(1) + // A range that ends at -1. + .L32(0xffff_ffff).L32(0x0000_0000) + .L32(0).L32(0xffff_ffff) + // A normal range with tombstone. + .L32(tombstone).L32(tombstone) + // A base address selection with tombstone followed by a normal range. + .L32(0xffff_ffff).L32(tombstone) + .L32(0x10a00).L32(0x10b00) + // A range end. + .L32(0).L32(0) + // Some extra data. + .L32(0); + + let buf = section.get_contents().unwrap(); + let debug_ranges = DebugRanges::new(&buf, LittleEndian); + let debug_rnglists = DebugRngLists::new(&[], LittleEndian); + let rnglists = RangeLists::new(debug_ranges, debug_rnglists); + let offset = RangeListsOffset((&first - &start) as usize); + let debug_addr = &DebugAddr::from(EndianSlice::new(&[], LittleEndian)); + let debug_addr_base = DebugAddrBase(0); + let encoding = Encoding { + format: Format::Dwarf32, + version: 4, + address_size: 4, + }; + let mut ranges = rnglists + .ranges(offset, encoding, 0x0100_0000, debug_addr, debug_addr_base) + .unwrap(); + + // A normal range. + assert_eq!( + ranges.next(), + Ok(Some(Range { + begin: 0x0101_0200, + end: 0x0101_0300, + })) + ); + + // A base address selection followed by a normal range. + assert_eq!( + ranges.next(), + Ok(Some(Range { + begin: 0x0201_0400, + end: 0x0201_0500, + })) + ); + + // An empty range followed by a normal range. + assert_eq!( + ranges.next(), + Ok(Some(Range { + begin: 0x0201_0600, + end: 0x0201_0600, + })) + ); + assert_eq!( + ranges.next(), + Ok(Some(Range { + begin: 0x0201_0800, + end: 0x0201_0900, + })) + ); + + // A range that starts at 0. + assert_eq!( + ranges.next(), + Ok(Some(Range { + begin: 0x0200_0000, + end: 0x0200_0001, + })) + ); + + // A range that ends at -1. + assert_eq!( + ranges.next(), + Ok(Some(Range { + begin: 0x0000_0000, + end: 0xffff_ffff, + })) + ); + + // A range end. + assert_eq!(ranges.next(), Ok(None)); + + // An offset at the end of buf. + let mut ranges = rnglists + .ranges( + RangeListsOffset(buf.len()), + encoding, + 0x0100_0000, + debug_addr, + debug_addr_base, + ) + .unwrap(); + assert_eq!(ranges.next(), Ok(None)); + } + + #[test] + fn test_ranges_64() { + let tombstone = !0u64 - 1; + let start = Label::new(); + let first = Label::new(); + #[rustfmt::skip] + let section = Section::with_endian(Endian::Little) + // A range before the offset. + .mark(&start) + .L64(0x10000).L64(0x10100) + .mark(&first) + // A normal range. + .L64(0x10200).L64(0x10300) + // A base address selection followed by a normal range. + .L64(0xffff_ffff_ffff_ffff).L64(0x0200_0000) + .L64(0x10400).L64(0x10500) + // An empty range followed by a normal range. + .L64(0x10600).L64(0x10600) + .L64(0x10800).L64(0x10900) + // A range that starts at 0. + .L64(0).L64(1) + // A range that ends at -1. + .L64(0xffff_ffff_ffff_ffff).L64(0x0000_0000) + .L64(0).L64(0xffff_ffff_ffff_ffff) + // A normal range with tombstone. + .L64(tombstone).L64(tombstone) + // A base address selection with tombstone followed by a normal range. + .L64(0xffff_ffff_ffff_ffff).L64(tombstone) + .L64(0x10a00).L64(0x10b00) + // A range end. + .L64(0).L64(0) + // Some extra data. + .L64(0); + + let buf = section.get_contents().unwrap(); + let debug_ranges = DebugRanges::new(&buf, LittleEndian); + let debug_rnglists = DebugRngLists::new(&[], LittleEndian); + let rnglists = RangeLists::new(debug_ranges, debug_rnglists); + let offset = RangeListsOffset((&first - &start) as usize); + let debug_addr = &DebugAddr::from(EndianSlice::new(&[], LittleEndian)); + let debug_addr_base = DebugAddrBase(0); + let encoding = Encoding { + format: Format::Dwarf64, + version: 4, + address_size: 8, + }; + let mut ranges = rnglists + .ranges(offset, encoding, 0x0100_0000, debug_addr, debug_addr_base) + .unwrap(); + + // A normal range. + assert_eq!( + ranges.next(), + Ok(Some(Range { + begin: 0x0101_0200, + end: 0x0101_0300, + })) + ); + + // A base address selection followed by a normal range. + assert_eq!( + ranges.next(), + Ok(Some(Range { + begin: 0x0201_0400, + end: 0x0201_0500, + })) + ); + + // An empty range followed by a normal range. + assert_eq!( + ranges.next(), + Ok(Some(Range { + begin: 0x0201_0600, + end: 0x0201_0600, + })) + ); + assert_eq!( + ranges.next(), + Ok(Some(Range { + begin: 0x0201_0800, + end: 0x0201_0900, + })) + ); + + // A range that starts at 0. + assert_eq!( + ranges.next(), + Ok(Some(Range { + begin: 0x0200_0000, + end: 0x0200_0001, + })) + ); + + // A range that ends at -1. + assert_eq!( + ranges.next(), + Ok(Some(Range { + begin: 0x0, + end: 0xffff_ffff_ffff_ffff, + })) + ); + + // A range end. + assert_eq!(ranges.next(), Ok(None)); + + // An offset at the end of buf. + let mut ranges = rnglists + .ranges( + RangeListsOffset(buf.len()), + encoding, + 0x0100_0000, + debug_addr, + debug_addr_base, + ) + .unwrap(); + assert_eq!(ranges.next(), Ok(None)); + } + + #[test] + fn test_ranges_invalid() { + #[rustfmt::skip] + let section = Section::with_endian(Endian::Little) + // An invalid range. + .L32(0x20000).L32(0x10000) + // An invalid range after wrapping. + .L32(0x20000).L32(0xff01_0000); + + let buf = section.get_contents().unwrap(); + let debug_ranges = DebugRanges::new(&buf, LittleEndian); + let debug_rnglists = DebugRngLists::new(&[], LittleEndian); + let rnglists = RangeLists::new(debug_ranges, debug_rnglists); + let debug_addr = &DebugAddr::from(EndianSlice::new(&[], LittleEndian)); + let debug_addr_base = DebugAddrBase(0); + let encoding = Encoding { + format: Format::Dwarf32, + version: 4, + address_size: 4, + }; + + // An invalid range. + let mut ranges = rnglists + .ranges( + RangeListsOffset(0x0), + encoding, + 0x0100_0000, + debug_addr, + debug_addr_base, + ) + .unwrap(); + assert_eq!(ranges.next(), Err(Error::InvalidAddressRange)); + + // An invalid range after wrapping. + let mut ranges = rnglists + .ranges( + RangeListsOffset(0x8), + encoding, + 0x0100_0000, + debug_addr, + debug_addr_base, + ) + .unwrap(); + assert_eq!(ranges.next(), Err(Error::InvalidAddressRange)); + + // An invalid offset. + match rnglists.ranges( + RangeListsOffset(buf.len() + 1), + encoding, + 0x0100_0000, + debug_addr, + debug_addr_base, + ) { + Err(Error::UnexpectedEof(_)) => {} + otherwise => panic!("Unexpected result: {:?}", otherwise), + } + } + + #[test] + fn test_get_offset() { + for format in vec![Format::Dwarf32, Format::Dwarf64] { + let encoding = Encoding { + format, + version: 5, + address_size: 4, + }; + + let zero = Label::new(); + let length = Label::new(); + let start = Label::new(); + let first = Label::new(); + let end = Label::new(); + let mut section = Section::with_endian(Endian::Little) + .mark(&zero) + .initial_length(format, &length, &start) + .D16(encoding.version) + .D8(encoding.address_size) + .D8(0) + .D32(20) + .mark(&first); + for i in 0..20 { + section = section.word(format.word_size(), 1000 + i); + } + section = section.mark(&end); + length.set_const((&end - &start) as u64); + let section = section.get_contents().unwrap(); + + let debug_ranges = DebugRanges::from(EndianSlice::new(&[], LittleEndian)); + let debug_rnglists = DebugRngLists::from(EndianSlice::new(§ion, LittleEndian)); + let ranges = RangeLists::new(debug_ranges, debug_rnglists); + + let base = DebugRngListsBase((&first - &zero) as usize); + assert_eq!( + ranges.get_offset(encoding, base, DebugRngListsIndex(0)), + Ok(RangeListsOffset(base.0 + 1000)) + ); + assert_eq!( + ranges.get_offset(encoding, base, DebugRngListsIndex(19)), + Ok(RangeListsOffset(base.0 + 1019)) + ); + } + } +} diff --git a/vendor/gimli/src/read/str.rs b/vendor/gimli/src/read/str.rs new file mode 100644 index 0000000..c6b87d8 --- /dev/null +++ b/vendor/gimli/src/read/str.rs @@ -0,0 +1,321 @@ +use crate::common::{ + DebugLineStrOffset, DebugStrOffset, DebugStrOffsetsBase, DebugStrOffsetsIndex, DwarfFileType, + Encoding, SectionId, +}; +use crate::endianity::Endianity; +use crate::read::{EndianSlice, Reader, ReaderOffset, Result, Section}; +use crate::Format; + +/// The `DebugStr` struct represents the DWARF strings +/// found in the `.debug_str` section. +#[derive(Debug, Default, Clone, Copy)] +pub struct DebugStr<R> { + debug_str_section: R, +} + +impl<'input, Endian> DebugStr<EndianSlice<'input, Endian>> +where + Endian: Endianity, +{ + /// Construct a new `DebugStr` instance from the data in the `.debug_str` + /// section. + /// + /// It is the caller's responsibility to read the `.debug_str` section and + /// present it as a `&[u8]` slice. That means using some ELF loader on + /// Linux, a Mach-O loader on macOS, etc. + /// + /// ``` + /// use gimli::{DebugStr, LittleEndian}; + /// + /// # let buf = [0x00, 0x01, 0x02, 0x03]; + /// # let read_debug_str_section_somehow = || &buf; + /// let debug_str = DebugStr::new(read_debug_str_section_somehow(), LittleEndian); + /// ``` + pub fn new(debug_str_section: &'input [u8], endian: Endian) -> Self { + Self::from(EndianSlice::new(debug_str_section, endian)) + } +} + +impl<R: Reader> DebugStr<R> { + /// Lookup a string from the `.debug_str` section by DebugStrOffset. + /// + /// ``` + /// use gimli::{DebugStr, DebugStrOffset, LittleEndian}; + /// + /// # let buf = [0x01, 0x02, 0x00]; + /// # let offset = DebugStrOffset(0); + /// # let read_debug_str_section_somehow = || &buf; + /// # let debug_str_offset_somehow = || offset; + /// let debug_str = DebugStr::new(read_debug_str_section_somehow(), LittleEndian); + /// println!("Found string {:?}", debug_str.get_str(debug_str_offset_somehow())); + /// ``` + pub fn get_str(&self, offset: DebugStrOffset<R::Offset>) -> Result<R> { + let input = &mut self.debug_str_section.clone(); + input.skip(offset.0)?; + input.read_null_terminated_slice() + } +} + +impl<T> DebugStr<T> { + /// Create a `DebugStr` section that references the data in `self`. + /// + /// This is useful when `R` implements `Reader` but `T` does not. + /// + /// ## Example Usage + /// + /// ```rust,no_run + /// # let load_section = || unimplemented!(); + /// // Read the DWARF section into a `Vec` with whatever object loader you're using. + /// let owned_section: gimli::DebugStr<Vec<u8>> = load_section(); + /// // Create a reference to the DWARF section. + /// let section = owned_section.borrow(|section| { + /// gimli::EndianSlice::new(§ion, gimli::LittleEndian) + /// }); + /// ``` + pub fn borrow<'a, F, R>(&'a self, mut borrow: F) -> DebugStr<R> + where + F: FnMut(&'a T) -> R, + { + borrow(&self.debug_str_section).into() + } +} + +impl<R> Section<R> for DebugStr<R> { + fn id() -> SectionId { + SectionId::DebugStr + } + + fn reader(&self) -> &R { + &self.debug_str_section + } +} + +impl<R> From<R> for DebugStr<R> { + fn from(debug_str_section: R) -> Self { + DebugStr { debug_str_section } + } +} + +/// The raw contents of the `.debug_str_offsets` section. +#[derive(Debug, Default, Clone, Copy)] +pub struct DebugStrOffsets<R> { + section: R, +} + +impl<R: Reader> DebugStrOffsets<R> { + // TODO: add an iterator over the sets of entries in the section. + // This is not needed for common usage of the section though. + + /// Returns the `.debug_str` offset at the given `base` and `index`. + /// + /// A set of entries in the `.debug_str_offsets` section consists of a header + /// followed by a series of string table offsets. + /// + /// The `base` must be the `DW_AT_str_offsets_base` value from the compilation unit DIE. + /// This is an offset that points to the first entry following the header. + /// + /// The `index` is the value of a `DW_FORM_strx` attribute. + /// + /// The `format` must be the DWARF format of the compilation unit. This format must + /// match the header. However, note that we do not parse the header to validate this, + /// since locating the header is unreliable, and the GNU extensions do not emit it. + pub fn get_str_offset( + &self, + format: Format, + base: DebugStrOffsetsBase<R::Offset>, + index: DebugStrOffsetsIndex<R::Offset>, + ) -> Result<DebugStrOffset<R::Offset>> { + let input = &mut self.section.clone(); + input.skip(base.0)?; + input.skip(R::Offset::from_u64( + index.0.into_u64() * u64::from(format.word_size()), + )?)?; + input.read_offset(format).map(DebugStrOffset) + } +} + +impl<T> DebugStrOffsets<T> { + /// Create a `DebugStrOffsets` section that references the data in `self`. + /// + /// This is useful when `R` implements `Reader` but `T` does not. + /// + /// ## Example Usage + /// + /// ```rust,no_run + /// # let load_section = || unimplemented!(); + /// // Read the DWARF section into a `Vec` with whatever object loader you're using. + /// let owned_section: gimli::DebugStrOffsets<Vec<u8>> = load_section(); + /// // Create a reference to the DWARF section. + /// let section = owned_section.borrow(|section| { + /// gimli::EndianSlice::new(§ion, gimli::LittleEndian) + /// }); + /// ``` + pub fn borrow<'a, F, R>(&'a self, mut borrow: F) -> DebugStrOffsets<R> + where + F: FnMut(&'a T) -> R, + { + borrow(&self.section).into() + } +} + +impl<R> Section<R> for DebugStrOffsets<R> { + fn id() -> SectionId { + SectionId::DebugStrOffsets + } + + fn reader(&self) -> &R { + &self.section + } +} + +impl<R> From<R> for DebugStrOffsets<R> { + fn from(section: R) -> Self { + DebugStrOffsets { section } + } +} + +impl<Offset> DebugStrOffsetsBase<Offset> +where + Offset: ReaderOffset, +{ + /// Returns a `DebugStrOffsetsBase` with the default value of DW_AT_str_offsets_base + /// for the given `Encoding` and `DwarfFileType`. + pub fn default_for_encoding_and_file( + encoding: Encoding, + file_type: DwarfFileType, + ) -> DebugStrOffsetsBase<Offset> { + if encoding.version >= 5 && file_type == DwarfFileType::Dwo { + // In .dwo files, the compiler omits the DW_AT_str_offsets_base attribute (because there is + // only a single unit in the file) but we must skip past the header, which the attribute + // would normally do for us. + // initial_length_size + version + 2 bytes of padding. + DebugStrOffsetsBase(Offset::from_u8( + encoding.format.initial_length_size() + 2 + 2, + )) + } else { + DebugStrOffsetsBase(Offset::from_u8(0)) + } + } +} + +/// The `DebugLineStr` struct represents the DWARF strings +/// found in the `.debug_line_str` section. +#[derive(Debug, Default, Clone, Copy)] +pub struct DebugLineStr<R> { + section: R, +} + +impl<'input, Endian> DebugLineStr<EndianSlice<'input, Endian>> +where + Endian: Endianity, +{ + /// Construct a new `DebugLineStr` instance from the data in the `.debug_line_str` + /// section. + /// + /// It is the caller's responsibility to read the `.debug_line_str` section and + /// present it as a `&[u8]` slice. That means using some ELF loader on + /// Linux, a Mach-O loader on macOS, etc. + /// + /// ``` + /// use gimli::{DebugLineStr, LittleEndian}; + /// + /// # let buf = [0x00, 0x01, 0x02, 0x03]; + /// # let read_debug_line_str_section_somehow = || &buf; + /// let debug_str = DebugLineStr::new(read_debug_line_str_section_somehow(), LittleEndian); + /// ``` + pub fn new(debug_line_str_section: &'input [u8], endian: Endian) -> Self { + Self::from(EndianSlice::new(debug_line_str_section, endian)) + } +} + +impl<R: Reader> DebugLineStr<R> { + /// Lookup a string from the `.debug_line_str` section by DebugLineStrOffset. + pub fn get_str(&self, offset: DebugLineStrOffset<R::Offset>) -> Result<R> { + let input = &mut self.section.clone(); + input.skip(offset.0)?; + input.read_null_terminated_slice() + } +} + +impl<T> DebugLineStr<T> { + /// Create a `DebugLineStr` section that references the data in `self`. + /// + /// This is useful when `R` implements `Reader` but `T` does not. + /// + /// ## Example Usage + /// + /// ```rust,no_run + /// # let load_section = || unimplemented!(); + /// // Read the DWARF section into a `Vec` with whatever object loader you're using. + /// let owned_section: gimli::DebugLineStr<Vec<u8>> = load_section(); + /// // Create a reference to the DWARF section. + /// let section = owned_section.borrow(|section| { + /// gimli::EndianSlice::new(§ion, gimli::LittleEndian) + /// }); + /// ``` + pub fn borrow<'a, F, R>(&'a self, mut borrow: F) -> DebugLineStr<R> + where + F: FnMut(&'a T) -> R, + { + borrow(&self.section).into() + } +} + +impl<R> Section<R> for DebugLineStr<R> { + fn id() -> SectionId { + SectionId::DebugLineStr + } + + fn reader(&self) -> &R { + &self.section + } +} + +impl<R> From<R> for DebugLineStr<R> { + fn from(section: R) -> Self { + DebugLineStr { section } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::test_util::GimliSectionMethods; + use crate::LittleEndian; + use test_assembler::{Endian, Label, LabelMaker, Section}; + + #[test] + fn test_get_str_offset() { + for format in vec![Format::Dwarf32, Format::Dwarf64] { + let zero = Label::new(); + let length = Label::new(); + let start = Label::new(); + let first = Label::new(); + let end = Label::new(); + let mut section = Section::with_endian(Endian::Little) + .mark(&zero) + .initial_length(format, &length, &start) + .D16(5) + .D16(0) + .mark(&first); + for i in 0..20 { + section = section.word(format.word_size(), 1000 + i); + } + section = section.mark(&end); + length.set_const((&end - &start) as u64); + + let section = section.get_contents().unwrap(); + let debug_str_offsets = DebugStrOffsets::from(EndianSlice::new(§ion, LittleEndian)); + let base = DebugStrOffsetsBase((&first - &zero) as usize); + + assert_eq!( + debug_str_offsets.get_str_offset(format, base, DebugStrOffsetsIndex(0)), + Ok(DebugStrOffset(1000)) + ); + assert_eq!( + debug_str_offsets.get_str_offset(format, base, DebugStrOffsetsIndex(19)), + Ok(DebugStrOffset(1019)) + ); + } + } +} diff --git a/vendor/gimli/src/read/unit.rs b/vendor/gimli/src/read/unit.rs new file mode 100644 index 0000000..d799f0f --- /dev/null +++ b/vendor/gimli/src/read/unit.rs @@ -0,0 +1,6139 @@ +//! Functions for parsing DWARF `.debug_info` and `.debug_types` sections. + +use core::cell::Cell; +use core::ops::{Range, RangeFrom, RangeTo}; +use core::{u16, u8}; + +use crate::common::{ + DebugAbbrevOffset, DebugAddrBase, DebugAddrIndex, DebugInfoOffset, DebugLineOffset, + DebugLineStrOffset, DebugLocListsBase, DebugLocListsIndex, DebugMacinfoOffset, + DebugMacroOffset, DebugRngListsBase, DebugRngListsIndex, DebugStrOffset, DebugStrOffsetsBase, + DebugStrOffsetsIndex, DebugTypeSignature, DebugTypesOffset, DwoId, Encoding, Format, + LocationListsOffset, RawRangeListsOffset, SectionId, UnitSectionOffset, +}; +use crate::constants; +use crate::endianity::Endianity; +use crate::read::abbrev::get_attribute_size; +use crate::read::{ + Abbreviation, Abbreviations, AttributeSpecification, DebugAbbrev, DebugStr, EndianSlice, Error, + Expression, Reader, ReaderOffset, Result, Section, UnitOffset, +}; + +impl<T: ReaderOffset> DebugTypesOffset<T> { + /// Convert an offset to be relative to the start of the given unit, + /// instead of relative to the start of the .debug_types section. + /// Returns `None` if the offset is not within the unit entries. + pub fn to_unit_offset<R>(&self, unit: &UnitHeader<R>) -> Option<UnitOffset<T>> + where + R: Reader<Offset = T>, + { + let unit_offset = unit.offset().as_debug_types_offset()?; + let offset = UnitOffset(self.0.checked_sub(unit_offset.0)?); + if !unit.is_valid_offset(offset) { + return None; + } + Some(offset) + } +} + +impl<T: ReaderOffset> DebugInfoOffset<T> { + /// Convert an offset to be relative to the start of the given unit, + /// instead of relative to the start of the .debug_info section. + /// Returns `None` if the offset is not within this unit entries. + pub fn to_unit_offset<R>(&self, unit: &UnitHeader<R>) -> Option<UnitOffset<T>> + where + R: Reader<Offset = T>, + { + let unit_offset = unit.offset().as_debug_info_offset()?; + let offset = UnitOffset(self.0.checked_sub(unit_offset.0)?); + if !unit.is_valid_offset(offset) { + return None; + } + Some(offset) + } +} + +impl<T: ReaderOffset> UnitOffset<T> { + /// Convert an offset to be relative to the start of the .debug_info section, + /// instead of relative to the start of the given unit. Returns None if the + /// provided unit lives in the .debug_types section. + pub fn to_debug_info_offset<R>(&self, unit: &UnitHeader<R>) -> Option<DebugInfoOffset<T>> + where + R: Reader<Offset = T>, + { + let unit_offset = unit.offset().as_debug_info_offset()?; + Some(DebugInfoOffset(unit_offset.0 + self.0)) + } + + /// Convert an offset to be relative to the start of the .debug_types section, + /// instead of relative to the start of the given unit. Returns None if the + /// provided unit lives in the .debug_info section. + pub fn to_debug_types_offset<R>(&self, unit: &UnitHeader<R>) -> Option<DebugTypesOffset<T>> + where + R: Reader<Offset = T>, + { + let unit_offset = unit.offset().as_debug_types_offset()?; + Some(DebugTypesOffset(unit_offset.0 + self.0)) + } +} + +/// The `DebugInfo` struct represents the DWARF debugging information found in +/// the `.debug_info` section. +#[derive(Debug, Default, Clone, Copy)] +pub struct DebugInfo<R> { + debug_info_section: R, +} + +impl<'input, Endian> DebugInfo<EndianSlice<'input, Endian>> +where + Endian: Endianity, +{ + /// Construct a new `DebugInfo` instance from the data in the `.debug_info` + /// section. + /// + /// It is the caller's responsibility to read the `.debug_info` section and + /// present it as a `&[u8]` slice. That means using some ELF loader on + /// Linux, a Mach-O loader on macOS, etc. + /// + /// ``` + /// use gimli::{DebugInfo, LittleEndian}; + /// + /// # let buf = [0x00, 0x01, 0x02, 0x03]; + /// # let read_debug_info_section_somehow = || &buf; + /// let debug_info = DebugInfo::new(read_debug_info_section_somehow(), LittleEndian); + /// ``` + pub fn new(debug_info_section: &'input [u8], endian: Endian) -> Self { + Self::from(EndianSlice::new(debug_info_section, endian)) + } +} + +impl<R: Reader> DebugInfo<R> { + /// Iterate the units in this `.debug_info` section. + /// + /// ``` + /// use gimli::{DebugInfo, LittleEndian}; + /// + /// # let buf = []; + /// # let read_debug_info_section_somehow = || &buf; + /// let debug_info = DebugInfo::new(read_debug_info_section_somehow(), LittleEndian); + /// + /// let mut iter = debug_info.units(); + /// while let Some(unit) = iter.next().unwrap() { + /// println!("unit's length is {}", unit.unit_length()); + /// } + /// ``` + /// + /// Can be [used with + /// `FallibleIterator`](./index.html#using-with-fallibleiterator). + pub fn units(&self) -> DebugInfoUnitHeadersIter<R> { + DebugInfoUnitHeadersIter { + input: self.debug_info_section.clone(), + offset: DebugInfoOffset(R::Offset::from_u8(0)), + } + } + + /// Get the UnitHeader located at offset from this .debug_info section. + /// + /// + pub fn header_from_offset(&self, offset: DebugInfoOffset<R::Offset>) -> Result<UnitHeader<R>> { + let input = &mut self.debug_info_section.clone(); + input.skip(offset.0)?; + parse_unit_header(input, offset.into()) + } +} + +impl<T> DebugInfo<T> { + /// Create a `DebugInfo` section that references the data in `self`. + /// + /// This is useful when `R` implements `Reader` but `T` does not. + /// + /// ## Example Usage + /// + /// ```rust,no_run + /// # let load_section = || unimplemented!(); + /// // Read the DWARF section into a `Vec` with whatever object loader you're using. + /// let owned_section: gimli::DebugInfo<Vec<u8>> = load_section(); + /// // Create a reference to the DWARF section. + /// let section = owned_section.borrow(|section| { + /// gimli::EndianSlice::new(§ion, gimli::LittleEndian) + /// }); + /// ``` + pub fn borrow<'a, F, R>(&'a self, mut borrow: F) -> DebugInfo<R> + where + F: FnMut(&'a T) -> R, + { + borrow(&self.debug_info_section).into() + } +} + +impl<R> Section<R> for DebugInfo<R> { + fn id() -> SectionId { + SectionId::DebugInfo + } + + fn reader(&self) -> &R { + &self.debug_info_section + } +} + +impl<R> From<R> for DebugInfo<R> { + fn from(debug_info_section: R) -> Self { + DebugInfo { debug_info_section } + } +} + +/// An iterator over the units of a .debug_info section. +/// +/// See the [documentation on +/// `DebugInfo::units`](./struct.DebugInfo.html#method.units) for more detail. +#[derive(Clone, Debug)] +pub struct DebugInfoUnitHeadersIter<R: Reader> { + input: R, + offset: DebugInfoOffset<R::Offset>, +} + +impl<R: Reader> DebugInfoUnitHeadersIter<R> { + /// Advance the iterator to the next unit header. + pub fn next(&mut self) -> Result<Option<UnitHeader<R>>> { + if self.input.is_empty() { + Ok(None) + } else { + let len = self.input.len(); + match parse_unit_header(&mut self.input, self.offset.into()) { + Ok(header) => { + self.offset.0 += len - self.input.len(); + Ok(Some(header)) + } + Err(e) => { + self.input.empty(); + Err(e) + } + } + } + } +} + +#[cfg(feature = "fallible-iterator")] +impl<R: Reader> fallible_iterator::FallibleIterator for DebugInfoUnitHeadersIter<R> { + type Item = UnitHeader<R>; + type Error = Error; + + fn next(&mut self) -> ::core::result::Result<Option<Self::Item>, Self::Error> { + DebugInfoUnitHeadersIter::next(self) + } +} + +/// Parse the unit type from the unit header. +fn parse_unit_type<R: Reader>(input: &mut R) -> Result<constants::DwUt> { + let val = input.read_u8()?; + Ok(constants::DwUt(val)) +} + +/// Parse the `debug_abbrev_offset` in the compilation unit header. +fn parse_debug_abbrev_offset<R: Reader>( + input: &mut R, + format: Format, +) -> Result<DebugAbbrevOffset<R::Offset>> { + input.read_offset(format).map(DebugAbbrevOffset) +} + +/// Parse the `debug_info_offset` in the arange header. +pub(crate) fn parse_debug_info_offset<R: Reader>( + input: &mut R, + format: Format, +) -> Result<DebugInfoOffset<R::Offset>> { + input.read_offset(format).map(DebugInfoOffset) +} + +/// This enum specifies the type of the unit and any type +/// specific data carried in the header (e.g. the type +/// signature/type offset of a type unit). +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum UnitType<Offset> +where + Offset: ReaderOffset, +{ + /// In DWARF5, a unit with type `DW_UT_compile`. In previous DWARF versions, + /// any unit appearing in the .debug_info section. + Compilation, + /// In DWARF5, a unit with type `DW_UT_type`. In DWARF4, any unit appearing + /// in the .debug_types section. + Type { + /// The unique type signature for this type unit. + type_signature: DebugTypeSignature, + /// The offset within this type unit where the type is defined. + type_offset: UnitOffset<Offset>, + }, + /// A unit with type `DW_UT_partial`. The root DIE of this unit should be a + /// `DW_TAG_partial_unit`. + Partial, + /// A unit with type `DW_UT_skeleton`. The enclosed dwo_id can be used to + /// link this with the corresponding `SplitCompilation` unit in a dwo file. + /// NB: The non-standard GNU split DWARF extension to DWARF 4 will instead + /// be a `Compilation` unit with the dwo_id present as an attribute on the + /// root DIE. + Skeleton(DwoId), + /// A unit with type `DW_UT_split_compile`. The enclosed dwo_id can be used to + /// link this with the corresponding `Skeleton` unit in the original binary. + /// NB: The non-standard GNU split DWARF extension to DWARF 4 will instead + /// be a `Compilation` unit with the dwo_id present as an attribute on the + /// root DIE. + SplitCompilation(DwoId), + /// A unit with type `DW_UT_split_type`. A split type unit is identical to a + /// conventional type unit except for the section in which it appears. + SplitType { + /// The unique type signature for this type unit. + type_signature: DebugTypeSignature, + /// The offset within this type unit where the type is defined. + type_offset: UnitOffset<Offset>, + }, +} + +impl<Offset> UnitType<Offset> +where + Offset: ReaderOffset, +{ + // TODO: This will be used by the DWARF writing code once it + // supports unit types other than simple compilation units. + #[allow(unused)] + pub(crate) fn dw_ut(&self) -> constants::DwUt { + match self { + UnitType::Compilation => constants::DW_UT_compile, + UnitType::Type { .. } => constants::DW_UT_type, + UnitType::Partial => constants::DW_UT_partial, + UnitType::Skeleton(_) => constants::DW_UT_skeleton, + UnitType::SplitCompilation(_) => constants::DW_UT_split_compile, + UnitType::SplitType { .. } => constants::DW_UT_split_type, + } + } +} + +/// The common fields for the headers of compilation units and +/// type units. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct UnitHeader<R, Offset = <R as Reader>::Offset> +where + R: Reader<Offset = Offset>, + Offset: ReaderOffset, +{ + encoding: Encoding, + unit_length: Offset, + unit_type: UnitType<Offset>, + debug_abbrev_offset: DebugAbbrevOffset<Offset>, + unit_offset: UnitSectionOffset<Offset>, + entries_buf: R, +} + +/// Static methods. +impl<R, Offset> UnitHeader<R, Offset> +where + R: Reader<Offset = Offset>, + Offset: ReaderOffset, +{ + /// Construct a new `UnitHeader`. + pub fn new( + encoding: Encoding, + unit_length: Offset, + unit_type: UnitType<Offset>, + debug_abbrev_offset: DebugAbbrevOffset<Offset>, + unit_offset: UnitSectionOffset<Offset>, + entries_buf: R, + ) -> Self { + UnitHeader { + encoding, + unit_length, + unit_type, + debug_abbrev_offset, + unit_offset, + entries_buf, + } + } +} + +/// Instance methods. +impl<R, Offset> UnitHeader<R, Offset> +where + R: Reader<Offset = Offset>, + Offset: ReaderOffset, +{ + /// Get the offset of this unit within its section. + pub fn offset(&self) -> UnitSectionOffset<Offset> { + self.unit_offset + } + + /// Return the serialized size of the common unit header for the given + /// DWARF format. + pub fn size_of_header(&self) -> usize { + let unit_length_size = self.encoding.format.initial_length_size() as usize; + let version_size = 2; + let debug_abbrev_offset_size = self.encoding.format.word_size() as usize; + let address_size_size = 1; + let unit_type_size = if self.encoding.version == 5 { 1 } else { 0 }; + let type_specific_size = match self.unit_type { + UnitType::Compilation | UnitType::Partial => 0, + UnitType::Type { .. } | UnitType::SplitType { .. } => { + let type_signature_size = 8; + let type_offset_size = self.encoding.format.word_size() as usize; + type_signature_size + type_offset_size + } + UnitType::Skeleton(_) | UnitType::SplitCompilation(_) => 8, + }; + + unit_length_size + + version_size + + debug_abbrev_offset_size + + address_size_size + + unit_type_size + + type_specific_size + } + + /// Get the length of the debugging info for this compilation unit, not + /// including the byte length of the encoded length itself. + pub fn unit_length(&self) -> Offset { + self.unit_length + } + + /// Get the length of the debugging info for this compilation unit, + /// including the byte length of the encoded length itself. + pub fn length_including_self(&self) -> Offset { + Offset::from_u8(self.format().initial_length_size()) + self.unit_length + } + + /// Return the encoding parameters for this unit. + pub fn encoding(&self) -> Encoding { + self.encoding + } + + /// Get the DWARF version of the debugging info for this compilation unit. + pub fn version(&self) -> u16 { + self.encoding.version + } + + /// Get the UnitType of this unit. + pub fn type_(&self) -> UnitType<Offset> { + self.unit_type + } + + /// The offset into the `.debug_abbrev` section for this compilation unit's + /// debugging information entries' abbreviations. + pub fn debug_abbrev_offset(&self) -> DebugAbbrevOffset<Offset> { + self.debug_abbrev_offset + } + + /// The size of addresses (in bytes) in this compilation unit. + pub fn address_size(&self) -> u8 { + self.encoding.address_size + } + + /// Whether this compilation unit is encoded in 64- or 32-bit DWARF. + pub fn format(&self) -> Format { + self.encoding.format + } + + /// The serialized size of the header for this compilation unit. + pub fn header_size(&self) -> Offset { + self.length_including_self() - self.entries_buf.len() + } + + pub(crate) fn is_valid_offset(&self, offset: UnitOffset<Offset>) -> bool { + let size_of_header = self.header_size(); + if offset.0 < size_of_header { + return false; + } + + let relative_to_entries_buf = offset.0 - size_of_header; + relative_to_entries_buf < self.entries_buf.len() + } + + /// Get the underlying bytes for the supplied range. + pub fn range(&self, idx: Range<UnitOffset<Offset>>) -> Result<R> { + if !self.is_valid_offset(idx.start) { + return Err(Error::OffsetOutOfBounds); + } + if !self.is_valid_offset(idx.end) { + return Err(Error::OffsetOutOfBounds); + } + assert!(idx.start <= idx.end); + let size_of_header = self.header_size(); + let start = idx.start.0 - size_of_header; + let end = idx.end.0 - size_of_header; + let mut input = self.entries_buf.clone(); + input.skip(start)?; + input.truncate(end - start)?; + Ok(input) + } + + /// Get the underlying bytes for the supplied range. + pub fn range_from(&self, idx: RangeFrom<UnitOffset<Offset>>) -> Result<R> { + if !self.is_valid_offset(idx.start) { + return Err(Error::OffsetOutOfBounds); + } + let start = idx.start.0 - self.header_size(); + let mut input = self.entries_buf.clone(); + input.skip(start)?; + Ok(input) + } + + /// Get the underlying bytes for the supplied range. + pub fn range_to(&self, idx: RangeTo<UnitOffset<Offset>>) -> Result<R> { + if !self.is_valid_offset(idx.end) { + return Err(Error::OffsetOutOfBounds); + } + let end = idx.end.0 - self.header_size(); + let mut input = self.entries_buf.clone(); + input.truncate(end)?; + Ok(input) + } + + /// Read the `DebuggingInformationEntry` at the given offset. + pub fn entry<'me, 'abbrev>( + &'me self, + abbreviations: &'abbrev Abbreviations, + offset: UnitOffset<Offset>, + ) -> Result<DebuggingInformationEntry<'abbrev, 'me, R>> { + let mut input = self.range_from(offset..)?; + let entry = DebuggingInformationEntry::parse(&mut input, self, abbreviations)?; + entry.ok_or(Error::NoEntryAtGivenOffset) + } + + /// Navigate this unit's `DebuggingInformationEntry`s. + pub fn entries<'me, 'abbrev>( + &'me self, + abbreviations: &'abbrev Abbreviations, + ) -> EntriesCursor<'abbrev, 'me, R> { + EntriesCursor { + unit: self, + input: self.entries_buf.clone(), + abbreviations, + cached_current: None, + delta_depth: 0, + } + } + + /// Navigate this compilation unit's `DebuggingInformationEntry`s + /// starting at the given offset. + pub fn entries_at_offset<'me, 'abbrev>( + &'me self, + abbreviations: &'abbrev Abbreviations, + offset: UnitOffset<Offset>, + ) -> Result<EntriesCursor<'abbrev, 'me, R>> { + let input = self.range_from(offset..)?; + Ok(EntriesCursor { + unit: self, + input, + abbreviations, + cached_current: None, + delta_depth: 0, + }) + } + + /// Navigate this unit's `DebuggingInformationEntry`s as a tree + /// starting at the given offset. + pub fn entries_tree<'me, 'abbrev>( + &'me self, + abbreviations: &'abbrev Abbreviations, + offset: Option<UnitOffset<Offset>>, + ) -> Result<EntriesTree<'abbrev, 'me, R>> { + let input = match offset { + Some(offset) => self.range_from(offset..)?, + None => self.entries_buf.clone(), + }; + Ok(EntriesTree::new(input, self, abbreviations)) + } + + /// Read the raw data that defines the Debugging Information Entries. + pub fn entries_raw<'me, 'abbrev>( + &'me self, + abbreviations: &'abbrev Abbreviations, + offset: Option<UnitOffset<Offset>>, + ) -> Result<EntriesRaw<'abbrev, 'me, R>> { + let input = match offset { + Some(offset) => self.range_from(offset..)?, + None => self.entries_buf.clone(), + }; + Ok(EntriesRaw { + input, + unit: self, + abbreviations, + depth: 0, + }) + } + + /// Parse this unit's abbreviations. + pub fn abbreviations(&self, debug_abbrev: &DebugAbbrev<R>) -> Result<Abbreviations> { + debug_abbrev.abbreviations(self.debug_abbrev_offset()) + } +} + +/// Parse a unit header. +fn parse_unit_header<R, Offset>( + input: &mut R, + unit_offset: UnitSectionOffset<Offset>, +) -> Result<UnitHeader<R>> +where + R: Reader<Offset = Offset>, + Offset: ReaderOffset, +{ + let (unit_length, format) = input.read_initial_length()?; + let mut rest = input.split(unit_length)?; + + let version = rest.read_u16()?; + let abbrev_offset; + let address_size; + let unit_type; + // DWARF 1 was very different, and is obsolete, so isn't supported by this + // reader. + if 2 <= version && version <= 4 { + abbrev_offset = parse_debug_abbrev_offset(&mut rest, format)?; + address_size = rest.read_u8()?; + // Before DWARF5, all units in the .debug_info section are compilation + // units, and all units in the .debug_types section are type units. + unit_type = match unit_offset { + UnitSectionOffset::DebugInfoOffset(_) => constants::DW_UT_compile, + UnitSectionOffset::DebugTypesOffset(_) => constants::DW_UT_type, + }; + } else if version == 5 { + unit_type = parse_unit_type(&mut rest)?; + address_size = rest.read_u8()?; + abbrev_offset = parse_debug_abbrev_offset(&mut rest, format)?; + } else { + return Err(Error::UnknownVersion(u64::from(version))); + } + let encoding = Encoding { + format, + version, + address_size, + }; + + // Parse any data specific to this type of unit. + let unit_type = match unit_type { + constants::DW_UT_compile => UnitType::Compilation, + constants::DW_UT_type => { + let type_signature = parse_type_signature(&mut rest)?; + let type_offset = parse_type_offset(&mut rest, format)?; + UnitType::Type { + type_signature, + type_offset, + } + } + constants::DW_UT_partial => UnitType::Partial, + constants::DW_UT_skeleton => { + let dwo_id = parse_dwo_id(&mut rest)?; + UnitType::Skeleton(dwo_id) + } + constants::DW_UT_split_compile => { + let dwo_id = parse_dwo_id(&mut rest)?; + UnitType::SplitCompilation(dwo_id) + } + constants::DW_UT_split_type => { + let type_signature = parse_type_signature(&mut rest)?; + let type_offset = parse_type_offset(&mut rest, format)?; + UnitType::SplitType { + type_signature, + type_offset, + } + } + _ => return Err(Error::UnsupportedUnitType), + }; + + Ok(UnitHeader::new( + encoding, + unit_length, + unit_type, + abbrev_offset, + unit_offset, + rest, + )) +} + +/// Parse a dwo_id from a header +fn parse_dwo_id<R: Reader>(input: &mut R) -> Result<DwoId> { + Ok(DwoId(input.read_u64()?)) +} + +/// A Debugging Information Entry (DIE). +/// +/// DIEs have a set of attributes and optionally have children DIEs as well. +#[derive(Clone, Debug)] +pub struct DebuggingInformationEntry<'abbrev, 'unit, R, Offset = <R as Reader>::Offset> +where + R: Reader<Offset = Offset>, + Offset: ReaderOffset, +{ + offset: UnitOffset<Offset>, + attrs_slice: R, + attrs_len: Cell<Option<Offset>>, + abbrev: &'abbrev Abbreviation, + unit: &'unit UnitHeader<R, Offset>, +} + +impl<'abbrev, 'unit, R, Offset> DebuggingInformationEntry<'abbrev, 'unit, R, Offset> +where + R: Reader<Offset = Offset>, + Offset: ReaderOffset, +{ + /// Construct a new `DebuggingInformationEntry`. + pub fn new( + offset: UnitOffset<Offset>, + attrs_slice: R, + abbrev: &'abbrev Abbreviation, + unit: &'unit UnitHeader<R, Offset>, + ) -> Self { + DebuggingInformationEntry { + offset, + attrs_slice, + attrs_len: Cell::new(None), + abbrev, + unit, + } + } + + /// Get this entry's code. + pub fn code(&self) -> u64 { + self.abbrev.code() + } + + /// Get this entry's offset. + pub fn offset(&self) -> UnitOffset<Offset> { + self.offset + } + + /// Get this entry's `DW_TAG_whatever` tag. + /// + /// ``` + /// # use gimli::{DebugAbbrev, DebugInfo, LittleEndian}; + /// # let info_buf = [ + /// # // Comilation unit header + /// # + /// # // 32-bit unit length = 12 + /// # 0x0c, 0x00, 0x00, 0x00, + /// # // Version 4 + /// # 0x04, 0x00, + /// # // debug_abbrev_offset + /// # 0x00, 0x00, 0x00, 0x00, + /// # // Address size + /// # 0x04, + /// # + /// # // DIEs + /// # + /// # // Abbreviation code + /// # 0x01, + /// # // Attribute of form DW_FORM_string = "foo\0" + /// # 0x66, 0x6f, 0x6f, 0x00, + /// # ]; + /// # let debug_info = DebugInfo::new(&info_buf, LittleEndian); + /// # let abbrev_buf = [ + /// # // Code + /// # 0x01, + /// # // DW_TAG_subprogram + /// # 0x2e, + /// # // DW_CHILDREN_no + /// # 0x00, + /// # // Begin attributes + /// # // Attribute name = DW_AT_name + /// # 0x03, + /// # // Attribute form = DW_FORM_string + /// # 0x08, + /// # // End attributes + /// # 0x00, + /// # 0x00, + /// # // Null terminator + /// # 0x00 + /// # ]; + /// # let debug_abbrev = DebugAbbrev::new(&abbrev_buf, LittleEndian); + /// # let unit = debug_info.units().next().unwrap().unwrap(); + /// # let abbrevs = unit.abbreviations(&debug_abbrev).unwrap(); + /// # let mut cursor = unit.entries(&abbrevs); + /// # let (_, entry) = cursor.next_dfs().unwrap().unwrap(); + /// # let mut get_some_entry = || entry; + /// let entry = get_some_entry(); + /// + /// match entry.tag() { + /// gimli::DW_TAG_subprogram => + /// println!("this entry contains debug info about a function"), + /// gimli::DW_TAG_inlined_subroutine => + /// println!("this entry contains debug info about a particular instance of inlining"), + /// gimli::DW_TAG_variable => + /// println!("this entry contains debug info about a local variable"), + /// gimli::DW_TAG_formal_parameter => + /// println!("this entry contains debug info about a function parameter"), + /// otherwise => + /// println!("this entry is some other kind of data: {:?}", otherwise), + /// }; + /// ``` + pub fn tag(&self) -> constants::DwTag { + self.abbrev.tag() + } + + /// Return true if this entry's type can have children, false otherwise. + pub fn has_children(&self) -> bool { + self.abbrev.has_children() + } + + /// Iterate over this entry's set of attributes. + /// + /// ``` + /// use gimli::{DebugAbbrev, DebugInfo, LittleEndian}; + /// + /// // Read the `.debug_info` section. + /// + /// # let info_buf = [ + /// # // Comilation unit header + /// # + /// # // 32-bit unit length = 12 + /// # 0x0c, 0x00, 0x00, 0x00, + /// # // Version 4 + /// # 0x04, 0x00, + /// # // debug_abbrev_offset + /// # 0x00, 0x00, 0x00, 0x00, + /// # // Address size + /// # 0x04, + /// # + /// # // DIEs + /// # + /// # // Abbreviation code + /// # 0x01, + /// # // Attribute of form DW_FORM_string = "foo\0" + /// # 0x66, 0x6f, 0x6f, 0x00, + /// # ]; + /// # let read_debug_info_section_somehow = || &info_buf; + /// let debug_info = DebugInfo::new(read_debug_info_section_somehow(), LittleEndian); + /// + /// // Get the data about the first compilation unit out of the `.debug_info`. + /// + /// let unit = debug_info.units().next() + /// .expect("Should have at least one compilation unit") + /// .expect("and it should parse ok"); + /// + /// // Read the `.debug_abbrev` section and parse the + /// // abbreviations for our compilation unit. + /// + /// # let abbrev_buf = [ + /// # // Code + /// # 0x01, + /// # // DW_TAG_subprogram + /// # 0x2e, + /// # // DW_CHILDREN_no + /// # 0x00, + /// # // Begin attributes + /// # // Attribute name = DW_AT_name + /// # 0x03, + /// # // Attribute form = DW_FORM_string + /// # 0x08, + /// # // End attributes + /// # 0x00, + /// # 0x00, + /// # // Null terminator + /// # 0x00 + /// # ]; + /// # let read_debug_abbrev_section_somehow = || &abbrev_buf; + /// let debug_abbrev = DebugAbbrev::new(read_debug_abbrev_section_somehow(), LittleEndian); + /// let abbrevs = unit.abbreviations(&debug_abbrev).unwrap(); + /// + /// // Get the first entry from that compilation unit. + /// + /// let mut cursor = unit.entries(&abbrevs); + /// let (_, entry) = cursor.next_dfs() + /// .expect("Should parse next entry") + /// .expect("Should have at least one entry"); + /// + /// // Finally, print the first entry's attributes. + /// + /// let mut attrs = entry.attrs(); + /// while let Some(attr) = attrs.next().unwrap() { + /// println!("Attribute name = {:?}", attr.name()); + /// println!("Attribute value = {:?}", attr.value()); + /// } + /// ``` + /// + /// Can be [used with + /// `FallibleIterator`](./index.html#using-with-fallibleiterator). + pub fn attrs<'me>(&'me self) -> AttrsIter<'abbrev, 'me, 'unit, R> { + AttrsIter { + input: self.attrs_slice.clone(), + attributes: self.abbrev.attributes(), + entry: self, + } + } + + /// Find the first attribute in this entry which has the given name, + /// and return it. Returns `Ok(None)` if no attribute is found. + pub fn attr(&self, name: constants::DwAt) -> Result<Option<Attribute<R>>> { + let mut attrs = self.attrs(); + while let Some(attr) = attrs.next()? { + if attr.name() == name { + return Ok(Some(attr)); + } + } + Ok(None) + } + + /// Find the first attribute in this entry which has the given name, + /// and return its raw value. Returns `Ok(None)` if no attribute is found. + pub fn attr_value_raw(&self, name: constants::DwAt) -> Result<Option<AttributeValue<R>>> { + self.attr(name) + .map(|attr| attr.map(|attr| attr.raw_value())) + } + + /// Find the first attribute in this entry which has the given name, + /// and return its normalized value. Returns `Ok(None)` if no + /// attribute is found. + pub fn attr_value(&self, name: constants::DwAt) -> Result<Option<AttributeValue<R>>> { + self.attr(name).map(|attr| attr.map(|attr| attr.value())) + } + + /// Return the input buffer after the last attribute. + #[inline(always)] + fn after_attrs(&self) -> Result<R> { + if let Some(attrs_len) = self.attrs_len.get() { + let mut input = self.attrs_slice.clone(); + input.skip(attrs_len)?; + Ok(input) + } else { + let mut attrs = self.attrs(); + while attrs.next()?.is_some() {} + Ok(attrs.input) + } + } + + /// Use the `DW_AT_sibling` attribute to find the input buffer for the + /// next sibling. Returns `None` if the attribute is missing or invalid. + fn sibling(&self) -> Option<R> { + let attr = self.attr_value(constants::DW_AT_sibling); + if let Ok(Some(AttributeValue::UnitRef(offset))) = attr { + if offset.0 > self.offset.0 { + if let Ok(input) = self.unit.range_from(offset..) { + return Some(input); + } + } + } + None + } + + /// Parse an entry. Returns `Ok(None)` for null entries. + #[inline(always)] + fn parse( + input: &mut R, + unit: &'unit UnitHeader<R>, + abbreviations: &'abbrev Abbreviations, + ) -> Result<Option<Self>> { + let offset = unit.header_size() + input.offset_from(&unit.entries_buf); + let code = input.read_uleb128()?; + if code == 0 { + return Ok(None); + }; + let abbrev = abbreviations.get(code).ok_or(Error::UnknownAbbreviation)?; + Ok(Some(DebuggingInformationEntry { + offset: UnitOffset(offset), + attrs_slice: input.clone(), + attrs_len: Cell::new(None), + abbrev, + unit, + })) + } +} + +/// The value of an attribute in a `DebuggingInformationEntry`. +// +// Set the discriminant size so that all variants use the same alignment +// for their data. This gives better code generation in `parse_attribute`. +#[repr(u64)] +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum AttributeValue<R, Offset = <R as Reader>::Offset> +where + R: Reader<Offset = Offset>, + Offset: ReaderOffset, +{ + /// "Refers to some location in the address space of the described program." + Addr(u64), + + /// A slice of an arbitrary number of bytes. + Block(R), + + /// A one byte constant data value. How to interpret the byte depends on context. + /// + /// From section 7 of the standard: "Depending on context, it may be a + /// signed integer, an unsigned integer, a floating-point constant, or + /// anything else." + Data1(u8), + + /// A two byte constant data value. How to interpret the bytes depends on context. + /// + /// These bytes have been converted from `R::Endian`. This may need to be reversed + /// if this was not required. + /// + /// From section 7 of the standard: "Depending on context, it may be a + /// signed integer, an unsigned integer, a floating-point constant, or + /// anything else." + Data2(u16), + + /// A four byte constant data value. How to interpret the bytes depends on context. + /// + /// These bytes have been converted from `R::Endian`. This may need to be reversed + /// if this was not required. + /// + /// From section 7 of the standard: "Depending on context, it may be a + /// signed integer, an unsigned integer, a floating-point constant, or + /// anything else." + Data4(u32), + + /// An eight byte constant data value. How to interpret the bytes depends on context. + /// + /// These bytes have been converted from `R::Endian`. This may need to be reversed + /// if this was not required. + /// + /// From section 7 of the standard: "Depending on context, it may be a + /// signed integer, an unsigned integer, a floating-point constant, or + /// anything else." + Data8(u64), + + /// A signed integer constant. + Sdata(i64), + + /// An unsigned integer constant. + Udata(u64), + + /// "The information bytes contain a DWARF expression (see Section 2.5) or + /// location description (see Section 2.6)." + Exprloc(Expression<R>), + + /// A boolean that indicates presence or absence of the attribute. + Flag(bool), + + /// An offset into another section. Which section this is an offset into + /// depends on context. + SecOffset(Offset), + + /// An offset to a set of addresses in the `.debug_addr` section. + DebugAddrBase(DebugAddrBase<Offset>), + + /// An index into a set of addresses in the `.debug_addr` section. + DebugAddrIndex(DebugAddrIndex<Offset>), + + /// An offset into the current compilation unit. + UnitRef(UnitOffset<Offset>), + + /// An offset into the current `.debug_info` section, but possibly a + /// different compilation unit from the current one. + DebugInfoRef(DebugInfoOffset<Offset>), + + /// An offset into the `.debug_info` section of the supplementary object file. + DebugInfoRefSup(DebugInfoOffset<Offset>), + + /// An offset into the `.debug_line` section. + DebugLineRef(DebugLineOffset<Offset>), + + /// An offset into either the `.debug_loc` section or the `.debug_loclists` section. + LocationListsRef(LocationListsOffset<Offset>), + + /// An offset to a set of offsets in the `.debug_loclists` section. + DebugLocListsBase(DebugLocListsBase<Offset>), + + /// An index into a set of offsets in the `.debug_loclists` section. + DebugLocListsIndex(DebugLocListsIndex<Offset>), + + /// An offset into the `.debug_macinfo` section. + DebugMacinfoRef(DebugMacinfoOffset<Offset>), + + /// An offset into the `.debug_macro` section. + DebugMacroRef(DebugMacroOffset<Offset>), + + /// An offset into the `.debug_ranges` section. + RangeListsRef(RawRangeListsOffset<Offset>), + + /// An offset to a set of offsets in the `.debug_rnglists` section. + DebugRngListsBase(DebugRngListsBase<Offset>), + + /// An index into a set of offsets in the `.debug_rnglists` section. + DebugRngListsIndex(DebugRngListsIndex<Offset>), + + /// A type signature. + DebugTypesRef(DebugTypeSignature), + + /// An offset into the `.debug_str` section. + DebugStrRef(DebugStrOffset<Offset>), + + /// An offset into the `.debug_str` section of the supplementary object file. + DebugStrRefSup(DebugStrOffset<Offset>), + + /// An offset to a set of entries in the `.debug_str_offsets` section. + DebugStrOffsetsBase(DebugStrOffsetsBase<Offset>), + + /// An index into a set of entries in the `.debug_str_offsets` section. + DebugStrOffsetsIndex(DebugStrOffsetsIndex<Offset>), + + /// An offset into the `.debug_line_str` section. + DebugLineStrRef(DebugLineStrOffset<Offset>), + + /// A slice of bytes representing a string. Does not include a final null byte. + /// Not guaranteed to be UTF-8 or anything like that. + String(R), + + /// The value of a `DW_AT_encoding` attribute. + Encoding(constants::DwAte), + + /// The value of a `DW_AT_decimal_sign` attribute. + DecimalSign(constants::DwDs), + + /// The value of a `DW_AT_endianity` attribute. + Endianity(constants::DwEnd), + + /// The value of a `DW_AT_accessibility` attribute. + Accessibility(constants::DwAccess), + + /// The value of a `DW_AT_visibility` attribute. + Visibility(constants::DwVis), + + /// The value of a `DW_AT_virtuality` attribute. + Virtuality(constants::DwVirtuality), + + /// The value of a `DW_AT_language` attribute. + Language(constants::DwLang), + + /// The value of a `DW_AT_address_class` attribute. + AddressClass(constants::DwAddr), + + /// The value of a `DW_AT_identifier_case` attribute. + IdentifierCase(constants::DwId), + + /// The value of a `DW_AT_calling_convention` attribute. + CallingConvention(constants::DwCc), + + /// The value of a `DW_AT_inline` attribute. + Inline(constants::DwInl), + + /// The value of a `DW_AT_ordering` attribute. + Ordering(constants::DwOrd), + + /// An index into the filename entries from the line number information + /// table for the compilation unit containing this value. + FileIndex(u64), + + /// An implementation-defined identifier uniquely identifying a compilation + /// unit. + DwoId(DwoId), +} + +/// An attribute in a `DebuggingInformationEntry`, consisting of a name and +/// associated value. +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub struct Attribute<R: Reader> { + name: constants::DwAt, + value: AttributeValue<R>, +} + +impl<R: Reader> Attribute<R> { + /// Get this attribute's name. + pub fn name(&self) -> constants::DwAt { + self.name + } + + /// Get this attribute's raw value. + pub fn raw_value(&self) -> AttributeValue<R> { + self.value.clone() + } + + /// Get this attribute's normalized value. + /// + /// Attribute values can potentially be encoded in multiple equivalent forms, + /// and may have special meaning depending on the attribute name. This method + /// converts the attribute value to a normalized form based on the attribute + /// name. + /// + /// See "Table 7.5: Attribute encodings" and "Table 7.6: Attribute form encodings". + pub fn value(&self) -> AttributeValue<R> { + // Table 7.5 shows the possible attribute classes for each name. + // Table 7.6 shows the possible attribute classes for each form. + // For each attribute name, we need to match on the form, and + // convert it to one of the classes that is allowed for both + // the name and the form. + // + // The individual class conversions rarely vary for each name, + // so for each class conversion we define a macro that matches + // on the allowed forms for that class. + // + // For some classes, we don't need to do any conversion, so their + // macro is empty. In the future we may want to fill them in to + // provide strict checking of the forms for each class. For now, + // they simply provide a way to document the allowed classes for + // each name. + + // DW_FORM_addr + // DW_FORM_addrx + // DW_FORM_addrx1 + // DW_FORM_addrx2 + // DW_FORM_addrx3 + // DW_FORM_addrx4 + macro_rules! address { + () => {}; + } + // DW_FORM_sec_offset + macro_rules! addrptr { + () => { + if let Some(offset) = self.offset_value() { + return AttributeValue::DebugAddrBase(DebugAddrBase(offset)); + } + }; + } + // DW_FORM_block + // DW_FORM_block1 + // DW_FORM_block2 + // DW_FORM_block4 + macro_rules! block { + () => {}; + } + // DW_FORM_sdata + // DW_FORM_udata + // DW_FORM_data1 + // DW_FORM_data2 + // DW_FORM_data4 + // DW_FORM_data8 + // DW_FORM_data16 + // DW_FORM_implicit_const + macro_rules! constant { + ($value:ident, $variant:ident) => { + if let Some(value) = self.$value() { + return AttributeValue::$variant(value); + } + }; + ($value:ident, $variant:ident, $constant:ident) => { + if let Some(value) = self.$value() { + return AttributeValue::$variant(constants::$constant(value)); + } + }; + } + // DW_FORM_exprloc + macro_rules! exprloc { + () => { + if let Some(value) = self.exprloc_value() { + return AttributeValue::Exprloc(value); + } + }; + } + // DW_FORM_flag + // DW_FORM_flag_present + macro_rules! flag { + () => {}; + } + // DW_FORM_sec_offset + macro_rules! lineptr { + () => { + if let Some(offset) = self.offset_value() { + return AttributeValue::DebugLineRef(DebugLineOffset(offset)); + } + }; + } + // This also covers `loclist` in DWARF version 5. + // DW_FORM_sec_offset + // DW_FORM_loclistx + macro_rules! loclistptr { + () => { + // DebugLocListsIndex is also an allowed form in DWARF version 5. + if let Some(offset) = self.offset_value() { + return AttributeValue::LocationListsRef(LocationListsOffset(offset)); + } + }; + } + // DW_FORM_sec_offset + macro_rules! loclistsptr { + () => { + if let Some(offset) = self.offset_value() { + return AttributeValue::DebugLocListsBase(DebugLocListsBase(offset)); + } + }; + } + // DWARF version <= 4. + // DW_FORM_sec_offset + macro_rules! macinfoptr { + () => { + if let Some(offset) = self.offset_value() { + return AttributeValue::DebugMacinfoRef(DebugMacinfoOffset(offset)); + } + }; + } + // DWARF version >= 5. + // DW_FORM_sec_offset + macro_rules! macroptr { + () => { + if let Some(offset) = self.offset_value() { + return AttributeValue::DebugMacroRef(DebugMacroOffset(offset)); + } + }; + } + // DW_FORM_ref_addr + // DW_FORM_ref1 + // DW_FORM_ref2 + // DW_FORM_ref4 + // DW_FORM_ref8 + // DW_FORM_ref_udata + // DW_FORM_ref_sig8 + // DW_FORM_ref_sup4 + // DW_FORM_ref_sup8 + macro_rules! reference { + () => {}; + } + // This also covers `rnglist` in DWARF version 5. + // DW_FORM_sec_offset + // DW_FORM_rnglistx + macro_rules! rangelistptr { + () => { + // DebugRngListsIndex is also an allowed form in DWARF version 5. + if let Some(offset) = self.offset_value() { + return AttributeValue::RangeListsRef(RawRangeListsOffset(offset)); + } + }; + } + // DW_FORM_sec_offset + macro_rules! rnglistsptr { + () => { + if let Some(offset) = self.offset_value() { + return AttributeValue::DebugRngListsBase(DebugRngListsBase(offset)); + } + }; + } + // DW_FORM_string + // DW_FORM_strp + // DW_FORM_strx + // DW_FORM_strx1 + // DW_FORM_strx2 + // DW_FORM_strx3 + // DW_FORM_strx4 + // DW_FORM_strp_sup + // DW_FORM_line_strp + macro_rules! string { + () => {}; + } + // DW_FORM_sec_offset + macro_rules! stroffsetsptr { + () => { + if let Some(offset) = self.offset_value() { + return AttributeValue::DebugStrOffsetsBase(DebugStrOffsetsBase(offset)); + } + }; + } + // This isn't a separate form but it's useful to distinguish it from a generic udata. + macro_rules! dwoid { + () => { + if let Some(value) = self.udata_value() { + return AttributeValue::DwoId(DwoId(value)); + } + }; + } + + // Perform the allowed class conversions for each attribute name. + match self.name { + constants::DW_AT_sibling => { + reference!(); + } + constants::DW_AT_location => { + exprloc!(); + loclistptr!(); + } + constants::DW_AT_name => { + string!(); + } + constants::DW_AT_ordering => { + constant!(u8_value, Ordering, DwOrd); + } + constants::DW_AT_byte_size + | constants::DW_AT_bit_offset + | constants::DW_AT_bit_size => { + constant!(udata_value, Udata); + exprloc!(); + reference!(); + } + constants::DW_AT_stmt_list => { + lineptr!(); + } + constants::DW_AT_low_pc => { + address!(); + } + constants::DW_AT_high_pc => { + address!(); + constant!(udata_value, Udata); + } + constants::DW_AT_language => { + constant!(u16_value, Language, DwLang); + } + constants::DW_AT_discr => { + reference!(); + } + constants::DW_AT_discr_value => { + // constant: depends on type of DW_TAG_variant_part, + // so caller must normalize. + } + constants::DW_AT_visibility => { + constant!(u8_value, Visibility, DwVis); + } + constants::DW_AT_import => { + reference!(); + } + constants::DW_AT_string_length => { + exprloc!(); + loclistptr!(); + reference!(); + } + constants::DW_AT_common_reference => { + reference!(); + } + constants::DW_AT_comp_dir => { + string!(); + } + constants::DW_AT_const_value => { + // TODO: constant: sign depends on DW_AT_type. + block!(); + string!(); + } + constants::DW_AT_containing_type => { + reference!(); + } + constants::DW_AT_default_value => { + // TODO: constant: sign depends on DW_AT_type. + reference!(); + flag!(); + } + constants::DW_AT_inline => { + constant!(u8_value, Inline, DwInl); + } + constants::DW_AT_is_optional => { + flag!(); + } + constants::DW_AT_lower_bound => { + // TODO: constant: sign depends on DW_AT_type. + exprloc!(); + reference!(); + } + constants::DW_AT_producer => { + string!(); + } + constants::DW_AT_prototyped => { + flag!(); + } + constants::DW_AT_return_addr => { + exprloc!(); + loclistptr!(); + } + constants::DW_AT_start_scope => { + // TODO: constant + rangelistptr!(); + } + constants::DW_AT_bit_stride => { + constant!(udata_value, Udata); + exprloc!(); + reference!(); + } + constants::DW_AT_upper_bound => { + // TODO: constant: sign depends on DW_AT_type. + exprloc!(); + reference!(); + } + constants::DW_AT_abstract_origin => { + reference!(); + } + constants::DW_AT_accessibility => { + constant!(u8_value, Accessibility, DwAccess); + } + constants::DW_AT_address_class => { + constant!(udata_value, AddressClass, DwAddr); + } + constants::DW_AT_artificial => { + flag!(); + } + constants::DW_AT_base_types => { + reference!(); + } + constants::DW_AT_calling_convention => { + constant!(u8_value, CallingConvention, DwCc); + } + constants::DW_AT_count => { + // TODO: constant + exprloc!(); + reference!(); + } + constants::DW_AT_data_member_location => { + // Constants must be handled before loclistptr so that DW_FORM_data4/8 + // are correctly interpreted for DWARF version 4+. + constant!(udata_value, Udata); + exprloc!(); + loclistptr!(); + } + constants::DW_AT_decl_column => { + constant!(udata_value, Udata); + } + constants::DW_AT_decl_file => { + constant!(udata_value, FileIndex); + } + constants::DW_AT_decl_line => { + constant!(udata_value, Udata); + } + constants::DW_AT_declaration => { + flag!(); + } + constants::DW_AT_discr_list => { + block!(); + } + constants::DW_AT_encoding => { + constant!(u8_value, Encoding, DwAte); + } + constants::DW_AT_external => { + flag!(); + } + constants::DW_AT_frame_base => { + exprloc!(); + loclistptr!(); + } + constants::DW_AT_friend => { + reference!(); + } + constants::DW_AT_identifier_case => { + constant!(u8_value, IdentifierCase, DwId); + } + constants::DW_AT_macro_info => { + macinfoptr!(); + } + constants::DW_AT_namelist_item => { + reference!(); + } + constants::DW_AT_priority => { + reference!(); + } + constants::DW_AT_segment => { + exprloc!(); + loclistptr!(); + } + constants::DW_AT_specification => { + reference!(); + } + constants::DW_AT_static_link => { + exprloc!(); + loclistptr!(); + } + constants::DW_AT_type => { + reference!(); + } + constants::DW_AT_use_location => { + exprloc!(); + loclistptr!(); + } + constants::DW_AT_variable_parameter => { + flag!(); + } + constants::DW_AT_virtuality => { + constant!(u8_value, Virtuality, DwVirtuality); + } + constants::DW_AT_vtable_elem_location => { + exprloc!(); + loclistptr!(); + } + constants::DW_AT_allocated => { + // TODO: constant + exprloc!(); + reference!(); + } + constants::DW_AT_associated => { + // TODO: constant + exprloc!(); + reference!(); + } + constants::DW_AT_data_location => { + exprloc!(); + } + constants::DW_AT_byte_stride => { + constant!(udata_value, Udata); + exprloc!(); + reference!(); + } + constants::DW_AT_entry_pc => { + // TODO: constant + address!(); + } + constants::DW_AT_use_UTF8 => { + flag!(); + } + constants::DW_AT_extension => { + reference!(); + } + constants::DW_AT_ranges => { + rangelistptr!(); + } + constants::DW_AT_trampoline => { + address!(); + flag!(); + reference!(); + string!(); + } + constants::DW_AT_call_column => { + constant!(udata_value, Udata); + } + constants::DW_AT_call_file => { + constant!(udata_value, FileIndex); + } + constants::DW_AT_call_line => { + constant!(udata_value, Udata); + } + constants::DW_AT_description => { + string!(); + } + constants::DW_AT_binary_scale => { + // TODO: constant + } + constants::DW_AT_decimal_scale => { + // TODO: constant + } + constants::DW_AT_small => { + reference!(); + } + constants::DW_AT_decimal_sign => { + constant!(u8_value, DecimalSign, DwDs); + } + constants::DW_AT_digit_count => { + // TODO: constant + } + constants::DW_AT_picture_string => { + string!(); + } + constants::DW_AT_mutable => { + flag!(); + } + constants::DW_AT_threads_scaled => { + flag!(); + } + constants::DW_AT_explicit => { + flag!(); + } + constants::DW_AT_object_pointer => { + reference!(); + } + constants::DW_AT_endianity => { + constant!(u8_value, Endianity, DwEnd); + } + constants::DW_AT_elemental => { + flag!(); + } + constants::DW_AT_pure => { + flag!(); + } + constants::DW_AT_recursive => { + flag!(); + } + constants::DW_AT_signature => { + reference!(); + } + constants::DW_AT_main_subprogram => { + flag!(); + } + constants::DW_AT_data_bit_offset => { + // TODO: constant + } + constants::DW_AT_const_expr => { + flag!(); + } + constants::DW_AT_enum_class => { + flag!(); + } + constants::DW_AT_linkage_name => { + string!(); + } + constants::DW_AT_string_length_bit_size => { + // TODO: constant + } + constants::DW_AT_string_length_byte_size => { + // TODO: constant + } + constants::DW_AT_rank => { + // TODO: constant + exprloc!(); + } + constants::DW_AT_str_offsets_base => { + stroffsetsptr!(); + } + constants::DW_AT_addr_base | constants::DW_AT_GNU_addr_base => { + addrptr!(); + } + constants::DW_AT_rnglists_base | constants::DW_AT_GNU_ranges_base => { + rnglistsptr!(); + } + constants::DW_AT_dwo_name => { + string!(); + } + constants::DW_AT_reference => { + flag!(); + } + constants::DW_AT_rvalue_reference => { + flag!(); + } + constants::DW_AT_macros => { + macroptr!(); + } + constants::DW_AT_call_all_calls => { + flag!(); + } + constants::DW_AT_call_all_source_calls => { + flag!(); + } + constants::DW_AT_call_all_tail_calls => { + flag!(); + } + constants::DW_AT_call_return_pc => { + address!(); + } + constants::DW_AT_call_value => { + exprloc!(); + } + constants::DW_AT_call_origin => { + exprloc!(); + } + constants::DW_AT_call_parameter => { + reference!(); + } + constants::DW_AT_call_pc => { + address!(); + } + constants::DW_AT_call_tail_call => { + flag!(); + } + constants::DW_AT_call_target => { + exprloc!(); + } + constants::DW_AT_call_target_clobbered => { + exprloc!(); + } + constants::DW_AT_call_data_location => { + exprloc!(); + } + constants::DW_AT_call_data_value => { + exprloc!(); + } + constants::DW_AT_noreturn => { + flag!(); + } + constants::DW_AT_alignment => { + // TODO: constant + } + constants::DW_AT_export_symbols => { + flag!(); + } + constants::DW_AT_deleted => { + flag!(); + } + constants::DW_AT_defaulted => { + // TODO: constant + } + constants::DW_AT_loclists_base => { + loclistsptr!(); + } + constants::DW_AT_GNU_dwo_id => { + dwoid!(); + } + _ => {} + } + self.value.clone() + } + + /// Try to convert this attribute's value to a u8. + #[inline] + pub fn u8_value(&self) -> Option<u8> { + self.value.u8_value() + } + + /// Try to convert this attribute's value to a u16. + #[inline] + pub fn u16_value(&self) -> Option<u16> { + self.value.u16_value() + } + + /// Try to convert this attribute's value to an unsigned integer. + #[inline] + pub fn udata_value(&self) -> Option<u64> { + self.value.udata_value() + } + + /// Try to convert this attribute's value to a signed integer. + #[inline] + pub fn sdata_value(&self) -> Option<i64> { + self.value.sdata_value() + } + + /// Try to convert this attribute's value to an offset. + #[inline] + pub fn offset_value(&self) -> Option<R::Offset> { + self.value.offset_value() + } + + /// Try to convert this attribute's value to an expression or location buffer. + /// + /// Expressions and locations may be `DW_FORM_block*` or `DW_FORM_exprloc`. + /// The standard doesn't mention `DW_FORM_block*` as a possible form, but + /// it is encountered in practice. + #[inline] + pub fn exprloc_value(&self) -> Option<Expression<R>> { + self.value.exprloc_value() + } + + /// Try to return this attribute's value as a string slice. + /// + /// If this attribute's value is either an inline `DW_FORM_string` string, + /// or a `DW_FORM_strp` reference to an offset into the `.debug_str` + /// section, return the attribute's string value as `Some`. Other attribute + /// value forms are returned as `None`. + /// + /// Warning: this function does not handle all possible string forms. + /// Use `Dwarf::attr_string` instead. + #[inline] + pub fn string_value(&self, debug_str: &DebugStr<R>) -> Option<R> { + self.value.string_value(debug_str) + } + + /// Try to return this attribute's value as a string slice. + /// + /// If this attribute's value is either an inline `DW_FORM_string` string, + /// or a `DW_FORM_strp` reference to an offset into the `.debug_str` + /// section, or a `DW_FORM_strp_sup` reference to an offset into a supplementary + /// object file, return the attribute's string value as `Some`. Other attribute + /// value forms are returned as `None`. + /// + /// Warning: this function does not handle all possible string forms. + /// Use `Dwarf::attr_string` instead. + #[inline] + pub fn string_value_sup( + &self, + debug_str: &DebugStr<R>, + debug_str_sup: Option<&DebugStr<R>>, + ) -> Option<R> { + self.value.string_value_sup(debug_str, debug_str_sup) + } +} + +impl<R, Offset> AttributeValue<R, Offset> +where + R: Reader<Offset = Offset>, + Offset: ReaderOffset, +{ + /// Try to convert this attribute's value to a u8. + pub fn u8_value(&self) -> Option<u8> { + if let Some(value) = self.udata_value() { + if value <= u64::from(u8::MAX) { + return Some(value as u8); + } + } + None + } + + /// Try to convert this attribute's value to a u16. + pub fn u16_value(&self) -> Option<u16> { + if let Some(value) = self.udata_value() { + if value <= u64::from(u16::MAX) { + return Some(value as u16); + } + } + None + } + + /// Try to convert this attribute's value to an unsigned integer. + pub fn udata_value(&self) -> Option<u64> { + Some(match *self { + AttributeValue::Data1(data) => u64::from(data), + AttributeValue::Data2(data) => u64::from(data), + AttributeValue::Data4(data) => u64::from(data), + AttributeValue::Data8(data) => data, + AttributeValue::Udata(data) => data, + AttributeValue::Sdata(data) => { + if data < 0 { + // Maybe we should emit a warning here + return None; + } + data as u64 + } + _ => return None, + }) + } + + /// Try to convert this attribute's value to a signed integer. + pub fn sdata_value(&self) -> Option<i64> { + Some(match *self { + AttributeValue::Data1(data) => i64::from(data as i8), + AttributeValue::Data2(data) => i64::from(data as i16), + AttributeValue::Data4(data) => i64::from(data as i32), + AttributeValue::Data8(data) => data as i64, + AttributeValue::Sdata(data) => data, + AttributeValue::Udata(data) => { + if data > i64::max_value() as u64 { + // Maybe we should emit a warning here + return None; + } + data as i64 + } + _ => return None, + }) + } + + /// Try to convert this attribute's value to an offset. + pub fn offset_value(&self) -> Option<R::Offset> { + // While offsets will be DW_FORM_data4/8 in DWARF version 2/3, + // these have already been converted to `SecOffset. + if let AttributeValue::SecOffset(offset) = *self { + Some(offset) + } else { + None + } + } + + /// Try to convert this attribute's value to an expression or location buffer. + /// + /// Expressions and locations may be `DW_FORM_block*` or `DW_FORM_exprloc`. + /// The standard doesn't mention `DW_FORM_block*` as a possible form, but + /// it is encountered in practice. + pub fn exprloc_value(&self) -> Option<Expression<R>> { + Some(match *self { + AttributeValue::Block(ref data) => Expression(data.clone()), + AttributeValue::Exprloc(ref data) => data.clone(), + _ => return None, + }) + } + + /// Try to return this attribute's value as a string slice. + /// + /// If this attribute's value is either an inline `DW_FORM_string` string, + /// or a `DW_FORM_strp` reference to an offset into the `.debug_str` + /// section, return the attribute's string value as `Some`. Other attribute + /// value forms are returned as `None`. + /// + /// Warning: this function does not handle all possible string forms. + /// Use `Dwarf::attr_string` instead. + pub fn string_value(&self, debug_str: &DebugStr<R>) -> Option<R> { + match *self { + AttributeValue::String(ref string) => Some(string.clone()), + AttributeValue::DebugStrRef(offset) => debug_str.get_str(offset).ok(), + _ => None, + } + } + + /// Try to return this attribute's value as a string slice. + /// + /// If this attribute's value is either an inline `DW_FORM_string` string, + /// or a `DW_FORM_strp` reference to an offset into the `.debug_str` + /// section, or a `DW_FORM_strp_sup` reference to an offset into a supplementary + /// object file, return the attribute's string value as `Some`. Other attribute + /// value forms are returned as `None`. + /// + /// Warning: this function does not handle all possible string forms. + /// Use `Dwarf::attr_string` instead. + pub fn string_value_sup( + &self, + debug_str: &DebugStr<R>, + debug_str_sup: Option<&DebugStr<R>>, + ) -> Option<R> { + match *self { + AttributeValue::String(ref string) => Some(string.clone()), + AttributeValue::DebugStrRef(offset) => debug_str.get_str(offset).ok(), + AttributeValue::DebugStrRefSup(offset) => { + debug_str_sup.and_then(|s| s.get_str(offset).ok()) + } + _ => None, + } + } +} + +fn length_u8_value<R: Reader>(input: &mut R) -> Result<R> { + let len = input.read_u8().map(R::Offset::from_u8)?; + input.split(len) +} + +fn length_u16_value<R: Reader>(input: &mut R) -> Result<R> { + let len = input.read_u16().map(R::Offset::from_u16)?; + input.split(len) +} + +fn length_u32_value<R: Reader>(input: &mut R) -> Result<R> { + let len = input.read_u32().map(R::Offset::from_u32)?; + input.split(len) +} + +fn length_uleb128_value<R: Reader>(input: &mut R) -> Result<R> { + let len = input.read_uleb128().and_then(R::Offset::from_u64)?; + input.split(len) +} + +// Return true if the given `name` can be a section offset in DWARF version 2/3. +// This is required to correctly handle relocations. +fn allow_section_offset(name: constants::DwAt, version: u16) -> bool { + match name { + constants::DW_AT_location + | constants::DW_AT_stmt_list + | constants::DW_AT_string_length + | constants::DW_AT_return_addr + | constants::DW_AT_start_scope + | constants::DW_AT_frame_base + | constants::DW_AT_macro_info + | constants::DW_AT_macros + | constants::DW_AT_segment + | constants::DW_AT_static_link + | constants::DW_AT_use_location + | constants::DW_AT_vtable_elem_location + | constants::DW_AT_ranges => true, + constants::DW_AT_data_member_location => version == 2 || version == 3, + _ => false, + } +} + +pub(crate) fn parse_attribute<R: Reader>( + input: &mut R, + encoding: Encoding, + spec: AttributeSpecification, +) -> Result<Attribute<R>> { + let mut form = spec.form(); + loop { + let value = match form { + constants::DW_FORM_indirect => { + let dynamic_form = input.read_uleb128_u16()?; + form = constants::DwForm(dynamic_form); + continue; + } + constants::DW_FORM_addr => { + let addr = input.read_address(encoding.address_size)?; + AttributeValue::Addr(addr) + } + constants::DW_FORM_block1 => { + let block = length_u8_value(input)?; + AttributeValue::Block(block) + } + constants::DW_FORM_block2 => { + let block = length_u16_value(input)?; + AttributeValue::Block(block) + } + constants::DW_FORM_block4 => { + let block = length_u32_value(input)?; + AttributeValue::Block(block) + } + constants::DW_FORM_block => { + let block = length_uleb128_value(input)?; + AttributeValue::Block(block) + } + constants::DW_FORM_data1 => { + let data = input.read_u8()?; + AttributeValue::Data1(data) + } + constants::DW_FORM_data2 => { + let data = input.read_u16()?; + AttributeValue::Data2(data) + } + constants::DW_FORM_data4 => { + // DWARF version 2/3 may use DW_FORM_data4/8 for section offsets. + // Ensure we handle relocations here. + if encoding.format == Format::Dwarf32 + && allow_section_offset(spec.name(), encoding.version) + { + let offset = input.read_offset(Format::Dwarf32)?; + AttributeValue::SecOffset(offset) + } else { + let data = input.read_u32()?; + AttributeValue::Data4(data) + } + } + constants::DW_FORM_data8 => { + // DWARF version 2/3 may use DW_FORM_data4/8 for section offsets. + // Ensure we handle relocations here. + if encoding.format == Format::Dwarf64 + && allow_section_offset(spec.name(), encoding.version) + { + let offset = input.read_offset(Format::Dwarf64)?; + AttributeValue::SecOffset(offset) + } else { + let data = input.read_u64()?; + AttributeValue::Data8(data) + } + } + constants::DW_FORM_data16 => { + let block = input.split(R::Offset::from_u8(16))?; + AttributeValue::Block(block) + } + constants::DW_FORM_udata => { + let data = input.read_uleb128()?; + AttributeValue::Udata(data) + } + constants::DW_FORM_sdata => { + let data = input.read_sleb128()?; + AttributeValue::Sdata(data) + } + constants::DW_FORM_exprloc => { + let block = length_uleb128_value(input)?; + AttributeValue::Exprloc(Expression(block)) + } + constants::DW_FORM_flag => { + let present = input.read_u8()?; + AttributeValue::Flag(present != 0) + } + constants::DW_FORM_flag_present => { + // FlagPresent is this weird compile time always true thing that + // isn't actually present in the serialized DIEs, only in the abbreviation. + AttributeValue::Flag(true) + } + constants::DW_FORM_sec_offset => { + let offset = input.read_offset(encoding.format)?; + AttributeValue::SecOffset(offset) + } + constants::DW_FORM_ref1 => { + let reference = input.read_u8().map(R::Offset::from_u8)?; + AttributeValue::UnitRef(UnitOffset(reference)) + } + constants::DW_FORM_ref2 => { + let reference = input.read_u16().map(R::Offset::from_u16)?; + AttributeValue::UnitRef(UnitOffset(reference)) + } + constants::DW_FORM_ref4 => { + let reference = input.read_u32().map(R::Offset::from_u32)?; + AttributeValue::UnitRef(UnitOffset(reference)) + } + constants::DW_FORM_ref8 => { + let reference = input.read_u64().and_then(R::Offset::from_u64)?; + AttributeValue::UnitRef(UnitOffset(reference)) + } + constants::DW_FORM_ref_udata => { + let reference = input.read_uleb128().and_then(R::Offset::from_u64)?; + AttributeValue::UnitRef(UnitOffset(reference)) + } + constants::DW_FORM_ref_addr => { + // This is an offset, but DWARF version 2 specifies that DW_FORM_ref_addr + // has the same size as an address on the target system. This was changed + // in DWARF version 3. + let offset = if encoding.version == 2 { + input.read_sized_offset(encoding.address_size)? + } else { + input.read_offset(encoding.format)? + }; + AttributeValue::DebugInfoRef(DebugInfoOffset(offset)) + } + constants::DW_FORM_ref_sig8 => { + let signature = input.read_u64()?; + AttributeValue::DebugTypesRef(DebugTypeSignature(signature)) + } + constants::DW_FORM_ref_sup4 => { + let offset = input.read_u32().map(R::Offset::from_u32)?; + AttributeValue::DebugInfoRefSup(DebugInfoOffset(offset)) + } + constants::DW_FORM_ref_sup8 => { + let offset = input.read_u64().and_then(R::Offset::from_u64)?; + AttributeValue::DebugInfoRefSup(DebugInfoOffset(offset)) + } + constants::DW_FORM_GNU_ref_alt => { + let offset = input.read_offset(encoding.format)?; + AttributeValue::DebugInfoRefSup(DebugInfoOffset(offset)) + } + constants::DW_FORM_string => { + let string = input.read_null_terminated_slice()?; + AttributeValue::String(string) + } + constants::DW_FORM_strp => { + let offset = input.read_offset(encoding.format)?; + AttributeValue::DebugStrRef(DebugStrOffset(offset)) + } + constants::DW_FORM_strp_sup | constants::DW_FORM_GNU_strp_alt => { + let offset = input.read_offset(encoding.format)?; + AttributeValue::DebugStrRefSup(DebugStrOffset(offset)) + } + constants::DW_FORM_line_strp => { + let offset = input.read_offset(encoding.format)?; + AttributeValue::DebugLineStrRef(DebugLineStrOffset(offset)) + } + constants::DW_FORM_implicit_const => { + let data = spec + .implicit_const_value() + .ok_or(Error::InvalidImplicitConst)?; + AttributeValue::Sdata(data) + } + constants::DW_FORM_strx | constants::DW_FORM_GNU_str_index => { + let index = input.read_uleb128().and_then(R::Offset::from_u64)?; + AttributeValue::DebugStrOffsetsIndex(DebugStrOffsetsIndex(index)) + } + constants::DW_FORM_strx1 => { + let index = input.read_u8().map(R::Offset::from_u8)?; + AttributeValue::DebugStrOffsetsIndex(DebugStrOffsetsIndex(index)) + } + constants::DW_FORM_strx2 => { + let index = input.read_u16().map(R::Offset::from_u16)?; + AttributeValue::DebugStrOffsetsIndex(DebugStrOffsetsIndex(index)) + } + constants::DW_FORM_strx3 => { + let index = input.read_uint(3).and_then(R::Offset::from_u64)?; + AttributeValue::DebugStrOffsetsIndex(DebugStrOffsetsIndex(index)) + } + constants::DW_FORM_strx4 => { + let index = input.read_u32().map(R::Offset::from_u32)?; + AttributeValue::DebugStrOffsetsIndex(DebugStrOffsetsIndex(index)) + } + constants::DW_FORM_addrx | constants::DW_FORM_GNU_addr_index => { + let index = input.read_uleb128().and_then(R::Offset::from_u64)?; + AttributeValue::DebugAddrIndex(DebugAddrIndex(index)) + } + constants::DW_FORM_addrx1 => { + let index = input.read_u8().map(R::Offset::from_u8)?; + AttributeValue::DebugAddrIndex(DebugAddrIndex(index)) + } + constants::DW_FORM_addrx2 => { + let index = input.read_u16().map(R::Offset::from_u16)?; + AttributeValue::DebugAddrIndex(DebugAddrIndex(index)) + } + constants::DW_FORM_addrx3 => { + let index = input.read_uint(3).and_then(R::Offset::from_u64)?; + AttributeValue::DebugAddrIndex(DebugAddrIndex(index)) + } + constants::DW_FORM_addrx4 => { + let index = input.read_u32().map(R::Offset::from_u32)?; + AttributeValue::DebugAddrIndex(DebugAddrIndex(index)) + } + constants::DW_FORM_loclistx => { + let index = input.read_uleb128().and_then(R::Offset::from_u64)?; + AttributeValue::DebugLocListsIndex(DebugLocListsIndex(index)) + } + constants::DW_FORM_rnglistx => { + let index = input.read_uleb128().and_then(R::Offset::from_u64)?; + AttributeValue::DebugRngListsIndex(DebugRngListsIndex(index)) + } + _ => { + return Err(Error::UnknownForm); + } + }; + let attr = Attribute { + name: spec.name(), + value, + }; + return Ok(attr); + } +} + +pub(crate) fn skip_attributes<R: Reader>( + input: &mut R, + encoding: Encoding, + specs: &[AttributeSpecification], +) -> Result<()> { + let mut skip_bytes = R::Offset::from_u8(0); + for spec in specs { + let mut form = spec.form(); + loop { + if let Some(len) = get_attribute_size(form, encoding) { + // We know the length of this attribute. Accumulate that length. + skip_bytes += R::Offset::from_u8(len); + break; + } + + // We have encountered a variable-length attribute. + if skip_bytes != R::Offset::from_u8(0) { + // Skip the accumulated skip bytes and then read the attribute normally. + input.skip(skip_bytes)?; + skip_bytes = R::Offset::from_u8(0); + } + + match form { + constants::DW_FORM_indirect => { + let dynamic_form = input.read_uleb128_u16()?; + form = constants::DwForm(dynamic_form); + continue; + } + constants::DW_FORM_block1 => { + skip_bytes = input.read_u8().map(R::Offset::from_u8)?; + } + constants::DW_FORM_block2 => { + skip_bytes = input.read_u16().map(R::Offset::from_u16)?; + } + constants::DW_FORM_block4 => { + skip_bytes = input.read_u32().map(R::Offset::from_u32)?; + } + constants::DW_FORM_block | constants::DW_FORM_exprloc => { + skip_bytes = input.read_uleb128().and_then(R::Offset::from_u64)?; + } + constants::DW_FORM_string => { + let _ = input.read_null_terminated_slice()?; + } + constants::DW_FORM_udata + | constants::DW_FORM_sdata + | constants::DW_FORM_ref_udata + | constants::DW_FORM_strx + | constants::DW_FORM_GNU_str_index + | constants::DW_FORM_addrx + | constants::DW_FORM_GNU_addr_index + | constants::DW_FORM_loclistx + | constants::DW_FORM_rnglistx => { + input.skip_leb128()?; + } + _ => { + return Err(Error::UnknownForm); + } + }; + break; + } + } + if skip_bytes != R::Offset::from_u8(0) { + // Skip the remaining accumulated skip bytes. + input.skip(skip_bytes)?; + } + Ok(()) +} + +/// An iterator over a particular entry's attributes. +/// +/// See [the documentation for +/// `DebuggingInformationEntry::attrs()`](./struct.DebuggingInformationEntry.html#method.attrs) +/// for details. +/// +/// Can be [used with +/// `FallibleIterator`](./index.html#using-with-fallibleiterator). +#[derive(Clone, Copy, Debug)] +pub struct AttrsIter<'abbrev, 'entry, 'unit, R: Reader> { + input: R, + attributes: &'abbrev [AttributeSpecification], + entry: &'entry DebuggingInformationEntry<'abbrev, 'unit, R>, +} + +impl<'abbrev, 'entry, 'unit, R: Reader> AttrsIter<'abbrev, 'entry, 'unit, R> { + /// Advance the iterator and return the next attribute. + /// + /// Returns `None` when iteration is finished. If an error + /// occurs while parsing the next attribute, then this error + /// is returned, and all subsequent calls return `None`. + #[inline(always)] + pub fn next(&mut self) -> Result<Option<Attribute<R>>> { + if self.attributes.is_empty() { + // Now that we have parsed all of the attributes, we know where + // either (1) this entry's children start, if the abbreviation says + // this entry has children; or (2) where this entry's siblings + // begin. + if let Some(end) = self.entry.attrs_len.get() { + debug_assert_eq!(end, self.input.offset_from(&self.entry.attrs_slice)); + } else { + self.entry + .attrs_len + .set(Some(self.input.offset_from(&self.entry.attrs_slice))); + } + + return Ok(None); + } + + let spec = self.attributes[0]; + let rest_spec = &self.attributes[1..]; + match parse_attribute(&mut self.input, self.entry.unit.encoding(), spec) { + Ok(attr) => { + self.attributes = rest_spec; + Ok(Some(attr)) + } + Err(e) => { + self.input.empty(); + Err(e) + } + } + } +} + +#[cfg(feature = "fallible-iterator")] +impl<'abbrev, 'entry, 'unit, R: Reader> fallible_iterator::FallibleIterator + for AttrsIter<'abbrev, 'entry, 'unit, R> +{ + type Item = Attribute<R>; + type Error = Error; + + fn next(&mut self) -> ::core::result::Result<Option<Self::Item>, Self::Error> { + AttrsIter::next(self) + } +} + +/// A raw reader of the data that defines the Debugging Information Entries. +/// +/// `EntriesRaw` provides primitives to read the components of Debugging Information +/// Entries (DIEs). A DIE consists of an abbreviation code (read with `read_abbreviation`) +/// followed by a number of attributes (read with `read_attribute`). +/// The user must provide the control flow to read these correctly. +/// In particular, all attributes must always be read before reading another +/// abbreviation code. +/// +/// `EntriesRaw` lacks some features of `EntriesCursor`, such as the ability to skip +/// to the next sibling DIE. However, this also allows it to optimize better, since it +/// does not need to perform the extra bookkeeping required to support these features, +/// and thus it is suitable for cases where performance is important. +/// +/// ## Example Usage +/// ```rust,no_run +/// # fn example() -> Result<(), gimli::Error> { +/// # let debug_info = gimli::DebugInfo::new(&[], gimli::LittleEndian); +/// # let get_some_unit = || debug_info.units().next().unwrap().unwrap(); +/// let unit = get_some_unit(); +/// # let debug_abbrev = gimli::DebugAbbrev::new(&[], gimli::LittleEndian); +/// # let get_abbrevs_for_unit = |_| unit.abbreviations(&debug_abbrev).unwrap(); +/// let abbrevs = get_abbrevs_for_unit(&unit); +/// +/// let mut entries = unit.entries_raw(&abbrevs, None)?; +/// while !entries.is_empty() { +/// let abbrev = if let Some(abbrev) = entries.read_abbreviation()? { +/// abbrev +/// } else { +/// // Null entry with no attributes. +/// continue +/// }; +/// match abbrev.tag() { +/// gimli::DW_TAG_subprogram => { +/// // Loop over attributes for DIEs we care about. +/// for spec in abbrev.attributes() { +/// let attr = entries.read_attribute(*spec)?; +/// match attr.name() { +/// // Handle attributes. +/// _ => {} +/// } +/// } +/// } +/// _ => { +/// // Skip attributes for DIEs we don't care about. +/// entries.skip_attributes(abbrev.attributes()); +/// } +/// } +/// } +/// # unreachable!() +/// # } +/// ``` +#[derive(Clone, Debug)] +pub struct EntriesRaw<'abbrev, 'unit, R> +where + R: Reader, +{ + input: R, + unit: &'unit UnitHeader<R>, + abbreviations: &'abbrev Abbreviations, + depth: isize, +} + +impl<'abbrev, 'unit, R: Reader> EntriesRaw<'abbrev, 'unit, R> { + /// Return true if there is no more input. + #[inline] + pub fn is_empty(&self) -> bool { + self.input.is_empty() + } + + /// Return the unit offset at which the reader will read next. + /// + /// If you want the offset of the next entry, then this must be called prior to reading + /// the next entry. + pub fn next_offset(&self) -> UnitOffset<R::Offset> { + UnitOffset(self.unit.header_size() + self.input.offset_from(&self.unit.entries_buf)) + } + + /// Return the depth of the next entry. + /// + /// This depth is updated when `read_abbreviation` is called, and is updated + /// based on null entries and the `has_children` field in the abbreviation. + #[inline] + pub fn next_depth(&self) -> isize { + self.depth + } + + /// Read an abbreviation code and lookup the corresponding `Abbreviation`. + /// + /// Returns `Ok(None)` for null entries. + #[inline] + pub fn read_abbreviation(&mut self) -> Result<Option<&'abbrev Abbreviation>> { + let code = self.input.read_uleb128()?; + if code == 0 { + self.depth -= 1; + return Ok(None); + }; + let abbrev = self + .abbreviations + .get(code) + .ok_or(Error::UnknownAbbreviation)?; + if abbrev.has_children() { + self.depth += 1; + } + Ok(Some(abbrev)) + } + + /// Read an attribute. + #[inline] + pub fn read_attribute(&mut self, spec: AttributeSpecification) -> Result<Attribute<R>> { + parse_attribute(&mut self.input, self.unit.encoding(), spec) + } + + /// Skip all the attributes of an abbreviation. + #[inline] + pub fn skip_attributes(&mut self, specs: &[AttributeSpecification]) -> Result<()> { + skip_attributes(&mut self.input, self.unit.encoding(), specs) + } +} + +/// A cursor into the Debugging Information Entries tree for a compilation unit. +/// +/// The `EntriesCursor` can traverse the DIE tree in DFS order using `next_dfs()`, +/// or skip to the next sibling of the entry the cursor is currently pointing to +/// using `next_sibling()`. +/// +/// It is also possible to traverse the DIE tree at a lower abstraction level +/// using `next_entry()`. This method does not skip over null entries, or provide +/// any indication of the current tree depth. In this case, you must use `current()` +/// to obtain the current entry, and `current().has_children()` to determine if +/// the entry following the current entry will be a sibling or child. `current()` +/// will return `None` if the current entry is a null entry, which signifies the +/// end of the current tree depth. +#[derive(Clone, Debug)] +pub struct EntriesCursor<'abbrev, 'unit, R> +where + R: Reader, +{ + input: R, + unit: &'unit UnitHeader<R>, + abbreviations: &'abbrev Abbreviations, + cached_current: Option<DebuggingInformationEntry<'abbrev, 'unit, R>>, + delta_depth: isize, +} + +impl<'abbrev, 'unit, R: Reader> EntriesCursor<'abbrev, 'unit, R> { + /// Get a reference to the entry that the cursor is currently pointing to. + /// + /// If the cursor is not pointing at an entry, or if the current entry is a + /// null entry, then `None` is returned. + #[inline] + pub fn current(&self) -> Option<&DebuggingInformationEntry<'abbrev, 'unit, R>> { + self.cached_current.as_ref() + } + + /// Move the cursor to the next DIE in the tree. + /// + /// Returns `Some` if there is a next entry, even if this entry is null. + /// If there is no next entry, then `None` is returned. + pub fn next_entry(&mut self) -> Result<Option<()>> { + if let Some(ref current) = self.cached_current { + self.input = current.after_attrs()?; + } + + if self.input.is_empty() { + self.cached_current = None; + self.delta_depth = 0; + return Ok(None); + } + + match DebuggingInformationEntry::parse(&mut self.input, self.unit, self.abbreviations) { + Ok(Some(entry)) => { + self.delta_depth = entry.has_children() as isize; + self.cached_current = Some(entry); + Ok(Some(())) + } + Ok(None) => { + self.delta_depth = -1; + self.cached_current = None; + Ok(Some(())) + } + Err(e) => { + self.input.empty(); + self.delta_depth = 0; + self.cached_current = None; + Err(e) + } + } + } + + /// Move the cursor to the next DIE in the tree in DFS order. + /// + /// Upon successful movement of the cursor, return the delta traversal + /// depth and the entry: + /// + /// * If we moved down into the previous current entry's children, we get + /// `Some((1, entry))`. + /// + /// * If we moved to the previous current entry's sibling, we get + /// `Some((0, entry))`. + /// + /// * If the previous entry does not have any siblings and we move up to + /// its parent's next sibling, then we get `Some((-1, entry))`. Note that + /// if the parent doesn't have a next sibling, then it could go up to the + /// parent's parent's next sibling and return `Some((-2, entry))`, etc. + /// + /// If there is no next entry, then `None` is returned. + /// + /// Here is an example that finds the first entry in a compilation unit that + /// does not have any children. + /// + /// ``` + /// # use gimli::{DebugAbbrev, DebugInfo, LittleEndian}; + /// # let info_buf = [ + /// # // Comilation unit header + /// # + /// # // 32-bit unit length = 25 + /// # 0x19, 0x00, 0x00, 0x00, + /// # // Version 4 + /// # 0x04, 0x00, + /// # // debug_abbrev_offset + /// # 0x00, 0x00, 0x00, 0x00, + /// # // Address size + /// # 0x04, + /// # + /// # // DIEs + /// # + /// # // Abbreviation code + /// # 0x01, + /// # // Attribute of form DW_FORM_string = "foo\0" + /// # 0x66, 0x6f, 0x6f, 0x00, + /// # + /// # // Children + /// # + /// # // Abbreviation code + /// # 0x01, + /// # // Attribute of form DW_FORM_string = "foo\0" + /// # 0x66, 0x6f, 0x6f, 0x00, + /// # + /// # // Children + /// # + /// # // Abbreviation code + /// # 0x01, + /// # // Attribute of form DW_FORM_string = "foo\0" + /// # 0x66, 0x6f, 0x6f, 0x00, + /// # + /// # // Children + /// # + /// # // End of children + /// # 0x00, + /// # + /// # // End of children + /// # 0x00, + /// # + /// # // End of children + /// # 0x00, + /// # ]; + /// # let debug_info = DebugInfo::new(&info_buf, LittleEndian); + /// # + /// # let abbrev_buf = [ + /// # // Code + /// # 0x01, + /// # // DW_TAG_subprogram + /// # 0x2e, + /// # // DW_CHILDREN_yes + /// # 0x01, + /// # // Begin attributes + /// # // Attribute name = DW_AT_name + /// # 0x03, + /// # // Attribute form = DW_FORM_string + /// # 0x08, + /// # // End attributes + /// # 0x00, + /// # 0x00, + /// # // Null terminator + /// # 0x00 + /// # ]; + /// # let debug_abbrev = DebugAbbrev::new(&abbrev_buf, LittleEndian); + /// # + /// # let get_some_unit = || debug_info.units().next().unwrap().unwrap(); + /// + /// let unit = get_some_unit(); + /// # let get_abbrevs_for_unit = |_| unit.abbreviations(&debug_abbrev).unwrap(); + /// let abbrevs = get_abbrevs_for_unit(&unit); + /// + /// let mut first_entry_with_no_children = None; + /// let mut cursor = unit.entries(&abbrevs); + /// + /// // Move the cursor to the root. + /// assert!(cursor.next_dfs().unwrap().is_some()); + /// + /// // Traverse the DIE tree in depth-first search order. + /// let mut depth = 0; + /// while let Some((delta_depth, current)) = cursor.next_dfs().expect("Should parse next dfs") { + /// // Update depth value, and break out of the loop when we + /// // return to the original starting position. + /// depth += delta_depth; + /// if depth <= 0 { + /// break; + /// } + /// + /// first_entry_with_no_children = Some(current.clone()); + /// } + /// + /// println!("The first entry with no children is {:?}", + /// first_entry_with_no_children.unwrap()); + /// ``` + pub fn next_dfs( + &mut self, + ) -> Result<Option<(isize, &DebuggingInformationEntry<'abbrev, 'unit, R>)>> { + let mut delta_depth = self.delta_depth; + loop { + // The next entry should be the one we want. + if self.next_entry()?.is_some() { + if let Some(ref entry) = self.cached_current { + return Ok(Some((delta_depth, entry))); + } + + // next_entry() read a null entry. + delta_depth += self.delta_depth; + } else { + return Ok(None); + } + } + } + + /// Move the cursor to the next sibling DIE of the current one. + /// + /// Returns `Ok(Some(entry))` when the cursor has been moved to + /// the next sibling, `Ok(None)` when there is no next sibling. + /// + /// The depth of the cursor is never changed if this method returns `Ok`. + /// Once `Ok(None)` is returned, this method will continue to return + /// `Ok(None)` until either `next_entry` or `next_dfs` is called. + /// + /// Here is an example that iterates over all of the direct children of the + /// root entry: + /// + /// ``` + /// # use gimli::{DebugAbbrev, DebugInfo, LittleEndian}; + /// # let info_buf = [ + /// # // Comilation unit header + /// # + /// # // 32-bit unit length = 25 + /// # 0x19, 0x00, 0x00, 0x00, + /// # // Version 4 + /// # 0x04, 0x00, + /// # // debug_abbrev_offset + /// # 0x00, 0x00, 0x00, 0x00, + /// # // Address size + /// # 0x04, + /// # + /// # // DIEs + /// # + /// # // Abbreviation code + /// # 0x01, + /// # // Attribute of form DW_FORM_string = "foo\0" + /// # 0x66, 0x6f, 0x6f, 0x00, + /// # + /// # // Children + /// # + /// # // Abbreviation code + /// # 0x01, + /// # // Attribute of form DW_FORM_string = "foo\0" + /// # 0x66, 0x6f, 0x6f, 0x00, + /// # + /// # // Children + /// # + /// # // Abbreviation code + /// # 0x01, + /// # // Attribute of form DW_FORM_string = "foo\0" + /// # 0x66, 0x6f, 0x6f, 0x00, + /// # + /// # // Children + /// # + /// # // End of children + /// # 0x00, + /// # + /// # // End of children + /// # 0x00, + /// # + /// # // End of children + /// # 0x00, + /// # ]; + /// # let debug_info = DebugInfo::new(&info_buf, LittleEndian); + /// # + /// # let get_some_unit = || debug_info.units().next().unwrap().unwrap(); + /// + /// # let abbrev_buf = [ + /// # // Code + /// # 0x01, + /// # // DW_TAG_subprogram + /// # 0x2e, + /// # // DW_CHILDREN_yes + /// # 0x01, + /// # // Begin attributes + /// # // Attribute name = DW_AT_name + /// # 0x03, + /// # // Attribute form = DW_FORM_string + /// # 0x08, + /// # // End attributes + /// # 0x00, + /// # 0x00, + /// # // Null terminator + /// # 0x00 + /// # ]; + /// # let debug_abbrev = DebugAbbrev::new(&abbrev_buf, LittleEndian); + /// # + /// let unit = get_some_unit(); + /// # let get_abbrevs_for_unit = |_| unit.abbreviations(&debug_abbrev).unwrap(); + /// let abbrevs = get_abbrevs_for_unit(&unit); + /// + /// let mut cursor = unit.entries(&abbrevs); + /// + /// // Move the cursor to the root. + /// assert!(cursor.next_dfs().unwrap().is_some()); + /// + /// // Move the cursor to the root's first child. + /// assert!(cursor.next_dfs().unwrap().is_some()); + /// + /// // Iterate the root's children. + /// loop { + /// { + /// let current = cursor.current().expect("Should be at an entry"); + /// println!("{:?} is a child of the root", current); + /// } + /// + /// if cursor.next_sibling().expect("Should parse next sibling").is_none() { + /// break; + /// } + /// } + /// ``` + pub fn next_sibling( + &mut self, + ) -> Result<Option<&DebuggingInformationEntry<'abbrev, 'unit, R>>> { + if self.current().is_none() { + // We're already at the null for the end of the sibling list. + return Ok(None); + } + + // Loop until we find an entry at the current level. + let mut depth = 0; + loop { + // Use is_some() and unwrap() to keep borrow checker happy. + if self.current().is_some() && self.current().unwrap().has_children() { + if let Some(sibling_input) = self.current().unwrap().sibling() { + // Fast path: this entry has a DW_AT_sibling + // attribute pointing to its sibling, so jump + // to it (which keeps us at the same depth). + self.input = sibling_input; + self.cached_current = None; + } else { + // This entry has children, so the next entry is + // down one level. + depth += 1; + } + } + + if self.next_entry()?.is_none() { + // End of input. + return Ok(None); + } + + if depth == 0 { + // Found an entry at the current level. + return Ok(self.current()); + } + + if self.current().is_none() { + // A null entry means the end of a child list, so we're + // back up a level. + depth -= 1; + } + } + } +} + +/// The state information for a tree view of the Debugging Information Entries. +/// +/// The `EntriesTree` can be used to recursively iterate through the DIE +/// tree, following the parent/child relationships. The `EntriesTree` contains +/// shared state for all nodes in the tree, avoiding any duplicate parsing of +/// entries during the traversal. +/// +/// ## Example Usage +/// ```rust,no_run +/// # fn example() -> Result<(), gimli::Error> { +/// # let debug_info = gimli::DebugInfo::new(&[], gimli::LittleEndian); +/// # let get_some_unit = || debug_info.units().next().unwrap().unwrap(); +/// let unit = get_some_unit(); +/// # let debug_abbrev = gimli::DebugAbbrev::new(&[], gimli::LittleEndian); +/// # let get_abbrevs_for_unit = |_| unit.abbreviations(&debug_abbrev).unwrap(); +/// let abbrevs = get_abbrevs_for_unit(&unit); +/// +/// let mut tree = unit.entries_tree(&abbrevs, None)?; +/// let root = tree.root()?; +/// process_tree(root)?; +/// # unreachable!() +/// # } +/// +/// fn process_tree<R>(mut node: gimli::EntriesTreeNode<R>) -> gimli::Result<()> +/// where R: gimli::Reader +/// { +/// { +/// // Examine the entry attributes. +/// let mut attrs = node.entry().attrs(); +/// while let Some(attr) = attrs.next()? { +/// } +/// } +/// let mut children = node.children(); +/// while let Some(child) = children.next()? { +/// // Recursively process a child. +/// process_tree(child); +/// } +/// Ok(()) +/// } +/// ``` +#[derive(Clone, Debug)] +pub struct EntriesTree<'abbrev, 'unit, R> +where + R: Reader, +{ + root: R, + unit: &'unit UnitHeader<R>, + abbreviations: &'abbrev Abbreviations, + input: R, + entry: Option<DebuggingInformationEntry<'abbrev, 'unit, R>>, + depth: isize, +} + +impl<'abbrev, 'unit, R: Reader> EntriesTree<'abbrev, 'unit, R> { + fn new(root: R, unit: &'unit UnitHeader<R>, abbreviations: &'abbrev Abbreviations) -> Self { + let input = root.clone(); + EntriesTree { + root, + unit, + abbreviations, + input, + entry: None, + depth: 0, + } + } + + /// Returns the root node of the tree. + pub fn root<'me>(&'me mut self) -> Result<EntriesTreeNode<'abbrev, 'unit, 'me, R>> { + self.input = self.root.clone(); + self.entry = + DebuggingInformationEntry::parse(&mut self.input, self.unit, self.abbreviations)?; + if self.entry.is_none() { + return Err(Error::UnexpectedNull); + } + self.depth = 0; + Ok(EntriesTreeNode::new(self, 1)) + } + + /// Move the cursor to the next entry at the specified depth. + /// + /// Requires `depth <= self.depth + 1`. + /// + /// Returns `true` if successful. + fn next(&mut self, depth: isize) -> Result<bool> { + if self.depth < depth { + debug_assert_eq!(self.depth + 1, depth); + + match self.entry { + Some(ref entry) => { + if !entry.has_children() { + return Ok(false); + } + self.depth += 1; + self.input = entry.after_attrs()?; + } + None => return Ok(false), + } + + if self.input.is_empty() { + self.entry = None; + return Ok(false); + } + + return match DebuggingInformationEntry::parse( + &mut self.input, + self.unit, + self.abbreviations, + ) { + Ok(entry) => { + self.entry = entry; + Ok(self.entry.is_some()) + } + Err(e) => { + self.input.empty(); + self.entry = None; + Err(e) + } + }; + } + + loop { + match self.entry { + Some(ref entry) => { + if entry.has_children() { + if let Some(sibling_input) = entry.sibling() { + // Fast path: this entry has a DW_AT_sibling + // attribute pointing to its sibling, so jump + // to it (which keeps us at the same depth). + self.input = sibling_input; + } else { + // This entry has children, so the next entry is + // down one level. + self.depth += 1; + self.input = entry.after_attrs()?; + } + } else { + // This entry has no children, so next entry is at same depth. + self.input = entry.after_attrs()?; + } + } + None => { + // This entry is a null, so next entry is up one level. + self.depth -= 1; + } + } + + if self.input.is_empty() { + self.entry = None; + return Ok(false); + } + + match DebuggingInformationEntry::parse(&mut self.input, self.unit, self.abbreviations) { + Ok(entry) => { + self.entry = entry; + if self.depth == depth { + return Ok(self.entry.is_some()); + } + } + Err(e) => { + self.input.empty(); + self.entry = None; + return Err(e); + } + } + } + } +} + +/// A node in the Debugging Information Entry tree. +/// +/// The root node of a tree can be obtained +/// via [`EntriesTree::root`](./struct.EntriesTree.html#method.root). +#[derive(Debug)] +pub struct EntriesTreeNode<'abbrev, 'unit, 'tree, R: Reader> { + tree: &'tree mut EntriesTree<'abbrev, 'unit, R>, + depth: isize, +} + +impl<'abbrev, 'unit, 'tree, R: Reader> EntriesTreeNode<'abbrev, 'unit, 'tree, R> { + fn new( + tree: &'tree mut EntriesTree<'abbrev, 'unit, R>, + depth: isize, + ) -> EntriesTreeNode<'abbrev, 'unit, 'tree, R> { + debug_assert!(tree.entry.is_some()); + EntriesTreeNode { tree, depth } + } + + /// Returns the current entry in the tree. + pub fn entry(&self) -> &DebuggingInformationEntry<'abbrev, 'unit, R> { + // We never create a node without an entry. + self.tree.entry.as_ref().unwrap() + } + + /// Create an iterator for the children of the current entry. + /// + /// The current entry can no longer be accessed after creating the + /// iterator. + pub fn children(self) -> EntriesTreeIter<'abbrev, 'unit, 'tree, R> { + EntriesTreeIter::new(self.tree, self.depth) + } +} + +/// An iterator that allows traversal of the children of an +/// `EntriesTreeNode`. +/// +/// The items returned by this iterator are also `EntriesTreeNode`s, +/// which allow recursive traversal of grandchildren, etc. +#[derive(Debug)] +pub struct EntriesTreeIter<'abbrev, 'unit, 'tree, R: Reader> { + tree: &'tree mut EntriesTree<'abbrev, 'unit, R>, + depth: isize, + empty: bool, +} + +impl<'abbrev, 'unit, 'tree, R: Reader> EntriesTreeIter<'abbrev, 'unit, 'tree, R> { + fn new( + tree: &'tree mut EntriesTree<'abbrev, 'unit, R>, + depth: isize, + ) -> EntriesTreeIter<'abbrev, 'unit, 'tree, R> { + EntriesTreeIter { + tree, + depth, + empty: false, + } + } + + /// Returns an `EntriesTreeNode` for the next child entry. + /// + /// Returns `None` if there are no more children. + pub fn next<'me>(&'me mut self) -> Result<Option<EntriesTreeNode<'abbrev, 'unit, 'me, R>>> { + if self.empty { + Ok(None) + } else if self.tree.next(self.depth)? { + Ok(Some(EntriesTreeNode::new(self.tree, self.depth + 1))) + } else { + self.empty = true; + Ok(None) + } + } +} + +/// Parse a type unit header's unique type signature. Callers should handle +/// unique-ness checking. +fn parse_type_signature<R: Reader>(input: &mut R) -> Result<DebugTypeSignature> { + input.read_u64().map(DebugTypeSignature) +} + +/// Parse a type unit header's type offset. +fn parse_type_offset<R: Reader>(input: &mut R, format: Format) -> Result<UnitOffset<R::Offset>> { + input.read_offset(format).map(UnitOffset) +} + +/// The `DebugTypes` struct represents the DWARF type information +/// found in the `.debug_types` section. +#[derive(Debug, Default, Clone, Copy)] +pub struct DebugTypes<R> { + debug_types_section: R, +} + +impl<'input, Endian> DebugTypes<EndianSlice<'input, Endian>> +where + Endian: Endianity, +{ + /// Construct a new `DebugTypes` instance from the data in the `.debug_types` + /// section. + /// + /// It is the caller's responsibility to read the `.debug_types` section and + /// present it as a `&[u8]` slice. That means using some ELF loader on + /// Linux, a Mach-O loader on macOS, etc. + /// + /// ``` + /// use gimli::{DebugTypes, LittleEndian}; + /// + /// # let buf = [0x00, 0x01, 0x02, 0x03]; + /// # let read_debug_types_section_somehow = || &buf; + /// let debug_types = DebugTypes::new(read_debug_types_section_somehow(), LittleEndian); + /// ``` + pub fn new(debug_types_section: &'input [u8], endian: Endian) -> Self { + Self::from(EndianSlice::new(debug_types_section, endian)) + } +} + +impl<T> DebugTypes<T> { + /// Create a `DebugTypes` section that references the data in `self`. + /// + /// This is useful when `R` implements `Reader` but `T` does not. + /// + /// ## Example Usage + /// + /// ```rust,no_run + /// # let load_section = || unimplemented!(); + /// // Read the DWARF section into a `Vec` with whatever object loader you're using. + /// let owned_section: gimli::DebugTypes<Vec<u8>> = load_section(); + /// // Create a reference to the DWARF section. + /// let section = owned_section.borrow(|section| { + /// gimli::EndianSlice::new(§ion, gimli::LittleEndian) + /// }); + /// ``` + pub fn borrow<'a, F, R>(&'a self, mut borrow: F) -> DebugTypes<R> + where + F: FnMut(&'a T) -> R, + { + borrow(&self.debug_types_section).into() + } +} + +impl<R> Section<R> for DebugTypes<R> { + fn id() -> SectionId { + SectionId::DebugTypes + } + + fn reader(&self) -> &R { + &self.debug_types_section + } +} + +impl<R> From<R> for DebugTypes<R> { + fn from(debug_types_section: R) -> Self { + DebugTypes { + debug_types_section, + } + } +} + +impl<R: Reader> DebugTypes<R> { + /// Iterate the type-units in this `.debug_types` section. + /// + /// ``` + /// use gimli::{DebugTypes, LittleEndian}; + /// + /// # let buf = []; + /// # let read_debug_types_section_somehow = || &buf; + /// let debug_types = DebugTypes::new(read_debug_types_section_somehow(), LittleEndian); + /// + /// let mut iter = debug_types.units(); + /// while let Some(unit) = iter.next().unwrap() { + /// println!("unit's length is {}", unit.unit_length()); + /// } + /// ``` + /// + /// Can be [used with + /// `FallibleIterator`](./index.html#using-with-fallibleiterator). + pub fn units(&self) -> DebugTypesUnitHeadersIter<R> { + DebugTypesUnitHeadersIter { + input: self.debug_types_section.clone(), + offset: DebugTypesOffset(R::Offset::from_u8(0)), + } + } +} + +/// An iterator over the type-units of this `.debug_types` section. +/// +/// See the [documentation on +/// `DebugTypes::units`](./struct.DebugTypes.html#method.units) for +/// more detail. +#[derive(Clone, Debug)] +pub struct DebugTypesUnitHeadersIter<R: Reader> { + input: R, + offset: DebugTypesOffset<R::Offset>, +} + +impl<R: Reader> DebugTypesUnitHeadersIter<R> { + /// Advance the iterator to the next type unit header. + pub fn next(&mut self) -> Result<Option<UnitHeader<R>>> { + if self.input.is_empty() { + Ok(None) + } else { + let len = self.input.len(); + match parse_unit_header(&mut self.input, self.offset.into()) { + Ok(header) => { + self.offset.0 += len - self.input.len(); + Ok(Some(header)) + } + Err(e) => { + self.input.empty(); + Err(e) + } + } + } + } +} + +#[cfg(feature = "fallible-iterator")] +impl<R: Reader> fallible_iterator::FallibleIterator for DebugTypesUnitHeadersIter<R> { + type Item = UnitHeader<R>; + type Error = Error; + + fn next(&mut self) -> ::core::result::Result<Option<Self::Item>, Self::Error> { + DebugTypesUnitHeadersIter::next(self) + } +} + +#[cfg(test)] +// Tests require leb128::write. +#[cfg(feature = "write")] +mod tests { + use super::*; + use crate::constants; + use crate::constants::*; + use crate::endianity::{Endianity, LittleEndian}; + use crate::leb128; + use crate::read::abbrev::tests::AbbrevSectionMethods; + use crate::read::{ + Abbreviation, AttributeSpecification, DebugAbbrev, EndianSlice, Error, Result, + }; + use crate::test_util::GimliSectionMethods; + use alloc::vec::Vec; + use core::cell::Cell; + use test_assembler::{Endian, Label, LabelMaker, Section}; + + // Mixin methods for `Section` to help define binary test data. + + trait UnitSectionMethods { + fn unit<'input, E>(self, unit: &mut UnitHeader<EndianSlice<'input, E>>) -> Self + where + E: Endianity; + fn die<F>(self, code: u64, attr: F) -> Self + where + F: Fn(Section) -> Section; + fn die_null(self) -> Self; + fn attr_string(self, s: &str) -> Self; + fn attr_ref1(self, o: u8) -> Self; + fn offset(self, offset: usize, format: Format) -> Self; + } + + impl UnitSectionMethods for Section { + fn unit<'input, E>(self, unit: &mut UnitHeader<EndianSlice<'input, E>>) -> Self + where + E: Endianity, + { + let size = self.size(); + let length = Label::new(); + let start = Label::new(); + let end = Label::new(); + + let section = match unit.format() { + Format::Dwarf32 => self.L32(&length), + Format::Dwarf64 => self.L32(0xffff_ffff).L64(&length), + }; + + let section = match unit.version() { + 2 | 3 | 4 => section + .mark(&start) + .L16(unit.version()) + .offset(unit.debug_abbrev_offset.0, unit.format()) + .D8(unit.address_size()), + 5 => section + .mark(&start) + .L16(unit.version()) + .D8(unit.type_().dw_ut().0) + .D8(unit.address_size()) + .offset(unit.debug_abbrev_offset.0, unit.format()), + _ => unreachable!(), + }; + + let section = match unit.type_() { + UnitType::Compilation | UnitType::Partial => { + unit.unit_offset = DebugInfoOffset(size as usize).into(); + section + } + UnitType::Type { + type_signature, + type_offset, + } + | UnitType::SplitType { + type_signature, + type_offset, + } => { + if unit.version() == 5 { + unit.unit_offset = DebugInfoOffset(size as usize).into(); + } else { + unit.unit_offset = DebugTypesOffset(size as usize).into(); + } + section + .L64(type_signature.0) + .offset(type_offset.0, unit.format()) + } + UnitType::Skeleton(dwo_id) | UnitType::SplitCompilation(dwo_id) => { + unit.unit_offset = DebugInfoOffset(size as usize).into(); + section.L64(dwo_id.0) + } + }; + + let section = section.append_bytes(unit.entries_buf.slice()).mark(&end); + + unit.unit_length = (&end - &start) as usize; + length.set_const(unit.unit_length as u64); + + section + } + + fn die<F>(self, code: u64, attr: F) -> Self + where + F: Fn(Section) -> Section, + { + let section = self.uleb(code); + attr(section) + } + + fn die_null(self) -> Self { + self.D8(0) + } + + fn attr_string(self, attr: &str) -> Self { + self.append_bytes(attr.as_bytes()).D8(0) + } + + fn attr_ref1(self, attr: u8) -> Self { + self.D8(attr) + } + + fn offset(self, offset: usize, format: Format) -> Self { + match format { + Format::Dwarf32 => self.L32(offset as u32), + Format::Dwarf64 => self.L64(offset as u64), + } + } + } + + /// Ensure that `UnitHeader<R>` is covariant wrt R. + #[test] + fn test_unit_header_variance() { + /// This only needs to compile. + fn _f<'a: 'b, 'b, E: Endianity>( + x: UnitHeader<EndianSlice<'a, E>>, + ) -> UnitHeader<EndianSlice<'b, E>> { + x + } + } + + #[test] + fn test_parse_debug_abbrev_offset_32() { + let section = Section::with_endian(Endian::Little).L32(0x0403_0201); + let buf = section.get_contents().unwrap(); + let buf = &mut EndianSlice::new(&buf, LittleEndian); + + match parse_debug_abbrev_offset(buf, Format::Dwarf32) { + Ok(val) => assert_eq!(val, DebugAbbrevOffset(0x0403_0201)), + otherwise => panic!("Unexpected result: {:?}", otherwise), + }; + } + + #[test] + fn test_parse_debug_abbrev_offset_32_incomplete() { + let buf = [0x01, 0x02]; + let buf = &mut EndianSlice::new(&buf, LittleEndian); + + match parse_debug_abbrev_offset(buf, Format::Dwarf32) { + Err(Error::UnexpectedEof(_)) => assert!(true), + otherwise => panic!("Unexpected result: {:?}", otherwise), + }; + } + + #[test] + #[cfg(target_pointer_width = "64")] + fn test_parse_debug_abbrev_offset_64() { + let section = Section::with_endian(Endian::Little).L64(0x0807_0605_0403_0201); + let buf = section.get_contents().unwrap(); + let buf = &mut EndianSlice::new(&buf, LittleEndian); + + match parse_debug_abbrev_offset(buf, Format::Dwarf64) { + Ok(val) => assert_eq!(val, DebugAbbrevOffset(0x0807_0605_0403_0201)), + otherwise => panic!("Unexpected result: {:?}", otherwise), + }; + } + + #[test] + fn test_parse_debug_abbrev_offset_64_incomplete() { + let buf = [0x01, 0x02]; + let buf = &mut EndianSlice::new(&buf, LittleEndian); + + match parse_debug_abbrev_offset(buf, Format::Dwarf64) { + Err(Error::UnexpectedEof(_)) => assert!(true), + otherwise => panic!("Unexpected result: {:?}", otherwise), + }; + } + + #[test] + fn test_parse_debug_info_offset_32() { + let section = Section::with_endian(Endian::Little).L32(0x0403_0201); + let buf = section.get_contents().unwrap(); + let buf = &mut EndianSlice::new(&buf, LittleEndian); + + match parse_debug_info_offset(buf, Format::Dwarf32) { + Ok(val) => assert_eq!(val, DebugInfoOffset(0x0403_0201)), + otherwise => panic!("Unexpected result: {:?}", otherwise), + }; + } + + #[test] + fn test_parse_debug_info_offset_32_incomplete() { + let buf = [0x01, 0x02]; + let buf = &mut EndianSlice::new(&buf, LittleEndian); + + match parse_debug_info_offset(buf, Format::Dwarf32) { + Err(Error::UnexpectedEof(_)) => assert!(true), + otherwise => panic!("Unexpected result: {:?}", otherwise), + }; + } + + #[test] + #[cfg(target_pointer_width = "64")] + fn test_parse_debug_info_offset_64() { + let section = Section::with_endian(Endian::Little).L64(0x0807_0605_0403_0201); + let buf = section.get_contents().unwrap(); + let buf = &mut EndianSlice::new(&buf, LittleEndian); + + match parse_debug_info_offset(buf, Format::Dwarf64) { + Ok(val) => assert_eq!(val, DebugInfoOffset(0x0807_0605_0403_0201)), + otherwise => panic!("Unexpected result: {:?}", otherwise), + }; + } + + #[test] + fn test_parse_debug_info_offset_64_incomplete() { + let buf = [0x01, 0x02]; + let buf = &mut EndianSlice::new(&buf, LittleEndian); + + match parse_debug_info_offset(buf, Format::Dwarf64) { + Err(Error::UnexpectedEof(_)) => assert!(true), + otherwise => panic!("Unexpected result: {:?}", otherwise), + }; + } + + #[test] + #[cfg(target_pointer_width = "64")] + fn test_units() { + let expected_rest = &[1, 2, 3, 4, 5, 6, 7, 8, 9]; + let mut unit64 = UnitHeader { + encoding: Encoding { + format: Format::Dwarf64, + version: 4, + address_size: 8, + }, + unit_length: 0, + unit_type: UnitType::Compilation, + debug_abbrev_offset: DebugAbbrevOffset(0x0102_0304_0506_0708), + unit_offset: DebugInfoOffset(0).into(), + entries_buf: EndianSlice::new(expected_rest, LittleEndian), + }; + let mut unit32 = UnitHeader { + encoding: Encoding { + format: Format::Dwarf32, + version: 4, + address_size: 4, + }, + unit_length: 0, + unit_type: UnitType::Compilation, + debug_abbrev_offset: DebugAbbrevOffset(0x0807_0605), + unit_offset: DebugInfoOffset(0).into(), + entries_buf: EndianSlice::new(expected_rest, LittleEndian), + }; + let section = Section::with_endian(Endian::Little) + .unit(&mut unit64) + .unit(&mut unit32); + let buf = section.get_contents().unwrap(); + + let debug_info = DebugInfo::new(&buf, LittleEndian); + let mut units = debug_info.units(); + + assert_eq!(units.next(), Ok(Some(unit64))); + assert_eq!(units.next(), Ok(Some(unit32))); + assert_eq!(units.next(), Ok(None)); + } + + #[test] + fn test_unit_version_unknown_version() { + let buf = [0x02, 0x00, 0x00, 0x00, 0xab, 0xcd]; + let rest = &mut EndianSlice::new(&buf, LittleEndian); + + match parse_unit_header(rest, DebugInfoOffset(0).into()) { + Err(Error::UnknownVersion(0xcdab)) => assert!(true), + otherwise => panic!("Unexpected result: {:?}", otherwise), + }; + + let buf = [0x02, 0x00, 0x00, 0x00, 0x1, 0x0]; + let rest = &mut EndianSlice::new(&buf, LittleEndian); + + match parse_unit_header(rest, DebugInfoOffset(0).into()) { + Err(Error::UnknownVersion(1)) => assert!(true), + otherwise => panic!("Unexpected result: {:?}", otherwise), + }; + } + + #[test] + fn test_unit_version_incomplete() { + let buf = [0x01, 0x00, 0x00, 0x00, 0x04]; + let rest = &mut EndianSlice::new(&buf, LittleEndian); + + match parse_unit_header(rest, DebugInfoOffset(0).into()) { + Err(Error::UnexpectedEof(_)) => assert!(true), + otherwise => panic!("Unexpected result: {:?}", otherwise), + }; + } + + #[test] + fn test_parse_unit_header_32_ok() { + let expected_rest = &[1, 2, 3, 4, 5, 6, 7, 8, 9]; + let encoding = Encoding { + format: Format::Dwarf32, + version: 4, + address_size: 4, + }; + let mut expected_unit = UnitHeader { + encoding, + unit_length: 0, + unit_type: UnitType::Compilation, + debug_abbrev_offset: DebugAbbrevOffset(0x0807_0605), + unit_offset: DebugInfoOffset(0).into(), + entries_buf: EndianSlice::new(expected_rest, LittleEndian), + }; + let section = Section::with_endian(Endian::Little) + .unit(&mut expected_unit) + .append_bytes(expected_rest); + let buf = section.get_contents().unwrap(); + let rest = &mut EndianSlice::new(&buf, LittleEndian); + + assert_eq!( + parse_unit_header(rest, DebugInfoOffset(0).into()), + Ok(expected_unit) + ); + assert_eq!(*rest, EndianSlice::new(expected_rest, LittleEndian)); + } + + #[test] + #[cfg(target_pointer_width = "64")] + fn test_parse_unit_header_64_ok() { + let expected_rest = &[1, 2, 3, 4, 5, 6, 7, 8, 9]; + let encoding = Encoding { + format: Format::Dwarf64, + version: 4, + address_size: 8, + }; + let mut expected_unit = UnitHeader { + encoding, + unit_length: 0, + unit_type: UnitType::Compilation, + debug_abbrev_offset: DebugAbbrevOffset(0x0102_0304_0506_0708), + unit_offset: DebugInfoOffset(0).into(), + entries_buf: EndianSlice::new(expected_rest, LittleEndian), + }; + let section = Section::with_endian(Endian::Little) + .unit(&mut expected_unit) + .append_bytes(expected_rest); + let buf = section.get_contents().unwrap(); + let rest = &mut EndianSlice::new(&buf, LittleEndian); + + assert_eq!( + parse_unit_header(rest, DebugInfoOffset(0).into()), + Ok(expected_unit) + ); + assert_eq!(*rest, EndianSlice::new(expected_rest, LittleEndian)); + } + + #[test] + fn test_parse_v5_unit_header_32_ok() { + let expected_rest = &[1, 2, 3, 4, 5, 6, 7, 8, 9]; + let encoding = Encoding { + format: Format::Dwarf32, + version: 5, + address_size: 4, + }; + let mut expected_unit = UnitHeader { + encoding, + unit_length: 0, + unit_type: UnitType::Compilation, + debug_abbrev_offset: DebugAbbrevOffset(0x0807_0605), + unit_offset: DebugInfoOffset(0).into(), + entries_buf: EndianSlice::new(expected_rest, LittleEndian), + }; + let section = Section::with_endian(Endian::Little) + .unit(&mut expected_unit) + .append_bytes(expected_rest); + let buf = section.get_contents().unwrap(); + let rest = &mut EndianSlice::new(&buf, LittleEndian); + + assert_eq!( + parse_unit_header(rest, DebugInfoOffset(0).into()), + Ok(expected_unit) + ); + assert_eq!(*rest, EndianSlice::new(expected_rest, LittleEndian)); + } + + #[test] + #[cfg(target_pointer_width = "64")] + fn test_parse_v5_unit_header_64_ok() { + let expected_rest = &[1, 2, 3, 4, 5, 6, 7, 8, 9]; + let encoding = Encoding { + format: Format::Dwarf64, + version: 5, + address_size: 8, + }; + let mut expected_unit = UnitHeader { + encoding, + unit_length: 0, + unit_type: UnitType::Compilation, + debug_abbrev_offset: DebugAbbrevOffset(0x0102_0304_0506_0708), + unit_offset: DebugInfoOffset(0).into(), + entries_buf: EndianSlice::new(expected_rest, LittleEndian), + }; + let section = Section::with_endian(Endian::Little) + .unit(&mut expected_unit) + .append_bytes(expected_rest); + let buf = section.get_contents().unwrap(); + let rest = &mut EndianSlice::new(&buf, LittleEndian); + + assert_eq!( + parse_unit_header(rest, DebugInfoOffset(0).into()), + Ok(expected_unit) + ); + assert_eq!(*rest, EndianSlice::new(expected_rest, LittleEndian)); + } + + #[test] + fn test_parse_v5_partial_unit_header_32_ok() { + let expected_rest = &[1, 2, 3, 4, 5, 6, 7, 8, 9]; + let encoding = Encoding { + format: Format::Dwarf32, + version: 5, + address_size: 4, + }; + let mut expected_unit = UnitHeader { + encoding, + unit_length: 0, + unit_type: UnitType::Partial, + debug_abbrev_offset: DebugAbbrevOffset(0x0807_0605), + unit_offset: DebugInfoOffset(0).into(), + entries_buf: EndianSlice::new(expected_rest, LittleEndian), + }; + let section = Section::with_endian(Endian::Little) + .unit(&mut expected_unit) + .append_bytes(expected_rest); + let buf = section.get_contents().unwrap(); + let rest = &mut EndianSlice::new(&buf, LittleEndian); + + assert_eq!( + parse_unit_header(rest, DebugInfoOffset(0).into()), + Ok(expected_unit) + ); + assert_eq!(*rest, EndianSlice::new(expected_rest, LittleEndian)); + } + + #[test] + #[cfg(target_pointer_width = "64")] + fn test_parse_v5_partial_unit_header_64_ok() { + let expected_rest = &[1, 2, 3, 4, 5, 6, 7, 8, 9]; + let encoding = Encoding { + format: Format::Dwarf64, + version: 5, + address_size: 8, + }; + let mut expected_unit = UnitHeader { + encoding, + unit_length: 0, + unit_type: UnitType::Partial, + debug_abbrev_offset: DebugAbbrevOffset(0x0102_0304_0506_0708), + unit_offset: DebugInfoOffset(0).into(), + entries_buf: EndianSlice::new(expected_rest, LittleEndian), + }; + let section = Section::with_endian(Endian::Little) + .unit(&mut expected_unit) + .append_bytes(expected_rest); + let buf = section.get_contents().unwrap(); + let rest = &mut EndianSlice::new(&buf, LittleEndian); + + assert_eq!( + parse_unit_header(rest, DebugInfoOffset(0).into()), + Ok(expected_unit) + ); + assert_eq!(*rest, EndianSlice::new(expected_rest, LittleEndian)); + } + + #[test] + fn test_parse_v5_skeleton_unit_header_32_ok() { + let expected_rest = &[1, 2, 3, 4, 5, 6, 7, 8, 9]; + let encoding = Encoding { + format: Format::Dwarf32, + version: 5, + address_size: 4, + }; + let mut expected_unit = UnitHeader { + encoding, + unit_length: 0, + unit_type: UnitType::Skeleton(DwoId(0x0706_5040_0302_1000)), + debug_abbrev_offset: DebugAbbrevOffset(0x0807_0605), + unit_offset: DebugInfoOffset(0).into(), + entries_buf: EndianSlice::new(expected_rest, LittleEndian), + }; + let section = Section::with_endian(Endian::Little) + .unit(&mut expected_unit) + .append_bytes(expected_rest); + let buf = section.get_contents().unwrap(); + let rest = &mut EndianSlice::new(&buf, LittleEndian); + + assert_eq!( + parse_unit_header(rest, DebugInfoOffset(0).into()), + Ok(expected_unit) + ); + assert_eq!(*rest, EndianSlice::new(expected_rest, LittleEndian)); + } + + #[test] + #[cfg(target_pointer_width = "64")] + fn test_parse_v5_skeleton_unit_header_64_ok() { + let expected_rest = &[1, 2, 3, 4, 5, 6, 7, 8, 9]; + let encoding = Encoding { + format: Format::Dwarf64, + version: 5, + address_size: 8, + }; + let mut expected_unit = UnitHeader { + encoding, + unit_length: 0, + unit_type: UnitType::Skeleton(DwoId(0x0706_5040_0302_1000)), + debug_abbrev_offset: DebugAbbrevOffset(0x0102_0304_0506_0708), + unit_offset: DebugInfoOffset(0).into(), + entries_buf: EndianSlice::new(expected_rest, LittleEndian), + }; + let section = Section::with_endian(Endian::Little) + .unit(&mut expected_unit) + .append_bytes(expected_rest); + let buf = section.get_contents().unwrap(); + let rest = &mut EndianSlice::new(&buf, LittleEndian); + + assert_eq!( + parse_unit_header(rest, DebugInfoOffset(0).into()), + Ok(expected_unit) + ); + assert_eq!(*rest, EndianSlice::new(expected_rest, LittleEndian)); + } + + #[test] + fn test_parse_v5_split_compilation_unit_header_32_ok() { + let expected_rest = &[1, 2, 3, 4, 5, 6, 7, 8, 9]; + let encoding = Encoding { + format: Format::Dwarf32, + version: 5, + address_size: 4, + }; + let mut expected_unit = UnitHeader { + encoding, + unit_length: 0, + unit_type: UnitType::SplitCompilation(DwoId(0x0706_5040_0302_1000)), + debug_abbrev_offset: DebugAbbrevOffset(0x0807_0605), + unit_offset: DebugInfoOffset(0).into(), + entries_buf: EndianSlice::new(expected_rest, LittleEndian), + }; + let section = Section::with_endian(Endian::Little) + .unit(&mut expected_unit) + .append_bytes(expected_rest); + let buf = section.get_contents().unwrap(); + let rest = &mut EndianSlice::new(&buf, LittleEndian); + + assert_eq!( + parse_unit_header(rest, DebugInfoOffset(0).into()), + Ok(expected_unit) + ); + assert_eq!(*rest, EndianSlice::new(expected_rest, LittleEndian)); + } + + #[test] + #[cfg(target_pointer_width = "64")] + fn test_parse_v5_split_compilation_unit_header_64_ok() { + let expected_rest = &[1, 2, 3, 4, 5, 6, 7, 8, 9]; + let encoding = Encoding { + format: Format::Dwarf64, + version: 5, + address_size: 8, + }; + let mut expected_unit = UnitHeader { + encoding, + unit_length: 0, + unit_type: UnitType::SplitCompilation(DwoId(0x0706_5040_0302_1000)), + debug_abbrev_offset: DebugAbbrevOffset(0x0102_0304_0506_0708), + unit_offset: DebugInfoOffset(0).into(), + entries_buf: EndianSlice::new(expected_rest, LittleEndian), + }; + let section = Section::with_endian(Endian::Little) + .unit(&mut expected_unit) + .append_bytes(expected_rest); + let buf = section.get_contents().unwrap(); + let rest = &mut EndianSlice::new(&buf, LittleEndian); + + assert_eq!( + parse_unit_header(rest, DebugInfoOffset(0).into()), + Ok(expected_unit) + ); + assert_eq!(*rest, EndianSlice::new(expected_rest, LittleEndian)); + } + + #[test] + fn test_parse_type_offset_32_ok() { + let buf = [0x12, 0x34, 0x56, 0x78, 0x00]; + let rest = &mut EndianSlice::new(&buf, LittleEndian); + + match parse_type_offset(rest, Format::Dwarf32) { + Ok(offset) => { + assert_eq!(rest.len(), 1); + assert_eq!(UnitOffset(0x7856_3412), offset); + } + otherwise => panic!("Unexpected result: {:?}", otherwise), + } + } + + #[test] + #[cfg(target_pointer_width = "64")] + fn test_parse_type_offset_64_ok() { + let buf = [0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xff, 0x00]; + let rest = &mut EndianSlice::new(&buf, LittleEndian); + + match parse_type_offset(rest, Format::Dwarf64) { + Ok(offset) => { + assert_eq!(rest.len(), 1); + assert_eq!(UnitOffset(0xffde_bc9a_7856_3412), offset); + } + otherwise => panic!("Unexpected result: {:?}", otherwise), + } + } + + #[test] + fn test_parse_type_offset_incomplete() { + // Need at least 4 bytes. + let buf = [0xff, 0xff, 0xff]; + let rest = &mut EndianSlice::new(&buf, LittleEndian); + + match parse_type_offset(rest, Format::Dwarf32) { + Err(Error::UnexpectedEof(_)) => assert!(true), + otherwise => panic!("Unexpected result: {:?}", otherwise), + }; + } + + #[test] + fn test_parse_type_unit_header_32_ok() { + let expected_rest = &[1, 2, 3, 4, 5, 6, 7, 8, 9]; + let encoding = Encoding { + format: Format::Dwarf32, + version: 4, + address_size: 8, + }; + let mut expected_unit = UnitHeader { + encoding, + unit_length: 0, + unit_type: UnitType::Type { + type_signature: DebugTypeSignature(0xdead_beef_dead_beef), + type_offset: UnitOffset(0x7856_3412), + }, + debug_abbrev_offset: DebugAbbrevOffset(0x0807_0605), + unit_offset: DebugTypesOffset(0).into(), + entries_buf: EndianSlice::new(expected_rest, LittleEndian), + }; + let section = Section::with_endian(Endian::Little) + .unit(&mut expected_unit) + .append_bytes(expected_rest); + let buf = section.get_contents().unwrap(); + let rest = &mut EndianSlice::new(&buf, LittleEndian); + + assert_eq!( + parse_unit_header(rest, DebugTypesOffset(0).into()), + Ok(expected_unit) + ); + assert_eq!(*rest, EndianSlice::new(expected_rest, LittleEndian)); + } + + #[test] + #[cfg(target_pointer_width = "64")] + fn test_parse_type_unit_header_64_ok() { + let expected_rest = &[1, 2, 3, 4, 5, 6, 7, 8, 9]; + let encoding = Encoding { + format: Format::Dwarf64, + version: 4, + address_size: 8, + }; + let mut expected_unit = UnitHeader { + encoding, + unit_length: 0, + unit_type: UnitType::Type { + type_signature: DebugTypeSignature(0xdead_beef_dead_beef), + type_offset: UnitOffset(0x7856_3412_7856_3412), + }, + debug_abbrev_offset: DebugAbbrevOffset(0x0807_0605), + unit_offset: DebugTypesOffset(0).into(), + entries_buf: EndianSlice::new(expected_rest, LittleEndian), + }; + let section = Section::with_endian(Endian::Little) + .unit(&mut expected_unit) + .append_bytes(expected_rest); + let buf = section.get_contents().unwrap(); + let rest = &mut EndianSlice::new(&buf, LittleEndian); + + assert_eq!( + parse_unit_header(rest, DebugTypesOffset(0).into()), + Ok(expected_unit) + ); + assert_eq!(*rest, EndianSlice::new(expected_rest, LittleEndian)); + } + + #[test] + fn test_parse_v5_type_unit_header_32_ok() { + let expected_rest = &[1, 2, 3, 4, 5, 6, 7, 8, 9]; + let encoding = Encoding { + format: Format::Dwarf32, + version: 5, + address_size: 8, + }; + let mut expected_unit = UnitHeader { + encoding, + unit_length: 0, + unit_type: UnitType::Type { + type_signature: DebugTypeSignature(0xdead_beef_dead_beef), + type_offset: UnitOffset(0x7856_3412), + }, + debug_abbrev_offset: DebugAbbrevOffset(0x0807_0605), + unit_offset: DebugInfoOffset(0).into(), + entries_buf: EndianSlice::new(expected_rest, LittleEndian), + }; + let section = Section::with_endian(Endian::Little) + .unit(&mut expected_unit) + .append_bytes(expected_rest); + let buf = section.get_contents().unwrap(); + let rest = &mut EndianSlice::new(&buf, LittleEndian); + + assert_eq!( + parse_unit_header(rest, DebugInfoOffset(0).into()), + Ok(expected_unit) + ); + assert_eq!(*rest, EndianSlice::new(expected_rest, LittleEndian)); + } + + #[test] + #[cfg(target_pointer_width = "64")] + fn test_parse_v5_type_unit_header_64_ok() { + let expected_rest = &[1, 2, 3, 4, 5, 6, 7, 8, 9]; + let encoding = Encoding { + format: Format::Dwarf64, + version: 5, + address_size: 8, + }; + let mut expected_unit = UnitHeader { + encoding, + unit_length: 0, + unit_type: UnitType::Type { + type_signature: DebugTypeSignature(0xdead_beef_dead_beef), + type_offset: UnitOffset(0x7856_3412_7856_3412), + }, + debug_abbrev_offset: DebugAbbrevOffset(0x0807_0605), + unit_offset: DebugInfoOffset(0).into(), + entries_buf: EndianSlice::new(expected_rest, LittleEndian), + }; + let section = Section::with_endian(Endian::Little) + .unit(&mut expected_unit) + .append_bytes(expected_rest); + let buf = section.get_contents().unwrap(); + let rest = &mut EndianSlice::new(&buf, LittleEndian); + + assert_eq!( + parse_unit_header(rest, DebugInfoOffset(0).into()), + Ok(expected_unit) + ); + assert_eq!(*rest, EndianSlice::new(expected_rest, LittleEndian)); + } + + #[test] + fn test_parse_v5_split_type_unit_header_32_ok() { + let expected_rest = &[1, 2, 3, 4, 5, 6, 7, 8, 9]; + let encoding = Encoding { + format: Format::Dwarf32, + version: 5, + address_size: 8, + }; + let mut expected_unit = UnitHeader { + encoding, + unit_length: 0, + unit_type: UnitType::SplitType { + type_signature: DebugTypeSignature(0xdead_beef_dead_beef), + type_offset: UnitOffset(0x7856_3412), + }, + debug_abbrev_offset: DebugAbbrevOffset(0x0807_0605), + unit_offset: DebugInfoOffset(0).into(), + entries_buf: EndianSlice::new(expected_rest, LittleEndian), + }; + let section = Section::with_endian(Endian::Little) + .unit(&mut expected_unit) + .append_bytes(expected_rest); + let buf = section.get_contents().unwrap(); + let rest = &mut EndianSlice::new(&buf, LittleEndian); + + assert_eq!( + parse_unit_header(rest, DebugInfoOffset(0).into()), + Ok(expected_unit) + ); + assert_eq!(*rest, EndianSlice::new(expected_rest, LittleEndian)); + } + + #[test] + #[cfg(target_pointer_width = "64")] + fn test_parse_v5_split_type_unit_header_64_ok() { + let expected_rest = &[1, 2, 3, 4, 5, 6, 7, 8, 9]; + let encoding = Encoding { + format: Format::Dwarf64, + version: 5, + address_size: 8, + }; + let mut expected_unit = UnitHeader { + encoding, + unit_length: 0, + unit_type: UnitType::SplitType { + type_signature: DebugTypeSignature(0xdead_beef_dead_beef), + type_offset: UnitOffset(0x7856_3412_7856_3412), + }, + debug_abbrev_offset: DebugAbbrevOffset(0x0807_0605), + unit_offset: DebugInfoOffset(0).into(), + entries_buf: EndianSlice::new(expected_rest, LittleEndian), + }; + let section = Section::with_endian(Endian::Little) + .unit(&mut expected_unit) + .append_bytes(expected_rest); + let buf = section.get_contents().unwrap(); + let rest = &mut EndianSlice::new(&buf, LittleEndian); + + assert_eq!( + parse_unit_header(rest, DebugInfoOffset(0).into()), + Ok(expected_unit) + ); + assert_eq!(*rest, EndianSlice::new(expected_rest, LittleEndian)); + } + + fn section_contents<F>(f: F) -> Vec<u8> + where + F: Fn(Section) -> Section, + { + f(Section::with_endian(Endian::Little)) + .get_contents() + .unwrap() + } + + #[test] + fn test_attribute_value() { + let mut unit = test_parse_attribute_unit_default(); + let endian = unit.entries_buf.endian(); + + let block_data = &[1, 2, 3, 4]; + let buf = section_contents(|s| s.uleb(block_data.len() as u64).append_bytes(block_data)); + let block = EndianSlice::new(&buf, endian); + + let buf = section_contents(|s| s.L32(0x0102_0304)); + let data4 = EndianSlice::new(&buf, endian); + + let buf = section_contents(|s| s.L64(0x0102_0304_0506_0708)); + let data8 = EndianSlice::new(&buf, endian); + + let tests = [ + ( + Format::Dwarf32, + 2, + constants::DW_AT_data_member_location, + constants::DW_FORM_block, + block, + AttributeValue::Block(EndianSlice::new(block_data, endian)), + AttributeValue::Exprloc(Expression(EndianSlice::new(block_data, endian))), + ), + ( + Format::Dwarf32, + 2, + constants::DW_AT_data_member_location, + constants::DW_FORM_data4, + data4, + AttributeValue::SecOffset(0x0102_0304), + AttributeValue::LocationListsRef(LocationListsOffset(0x0102_0304)), + ), + ( + Format::Dwarf64, + 2, + constants::DW_AT_data_member_location, + constants::DW_FORM_data4, + data4, + AttributeValue::Data4(0x0102_0304), + AttributeValue::Udata(0x0102_0304), + ), + ( + Format::Dwarf32, + 4, + constants::DW_AT_data_member_location, + constants::DW_FORM_data4, + data4, + AttributeValue::Data4(0x0102_0304), + AttributeValue::Udata(0x0102_0304), + ), + ( + Format::Dwarf32, + 2, + constants::DW_AT_data_member_location, + constants::DW_FORM_data8, + data8, + AttributeValue::Data8(0x0102_0304_0506_0708), + AttributeValue::Udata(0x0102_0304_0506_0708), + ), + #[cfg(target_pointer_width = "64")] + ( + Format::Dwarf64, + 2, + constants::DW_AT_data_member_location, + constants::DW_FORM_data8, + data8, + AttributeValue::SecOffset(0x0102_0304_0506_0708), + AttributeValue::LocationListsRef(LocationListsOffset(0x0102_0304_0506_0708)), + ), + ( + Format::Dwarf64, + 4, + constants::DW_AT_data_member_location, + constants::DW_FORM_data8, + data8, + AttributeValue::Data8(0x0102_0304_0506_0708), + AttributeValue::Udata(0x0102_0304_0506_0708), + ), + ( + Format::Dwarf32, + 4, + constants::DW_AT_location, + constants::DW_FORM_data4, + data4, + AttributeValue::SecOffset(0x0102_0304), + AttributeValue::LocationListsRef(LocationListsOffset(0x0102_0304)), + ), + #[cfg(target_pointer_width = "64")] + ( + Format::Dwarf64, + 4, + constants::DW_AT_location, + constants::DW_FORM_data8, + data8, + AttributeValue::SecOffset(0x0102_0304_0506_0708), + AttributeValue::LocationListsRef(LocationListsOffset(0x0102_0304_0506_0708)), + ), + ( + Format::Dwarf32, + 4, + constants::DW_AT_str_offsets_base, + constants::DW_FORM_sec_offset, + data4, + AttributeValue::SecOffset(0x0102_0304), + AttributeValue::DebugStrOffsetsBase(DebugStrOffsetsBase(0x0102_0304)), + ), + ( + Format::Dwarf32, + 4, + constants::DW_AT_stmt_list, + constants::DW_FORM_sec_offset, + data4, + AttributeValue::SecOffset(0x0102_0304), + AttributeValue::DebugLineRef(DebugLineOffset(0x0102_0304)), + ), + ( + Format::Dwarf32, + 4, + constants::DW_AT_addr_base, + constants::DW_FORM_sec_offset, + data4, + AttributeValue::SecOffset(0x0102_0304), + AttributeValue::DebugAddrBase(DebugAddrBase(0x0102_0304)), + ), + ( + Format::Dwarf32, + 4, + constants::DW_AT_rnglists_base, + constants::DW_FORM_sec_offset, + data4, + AttributeValue::SecOffset(0x0102_0304), + AttributeValue::DebugRngListsBase(DebugRngListsBase(0x0102_0304)), + ), + ( + Format::Dwarf32, + 4, + constants::DW_AT_loclists_base, + constants::DW_FORM_sec_offset, + data4, + AttributeValue::SecOffset(0x0102_0304), + AttributeValue::DebugLocListsBase(DebugLocListsBase(0x0102_0304)), + ), + ]; + + for test in tests.iter() { + let (format, version, name, form, mut input, expect_raw, expect_value) = *test; + unit.encoding.format = format; + unit.encoding.version = version; + let spec = AttributeSpecification::new(name, form, None); + let attribute = + parse_attribute(&mut input, unit.encoding(), spec).expect("Should parse attribute"); + assert_eq!(attribute.raw_value(), expect_raw); + assert_eq!(attribute.value(), expect_value); + } + } + + #[test] + fn test_attribute_udata_sdata_value() { + let tests: &[( + AttributeValue<EndianSlice<LittleEndian>>, + Option<u64>, + Option<i64>, + )] = &[ + (AttributeValue::Data1(1), Some(1), Some(1)), + ( + AttributeValue::Data1(core::u8::MAX), + Some(u64::from(std::u8::MAX)), + Some(-1), + ), + (AttributeValue::Data2(1), Some(1), Some(1)), + ( + AttributeValue::Data2(core::u16::MAX), + Some(u64::from(std::u16::MAX)), + Some(-1), + ), + (AttributeValue::Data4(1), Some(1), Some(1)), + ( + AttributeValue::Data4(core::u32::MAX), + Some(u64::from(std::u32::MAX)), + Some(-1), + ), + (AttributeValue::Data8(1), Some(1), Some(1)), + ( + AttributeValue::Data8(core::u64::MAX), + Some(core::u64::MAX), + Some(-1), + ), + (AttributeValue::Sdata(1), Some(1), Some(1)), + (AttributeValue::Sdata(-1), None, Some(-1)), + (AttributeValue::Udata(1), Some(1), Some(1)), + (AttributeValue::Udata(1u64 << 63), Some(1u64 << 63), None), + ]; + for test in tests.iter() { + let (value, expect_udata, expect_sdata) = *test; + let attribute = Attribute { + name: DW_AT_data_member_location, + value, + }; + assert_eq!(attribute.udata_value(), expect_udata); + assert_eq!(attribute.sdata_value(), expect_sdata); + } + } + + fn test_parse_attribute_unit<Endian>( + address_size: u8, + format: Format, + endian: Endian, + ) -> UnitHeader<EndianSlice<'static, Endian>> + where + Endian: Endianity, + { + let encoding = Encoding { + format, + version: 4, + address_size, + }; + UnitHeader::new( + encoding, + 7, + UnitType::Compilation, + DebugAbbrevOffset(0x0807_0605), + DebugInfoOffset(0).into(), + EndianSlice::new(&[], endian), + ) + } + + fn test_parse_attribute_unit_default() -> UnitHeader<EndianSlice<'static, LittleEndian>> { + test_parse_attribute_unit(4, Format::Dwarf32, LittleEndian) + } + + fn test_parse_attribute<'input, Endian>( + buf: &'input [u8], + len: usize, + unit: &UnitHeader<EndianSlice<'input, Endian>>, + form: constants::DwForm, + value: AttributeValue<EndianSlice<'input, Endian>>, + ) where + Endian: Endianity, + { + let spec = AttributeSpecification::new(constants::DW_AT_low_pc, form, None); + + let expect = Attribute { + name: constants::DW_AT_low_pc, + value, + }; + + let rest = &mut EndianSlice::new(buf, Endian::default()); + match parse_attribute(rest, unit.encoding(), spec) { + Ok(attr) => { + assert_eq!(attr, expect); + assert_eq!(*rest, EndianSlice::new(&buf[len..], Endian::default())); + if let Some(size) = spec.size(unit) { + assert_eq!(rest.len() + size, buf.len()); + } + } + otherwise => { + assert!(false, "Unexpected parse result = {:#?}", otherwise); + } + }; + } + + #[test] + fn test_parse_attribute_addr() { + let buf = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08]; + let unit = test_parse_attribute_unit(4, Format::Dwarf32, LittleEndian); + let form = constants::DW_FORM_addr; + let value = AttributeValue::Addr(0x0403_0201); + test_parse_attribute(&buf, 4, &unit, form, value); + } + + #[test] + fn test_parse_attribute_addr8() { + let buf = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08]; + let unit = test_parse_attribute_unit(8, Format::Dwarf32, LittleEndian); + let form = constants::DW_FORM_addr; + let value = AttributeValue::Addr(0x0807_0605_0403_0201); + test_parse_attribute(&buf, 8, &unit, form, value); + } + + #[test] + fn test_parse_attribute_block1() { + // Length of data (3), three bytes of data, two bytes of left over input. + let buf = [0x03, 0x09, 0x09, 0x09, 0x00, 0x00]; + let unit = test_parse_attribute_unit_default(); + let form = constants::DW_FORM_block1; + let value = AttributeValue::Block(EndianSlice::new(&buf[1..4], LittleEndian)); + test_parse_attribute(&buf, 4, &unit, form, value); + } + + #[test] + fn test_parse_attribute_block2() { + // Two byte length of data (2), two bytes of data, two bytes of left over input. + let buf = [0x02, 0x00, 0x09, 0x09, 0x00, 0x00]; + let unit = test_parse_attribute_unit_default(); + let form = constants::DW_FORM_block2; + let value = AttributeValue::Block(EndianSlice::new(&buf[2..4], LittleEndian)); + test_parse_attribute(&buf, 4, &unit, form, value); + } + + #[test] + fn test_parse_attribute_block4() { + // Four byte length of data (2), two bytes of data, no left over input. + let buf = [0x02, 0x00, 0x00, 0x00, 0x99, 0x99]; + let unit = test_parse_attribute_unit_default(); + let form = constants::DW_FORM_block4; + let value = AttributeValue::Block(EndianSlice::new(&buf[4..], LittleEndian)); + test_parse_attribute(&buf, 6, &unit, form, value); + } + + #[test] + fn test_parse_attribute_block() { + // LEB length of data (2, one byte), two bytes of data, no left over input. + let buf = [0x02, 0x99, 0x99]; + let unit = test_parse_attribute_unit_default(); + let form = constants::DW_FORM_block; + let value = AttributeValue::Block(EndianSlice::new(&buf[1..], LittleEndian)); + test_parse_attribute(&buf, 3, &unit, form, value); + } + + #[test] + fn test_parse_attribute_data1() { + let buf = [0x03]; + let unit = test_parse_attribute_unit_default(); + let form = constants::DW_FORM_data1; + let value = AttributeValue::Data1(0x03); + test_parse_attribute(&buf, 1, &unit, form, value); + } + + #[test] + fn test_parse_attribute_data2() { + let buf = [0x02, 0x01, 0x0]; + let unit = test_parse_attribute_unit_default(); + let form = constants::DW_FORM_data2; + let value = AttributeValue::Data2(0x0102); + test_parse_attribute(&buf, 2, &unit, form, value); + } + + #[test] + fn test_parse_attribute_data4() { + let buf = [0x01, 0x02, 0x03, 0x04, 0x99, 0x99]; + let unit = test_parse_attribute_unit_default(); + let form = constants::DW_FORM_data4; + let value = AttributeValue::Data4(0x0403_0201); + test_parse_attribute(&buf, 4, &unit, form, value); + } + + #[test] + fn test_parse_attribute_data8() { + let buf = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x99, 0x99]; + let unit = test_parse_attribute_unit_default(); + let form = constants::DW_FORM_data8; + let value = AttributeValue::Data8(0x0807_0605_0403_0201); + test_parse_attribute(&buf, 8, &unit, form, value); + } + + #[test] + fn test_parse_attribute_udata() { + let mut buf = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + + let bytes_written = { + let mut writable = &mut buf[..]; + leb128::write::unsigned(&mut writable, 4097).expect("should write ok") + }; + + let unit = test_parse_attribute_unit_default(); + let form = constants::DW_FORM_udata; + let value = AttributeValue::Udata(4097); + test_parse_attribute(&buf, bytes_written, &unit, form, value); + } + + #[test] + fn test_parse_attribute_sdata() { + let mut buf = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + + let bytes_written = { + let mut writable = &mut buf[..]; + leb128::write::signed(&mut writable, -4097).expect("should write ok") + }; + + let unit = test_parse_attribute_unit_default(); + let form = constants::DW_FORM_sdata; + let value = AttributeValue::Sdata(-4097); + test_parse_attribute(&buf, bytes_written, &unit, form, value); + } + + #[test] + fn test_parse_attribute_exprloc() { + // LEB length of data (2, one byte), two bytes of data, one byte left over input. + let buf = [0x02, 0x99, 0x99, 0x11]; + let unit = test_parse_attribute_unit_default(); + let form = constants::DW_FORM_exprloc; + let value = AttributeValue::Exprloc(Expression(EndianSlice::new(&buf[1..3], LittleEndian))); + test_parse_attribute(&buf, 3, &unit, form, value); + } + + #[test] + fn test_parse_attribute_flag_true() { + let buf = [0x42]; + let unit = test_parse_attribute_unit_default(); + let form = constants::DW_FORM_flag; + let value = AttributeValue::Flag(true); + test_parse_attribute(&buf, 1, &unit, form, value); + } + + #[test] + fn test_parse_attribute_flag_false() { + let buf = [0x00]; + let unit = test_parse_attribute_unit_default(); + let form = constants::DW_FORM_flag; + let value = AttributeValue::Flag(false); + test_parse_attribute(&buf, 1, &unit, form, value); + } + + #[test] + fn test_parse_attribute_flag_present() { + let buf = [0x01, 0x02, 0x03, 0x04]; + let unit = test_parse_attribute_unit_default(); + let form = constants::DW_FORM_flag_present; + let value = AttributeValue::Flag(true); + // DW_FORM_flag_present does not consume any bytes of the input stream. + test_parse_attribute(&buf, 0, &unit, form, value); + } + + #[test] + fn test_parse_attribute_sec_offset_32() { + let buf = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10]; + let unit = test_parse_attribute_unit(4, Format::Dwarf32, LittleEndian); + let form = constants::DW_FORM_sec_offset; + let value = AttributeValue::SecOffset(0x0403_0201); + test_parse_attribute(&buf, 4, &unit, form, value); + } + + #[test] + #[cfg(target_pointer_width = "64")] + fn test_parse_attribute_sec_offset_64() { + let buf = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10]; + let unit = test_parse_attribute_unit(4, Format::Dwarf64, LittleEndian); + let form = constants::DW_FORM_sec_offset; + let value = AttributeValue::SecOffset(0x0807_0605_0403_0201); + test_parse_attribute(&buf, 8, &unit, form, value); + } + + #[test] + fn test_parse_attribute_ref1() { + let buf = [0x03]; + let unit = test_parse_attribute_unit_default(); + let form = constants::DW_FORM_ref1; + let value = AttributeValue::UnitRef(UnitOffset(3)); + test_parse_attribute(&buf, 1, &unit, form, value); + } + + #[test] + fn test_parse_attribute_ref2() { + let buf = [0x02, 0x01, 0x0]; + let unit = test_parse_attribute_unit_default(); + let form = constants::DW_FORM_ref2; + let value = AttributeValue::UnitRef(UnitOffset(258)); + test_parse_attribute(&buf, 2, &unit, form, value); + } + + #[test] + fn test_parse_attribute_ref4() { + let buf = [0x01, 0x02, 0x03, 0x04, 0x99, 0x99]; + let unit = test_parse_attribute_unit_default(); + let form = constants::DW_FORM_ref4; + let value = AttributeValue::UnitRef(UnitOffset(0x0403_0201)); + test_parse_attribute(&buf, 4, &unit, form, value); + } + + #[test] + #[cfg(target_pointer_width = "64")] + fn test_parse_attribute_ref8() { + let buf = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x99, 0x99]; + let unit = test_parse_attribute_unit_default(); + let form = constants::DW_FORM_ref8; + let value = AttributeValue::UnitRef(UnitOffset(0x0807_0605_0403_0201)); + test_parse_attribute(&buf, 8, &unit, form, value); + } + + #[test] + fn test_parse_attribute_ref_sup4() { + let buf = [0x01, 0x02, 0x03, 0x04, 0x99, 0x99]; + let unit = test_parse_attribute_unit_default(); + let form = constants::DW_FORM_ref_sup4; + let value = AttributeValue::DebugInfoRefSup(DebugInfoOffset(0x0403_0201)); + test_parse_attribute(&buf, 4, &unit, form, value); + } + + #[test] + #[cfg(target_pointer_width = "64")] + fn test_parse_attribute_ref_sup8() { + let buf = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x99, 0x99]; + let unit = test_parse_attribute_unit_default(); + let form = constants::DW_FORM_ref_sup8; + let value = AttributeValue::DebugInfoRefSup(DebugInfoOffset(0x0807_0605_0403_0201)); + test_parse_attribute(&buf, 8, &unit, form, value); + } + + #[test] + fn test_parse_attribute_refudata() { + let mut buf = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + + let bytes_written = { + let mut writable = &mut buf[..]; + leb128::write::unsigned(&mut writable, 4097).expect("should write ok") + }; + + let unit = test_parse_attribute_unit_default(); + let form = constants::DW_FORM_ref_udata; + let value = AttributeValue::UnitRef(UnitOffset(4097)); + test_parse_attribute(&buf, bytes_written, &unit, form, value); + } + + #[test] + fn test_parse_attribute_refaddr_32() { + let buf = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x99, 0x99]; + let unit = test_parse_attribute_unit(4, Format::Dwarf32, LittleEndian); + let form = constants::DW_FORM_ref_addr; + let value = AttributeValue::DebugInfoRef(DebugInfoOffset(0x0403_0201)); + test_parse_attribute(&buf, 4, &unit, form, value); + } + + #[test] + #[cfg(target_pointer_width = "64")] + fn test_parse_attribute_refaddr_64() { + let buf = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x99, 0x99]; + let unit = test_parse_attribute_unit(4, Format::Dwarf64, LittleEndian); + let form = constants::DW_FORM_ref_addr; + let value = AttributeValue::DebugInfoRef(DebugInfoOffset(0x0807_0605_0403_0201)); + test_parse_attribute(&buf, 8, &unit, form, value); + } + + #[test] + fn test_parse_attribute_refaddr_version2() { + let buf = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x99, 0x99]; + let mut unit = test_parse_attribute_unit(4, Format::Dwarf32, LittleEndian); + unit.encoding.version = 2; + let form = constants::DW_FORM_ref_addr; + let value = AttributeValue::DebugInfoRef(DebugInfoOffset(0x0403_0201)); + test_parse_attribute(&buf, 4, &unit, form, value); + } + + #[test] + #[cfg(target_pointer_width = "64")] + fn test_parse_attribute_refaddr8_version2() { + let buf = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x99, 0x99]; + let mut unit = test_parse_attribute_unit(8, Format::Dwarf32, LittleEndian); + unit.encoding.version = 2; + let form = constants::DW_FORM_ref_addr; + let value = AttributeValue::DebugInfoRef(DebugInfoOffset(0x0807_0605_0403_0201)); + test_parse_attribute(&buf, 8, &unit, form, value); + } + + #[test] + fn test_parse_attribute_gnu_ref_alt_32() { + let buf = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x99, 0x99]; + let unit = test_parse_attribute_unit(4, Format::Dwarf32, LittleEndian); + let form = constants::DW_FORM_GNU_ref_alt; + let value = AttributeValue::DebugInfoRefSup(DebugInfoOffset(0x0403_0201)); + test_parse_attribute(&buf, 4, &unit, form, value); + } + + #[test] + #[cfg(target_pointer_width = "64")] + fn test_parse_attribute_gnu_ref_alt_64() { + let buf = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x99, 0x99]; + let unit = test_parse_attribute_unit(4, Format::Dwarf64, LittleEndian); + let form = constants::DW_FORM_GNU_ref_alt; + let value = AttributeValue::DebugInfoRefSup(DebugInfoOffset(0x0807_0605_0403_0201)); + test_parse_attribute(&buf, 8, &unit, form, value); + } + + #[test] + fn test_parse_attribute_refsig8() { + let buf = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x99, 0x99]; + let unit = test_parse_attribute_unit_default(); + let form = constants::DW_FORM_ref_sig8; + let value = AttributeValue::DebugTypesRef(DebugTypeSignature(0x0807_0605_0403_0201)); + test_parse_attribute(&buf, 8, &unit, form, value); + } + + #[test] + fn test_parse_attribute_string() { + let buf = [0x01, 0x02, 0x03, 0x04, 0x05, 0x0, 0x99, 0x99]; + let unit = test_parse_attribute_unit_default(); + let form = constants::DW_FORM_string; + let value = AttributeValue::String(EndianSlice::new(&buf[..5], LittleEndian)); + test_parse_attribute(&buf, 6, &unit, form, value); + } + + #[test] + fn test_parse_attribute_strp_32() { + let buf = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x99, 0x99]; + let unit = test_parse_attribute_unit(4, Format::Dwarf32, LittleEndian); + let form = constants::DW_FORM_strp; + let value = AttributeValue::DebugStrRef(DebugStrOffset(0x0403_0201)); + test_parse_attribute(&buf, 4, &unit, form, value); + } + + #[test] + #[cfg(target_pointer_width = "64")] + fn test_parse_attribute_strp_64() { + let buf = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x99, 0x99]; + let unit = test_parse_attribute_unit(4, Format::Dwarf64, LittleEndian); + let form = constants::DW_FORM_strp; + let value = AttributeValue::DebugStrRef(DebugStrOffset(0x0807_0605_0403_0201)); + test_parse_attribute(&buf, 8, &unit, form, value); + } + + #[test] + fn test_parse_attribute_strp_sup_32() { + let buf = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x99, 0x99]; + let unit = test_parse_attribute_unit(4, Format::Dwarf32, LittleEndian); + let form = constants::DW_FORM_strp_sup; + let value = AttributeValue::DebugStrRefSup(DebugStrOffset(0x0403_0201)); + test_parse_attribute(&buf, 4, &unit, form, value); + } + + #[test] + #[cfg(target_pointer_width = "64")] + fn test_parse_attribute_strp_sup_64() { + let buf = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x99, 0x99]; + let unit = test_parse_attribute_unit(4, Format::Dwarf64, LittleEndian); + let form = constants::DW_FORM_strp_sup; + let value = AttributeValue::DebugStrRefSup(DebugStrOffset(0x0807_0605_0403_0201)); + test_parse_attribute(&buf, 8, &unit, form, value); + } + + #[test] + fn test_parse_attribute_gnu_strp_alt_32() { + let buf = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x99, 0x99]; + let unit = test_parse_attribute_unit(4, Format::Dwarf32, LittleEndian); + let form = constants::DW_FORM_GNU_strp_alt; + let value = AttributeValue::DebugStrRefSup(DebugStrOffset(0x0403_0201)); + test_parse_attribute(&buf, 4, &unit, form, value); + } + + #[test] + #[cfg(target_pointer_width = "64")] + fn test_parse_attribute_gnu_strp_alt_64() { + let buf = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x99, 0x99]; + let unit = test_parse_attribute_unit(4, Format::Dwarf64, LittleEndian); + let form = constants::DW_FORM_GNU_strp_alt; + let value = AttributeValue::DebugStrRefSup(DebugStrOffset(0x0807_0605_0403_0201)); + test_parse_attribute(&buf, 8, &unit, form, value); + } + + #[test] + fn test_parse_attribute_strx() { + let mut buf = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + + let bytes_written = { + let mut writable = &mut buf[..]; + leb128::write::unsigned(&mut writable, 4097).expect("should write ok") + }; + + let unit = test_parse_attribute_unit_default(); + let form = constants::DW_FORM_strx; + let value = AttributeValue::DebugStrOffsetsIndex(DebugStrOffsetsIndex(4097)); + test_parse_attribute(&buf, bytes_written, &unit, form, value); + } + + #[test] + fn test_parse_attribute_strx1() { + let buf = [0x01, 0x99, 0x99]; + let unit = test_parse_attribute_unit(4, Format::Dwarf64, LittleEndian); + let form = constants::DW_FORM_strx1; + let value = AttributeValue::DebugStrOffsetsIndex(DebugStrOffsetsIndex(0x01)); + test_parse_attribute(&buf, 1, &unit, form, value); + } + + #[test] + fn test_parse_attribute_strx2() { + let buf = [0x01, 0x02, 0x99, 0x99]; + let unit = test_parse_attribute_unit(4, Format::Dwarf64, LittleEndian); + let form = constants::DW_FORM_strx2; + let value = AttributeValue::DebugStrOffsetsIndex(DebugStrOffsetsIndex(0x0201)); + test_parse_attribute(&buf, 2, &unit, form, value); + } + + #[test] + fn test_parse_attribute_strx3() { + let buf = [0x01, 0x02, 0x03, 0x99, 0x99]; + let unit = test_parse_attribute_unit(4, Format::Dwarf64, LittleEndian); + let form = constants::DW_FORM_strx3; + let value = AttributeValue::DebugStrOffsetsIndex(DebugStrOffsetsIndex(0x03_0201)); + test_parse_attribute(&buf, 3, &unit, form, value); + } + + #[test] + fn test_parse_attribute_strx4() { + let buf = [0x01, 0x02, 0x03, 0x04, 0x99, 0x99]; + let unit = test_parse_attribute_unit(4, Format::Dwarf64, LittleEndian); + let form = constants::DW_FORM_strx4; + let value = AttributeValue::DebugStrOffsetsIndex(DebugStrOffsetsIndex(0x0403_0201)); + test_parse_attribute(&buf, 4, &unit, form, value); + } + + #[test] + fn test_parse_attribute_addrx() { + let mut buf = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + + let bytes_written = { + let mut writable = &mut buf[..]; + leb128::write::unsigned(&mut writable, 4097).expect("should write ok") + }; + + let unit = test_parse_attribute_unit_default(); + let form = constants::DW_FORM_addrx; + let value = AttributeValue::DebugAddrIndex(DebugAddrIndex(4097)); + test_parse_attribute(&buf, bytes_written, &unit, form, value); + } + + #[test] + fn test_parse_attribute_addrx1() { + let buf = [0x01, 0x99, 0x99]; + let unit = test_parse_attribute_unit(4, Format::Dwarf64, LittleEndian); + let form = constants::DW_FORM_addrx1; + let value = AttributeValue::DebugAddrIndex(DebugAddrIndex(0x01)); + test_parse_attribute(&buf, 1, &unit, form, value); + } + + #[test] + fn test_parse_attribute_addrx2() { + let buf = [0x01, 0x02, 0x99, 0x99]; + let unit = test_parse_attribute_unit(4, Format::Dwarf64, LittleEndian); + let form = constants::DW_FORM_addrx2; + let value = AttributeValue::DebugAddrIndex(DebugAddrIndex(0x0201)); + test_parse_attribute(&buf, 2, &unit, form, value); + } + + #[test] + fn test_parse_attribute_addrx3() { + let buf = [0x01, 0x02, 0x03, 0x99, 0x99]; + let unit = test_parse_attribute_unit(4, Format::Dwarf64, LittleEndian); + let form = constants::DW_FORM_addrx3; + let value = AttributeValue::DebugAddrIndex(DebugAddrIndex(0x03_0201)); + test_parse_attribute(&buf, 3, &unit, form, value); + } + + #[test] + fn test_parse_attribute_addrx4() { + let buf = [0x01, 0x02, 0x03, 0x04, 0x99, 0x99]; + let unit = test_parse_attribute_unit(4, Format::Dwarf64, LittleEndian); + let form = constants::DW_FORM_addrx4; + let value = AttributeValue::DebugAddrIndex(DebugAddrIndex(0x0403_0201)); + test_parse_attribute(&buf, 4, &unit, form, value); + } + + #[test] + fn test_parse_attribute_loclistx() { + let mut buf = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + + let bytes_written = { + let mut writable = &mut buf[..]; + leb128::write::unsigned(&mut writable, 4097).expect("should write ok") + }; + + let unit = test_parse_attribute_unit_default(); + let form = constants::DW_FORM_loclistx; + let value = AttributeValue::DebugLocListsIndex(DebugLocListsIndex(4097)); + test_parse_attribute(&buf, bytes_written, &unit, form, value); + } + + #[test] + fn test_parse_attribute_rnglistx() { + let mut buf = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + + let bytes_written = { + let mut writable = &mut buf[..]; + leb128::write::unsigned(&mut writable, 4097).expect("should write ok") + }; + + let unit = test_parse_attribute_unit_default(); + let form = constants::DW_FORM_rnglistx; + let value = AttributeValue::DebugRngListsIndex(DebugRngListsIndex(4097)); + test_parse_attribute(&buf, bytes_written, &unit, form, value); + } + + #[test] + fn test_parse_attribute_indirect() { + let mut buf = [0; 100]; + + let bytes_written = { + let mut writable = &mut buf[..]; + leb128::write::unsigned(&mut writable, constants::DW_FORM_udata.0.into()) + .expect("should write udata") + + leb128::write::unsigned(&mut writable, 9_999_999).expect("should write value") + }; + + let unit = test_parse_attribute_unit_default(); + let form = constants::DW_FORM_indirect; + let value = AttributeValue::Udata(9_999_999); + test_parse_attribute(&buf, bytes_written, &unit, form, value); + } + + #[test] + fn test_parse_attribute_indirect_implicit_const() { + let encoding = Encoding { + format: Format::Dwarf32, + version: 4, + address_size: 4, + }; + let mut buf = [0; 100]; + let mut writable = &mut buf[..]; + leb128::write::unsigned(&mut writable, constants::DW_FORM_implicit_const.0.into()) + .expect("should write implicit_const"); + + let input = &mut EndianSlice::new(&buf, LittleEndian); + let spec = + AttributeSpecification::new(constants::DW_AT_low_pc, constants::DW_FORM_indirect, None); + assert_eq!( + parse_attribute(input, encoding, spec), + Err(Error::InvalidImplicitConst) + ); + } + + #[test] + fn test_attrs_iter() { + let encoding = Encoding { + format: Format::Dwarf32, + version: 4, + address_size: 4, + }; + let unit = UnitHeader::new( + encoding, + 7, + UnitType::Compilation, + DebugAbbrevOffset(0x0807_0605), + DebugInfoOffset(0).into(), + EndianSlice::new(&[], LittleEndian), + ); + + let abbrev = Abbreviation::new( + 42, + constants::DW_TAG_subprogram, + constants::DW_CHILDREN_yes, + vec![ + AttributeSpecification::new(constants::DW_AT_name, constants::DW_FORM_string, None), + AttributeSpecification::new(constants::DW_AT_low_pc, constants::DW_FORM_addr, None), + AttributeSpecification::new( + constants::DW_AT_high_pc, + constants::DW_FORM_addr, + None, + ), + ] + .into(), + ); + + // "foo", 42, 1337, 4 dangling bytes of 0xaa where children would be + let buf = [ + 0x66, 0x6f, 0x6f, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x39, 0x05, 0x00, 0x00, 0xaa, 0xaa, + 0xaa, 0xaa, + ]; + + let entry = DebuggingInformationEntry { + offset: UnitOffset(0), + attrs_slice: EndianSlice::new(&buf, LittleEndian), + attrs_len: Cell::new(None), + abbrev: &abbrev, + unit: &unit, + }; + + let mut attrs = AttrsIter { + input: EndianSlice::new(&buf, LittleEndian), + attributes: abbrev.attributes(), + entry: &entry, + }; + + match attrs.next() { + Ok(Some(attr)) => { + assert_eq!( + attr, + Attribute { + name: constants::DW_AT_name, + value: AttributeValue::String(EndianSlice::new(b"foo", LittleEndian)), + } + ); + } + otherwise => { + assert!(false, "Unexpected parse result = {:#?}", otherwise); + } + } + + assert!(entry.attrs_len.get().is_none()); + + match attrs.next() { + Ok(Some(attr)) => { + assert_eq!( + attr, + Attribute { + name: constants::DW_AT_low_pc, + value: AttributeValue::Addr(0x2a), + } + ); + } + otherwise => { + assert!(false, "Unexpected parse result = {:#?}", otherwise); + } + } + + assert!(entry.attrs_len.get().is_none()); + + match attrs.next() { + Ok(Some(attr)) => { + assert_eq!( + attr, + Attribute { + name: constants::DW_AT_high_pc, + value: AttributeValue::Addr(0x539), + } + ); + } + otherwise => { + assert!(false, "Unexpected parse result = {:#?}", otherwise); + } + } + + assert!(entry.attrs_len.get().is_none()); + + assert!(attrs.next().expect("should parse next").is_none()); + assert!(entry.attrs_len.get().is_some()); + assert_eq!( + entry.attrs_len.get().expect("should have entry.attrs_len"), + buf.len() - 4 + ) + } + + #[test] + fn test_attrs_iter_incomplete() { + let encoding = Encoding { + format: Format::Dwarf32, + version: 4, + address_size: 4, + }; + let unit = UnitHeader::new( + encoding, + 7, + UnitType::Compilation, + DebugAbbrevOffset(0x0807_0605), + DebugInfoOffset(0).into(), + EndianSlice::new(&[], LittleEndian), + ); + + let abbrev = Abbreviation::new( + 42, + constants::DW_TAG_subprogram, + constants::DW_CHILDREN_yes, + vec![ + AttributeSpecification::new(constants::DW_AT_name, constants::DW_FORM_string, None), + AttributeSpecification::new(constants::DW_AT_low_pc, constants::DW_FORM_addr, None), + AttributeSpecification::new( + constants::DW_AT_high_pc, + constants::DW_FORM_addr, + None, + ), + ] + .into(), + ); + + // "foo" + let buf = [0x66, 0x6f, 0x6f, 0x00]; + + let entry = DebuggingInformationEntry { + offset: UnitOffset(0), + attrs_slice: EndianSlice::new(&buf, LittleEndian), + attrs_len: Cell::new(None), + abbrev: &abbrev, + unit: &unit, + }; + + let mut attrs = AttrsIter { + input: EndianSlice::new(&buf, LittleEndian), + attributes: abbrev.attributes(), + entry: &entry, + }; + + match attrs.next() { + Ok(Some(attr)) => { + assert_eq!( + attr, + Attribute { + name: constants::DW_AT_name, + value: AttributeValue::String(EndianSlice::new(b"foo", LittleEndian)), + } + ); + } + otherwise => { + assert!(false, "Unexpected parse result = {:#?}", otherwise); + } + } + + assert!(entry.attrs_len.get().is_none()); + + // Return error for incomplete attribute. + assert!(attrs.next().is_err()); + assert!(entry.attrs_len.get().is_none()); + + // Return error for all subsequent calls. + assert!(attrs.next().is_err()); + assert!(attrs.next().is_err()); + assert!(attrs.next().is_err()); + assert!(attrs.next().is_err()); + assert!(entry.attrs_len.get().is_none()); + } + + fn assert_entry_name<Endian>(entry: &DebuggingInformationEntry<EndianSlice<Endian>>, name: &str) + where + Endian: Endianity, + { + let value = entry + .attr_value(constants::DW_AT_name) + .expect("Should have parsed the name attribute") + .expect("Should have found the name attribute"); + + assert_eq!( + value, + AttributeValue::String(EndianSlice::new(name.as_bytes(), Endian::default())) + ); + } + + fn assert_current_name<Endian>(cursor: &EntriesCursor<EndianSlice<Endian>>, name: &str) + where + Endian: Endianity, + { + let entry = cursor.current().expect("Should have an entry result"); + assert_entry_name(entry, name); + } + + fn assert_next_entry<Endian>(cursor: &mut EntriesCursor<EndianSlice<Endian>>, name: &str) + where + Endian: Endianity, + { + cursor + .next_entry() + .expect("Should parse next entry") + .expect("Should have an entry"); + assert_current_name(cursor, name); + } + + fn assert_next_entry_null<Endian>(cursor: &mut EntriesCursor<EndianSlice<Endian>>) + where + Endian: Endianity, + { + cursor + .next_entry() + .expect("Should parse next entry") + .expect("Should have an entry"); + assert!(cursor.current().is_none()); + } + + fn assert_next_dfs<Endian>( + cursor: &mut EntriesCursor<EndianSlice<Endian>>, + name: &str, + depth: isize, + ) where + Endian: Endianity, + { + { + let (val, entry) = cursor + .next_dfs() + .expect("Should parse next dfs") + .expect("Should not be done with traversal"); + assert_eq!(val, depth); + assert_entry_name(entry, name); + } + assert_current_name(cursor, name); + } + + fn assert_next_sibling<Endian>(cursor: &mut EntriesCursor<EndianSlice<Endian>>, name: &str) + where + Endian: Endianity, + { + { + let entry = cursor + .next_sibling() + .expect("Should parse next sibling") + .expect("Should not be done with traversal"); + assert_entry_name(entry, name); + } + assert_current_name(cursor, name); + } + + fn assert_valid_sibling_ptr<Endian>(cursor: &EntriesCursor<EndianSlice<Endian>>) + where + Endian: Endianity, + { + let sibling_ptr = cursor + .current() + .expect("Should have current entry") + .attr_value(constants::DW_AT_sibling); + match sibling_ptr { + Ok(Some(AttributeValue::UnitRef(offset))) => { + cursor + .unit + .range_from(offset..) + .expect("Sibling offset should be valid"); + } + _ => panic!("Invalid sibling pointer {:?}", sibling_ptr), + } + } + + fn entries_cursor_tests_abbrev_buf() -> Vec<u8> { + #[rustfmt::skip] + let section = Section::with_endian(Endian::Little) + .abbrev(1, DW_TAG_subprogram, DW_CHILDREN_yes) + .abbrev_attr(DW_AT_name, DW_FORM_string) + .abbrev_attr_null() + .abbrev_null(); + section.get_contents().unwrap() + } + + fn entries_cursor_tests_debug_info_buf() -> Vec<u8> { + #[rustfmt::skip] + let section = Section::with_endian(Endian::Little) + .die(1, |s| s.attr_string("001")) + .die(1, |s| s.attr_string("002")) + .die(1, |s| s.attr_string("003")) + .die_null() + .die_null() + .die(1, |s| s.attr_string("004")) + .die(1, |s| s.attr_string("005")) + .die_null() + .die(1, |s| s.attr_string("006")) + .die_null() + .die_null() + .die(1, |s| s.attr_string("007")) + .die(1, |s| s.attr_string("008")) + .die(1, |s| s.attr_string("009")) + .die_null() + .die_null() + .die_null() + .die(1, |s| s.attr_string("010")) + .die_null() + .die_null(); + let entries_buf = section.get_contents().unwrap(); + + let encoding = Encoding { + format: Format::Dwarf32, + version: 4, + address_size: 4, + }; + let mut unit = UnitHeader { + encoding, + unit_length: 0, + unit_type: UnitType::Compilation, + debug_abbrev_offset: DebugAbbrevOffset(0), + unit_offset: DebugInfoOffset(0).into(), + entries_buf: EndianSlice::new(&entries_buf, LittleEndian), + }; + let section = Section::with_endian(Endian::Little).unit(&mut unit); + section.get_contents().unwrap() + } + + #[test] + fn test_cursor_next_entry_incomplete() { + #[rustfmt::skip] + let section = Section::with_endian(Endian::Little) + .die(1, |s| s.attr_string("001")) + .die(1, |s| s.attr_string("002")) + .die(1, |s| s); + let entries_buf = section.get_contents().unwrap(); + + let encoding = Encoding { + format: Format::Dwarf32, + version: 4, + address_size: 4, + }; + let mut unit = UnitHeader { + encoding, + unit_length: 0, + unit_type: UnitType::Compilation, + debug_abbrev_offset: DebugAbbrevOffset(0), + unit_offset: DebugInfoOffset(0).into(), + entries_buf: EndianSlice::new(&entries_buf, LittleEndian), + }; + let section = Section::with_endian(Endian::Little).unit(&mut unit); + let info_buf = §ion.get_contents().unwrap(); + let debug_info = DebugInfo::new(info_buf, LittleEndian); + + let unit = debug_info + .units() + .next() + .expect("should have a unit result") + .expect("and it should be ok"); + + let abbrevs_buf = &entries_cursor_tests_abbrev_buf(); + let debug_abbrev = DebugAbbrev::new(abbrevs_buf, LittleEndian); + + let abbrevs = unit + .abbreviations(&debug_abbrev) + .expect("Should parse abbreviations"); + + let mut cursor = unit.entries(&abbrevs); + + assert_next_entry(&mut cursor, "001"); + assert_next_entry(&mut cursor, "002"); + + { + // Entry code is present, but none of the attributes. + cursor + .next_entry() + .expect("Should parse next entry") + .expect("Should have an entry"); + let entry = cursor.current().expect("Should have an entry result"); + assert!(entry.attrs().next().is_err()); + } + + assert!(cursor.next_entry().is_err()); + assert!(cursor.next_entry().is_err()); + } + + #[test] + fn test_cursor_next_entry() { + let info_buf = &entries_cursor_tests_debug_info_buf(); + let debug_info = DebugInfo::new(info_buf, LittleEndian); + + let unit = debug_info + .units() + .next() + .expect("should have a unit result") + .expect("and it should be ok"); + + let abbrevs_buf = &entries_cursor_tests_abbrev_buf(); + let debug_abbrev = DebugAbbrev::new(abbrevs_buf, LittleEndian); + + let abbrevs = unit + .abbreviations(&debug_abbrev) + .expect("Should parse abbreviations"); + + let mut cursor = unit.entries(&abbrevs); + + assert_next_entry(&mut cursor, "001"); + assert_next_entry(&mut cursor, "002"); + assert_next_entry(&mut cursor, "003"); + assert_next_entry_null(&mut cursor); + assert_next_entry_null(&mut cursor); + assert_next_entry(&mut cursor, "004"); + assert_next_entry(&mut cursor, "005"); + assert_next_entry_null(&mut cursor); + assert_next_entry(&mut cursor, "006"); + assert_next_entry_null(&mut cursor); + assert_next_entry_null(&mut cursor); + assert_next_entry(&mut cursor, "007"); + assert_next_entry(&mut cursor, "008"); + assert_next_entry(&mut cursor, "009"); + assert_next_entry_null(&mut cursor); + assert_next_entry_null(&mut cursor); + assert_next_entry_null(&mut cursor); + assert_next_entry(&mut cursor, "010"); + assert_next_entry_null(&mut cursor); + assert_next_entry_null(&mut cursor); + + assert!(cursor + .next_entry() + .expect("Should parse next entry") + .is_none()); + assert!(cursor.current().is_none()); + } + + #[test] + fn test_cursor_next_dfs() { + let info_buf = &entries_cursor_tests_debug_info_buf(); + let debug_info = DebugInfo::new(info_buf, LittleEndian); + + let unit = debug_info + .units() + .next() + .expect("should have a unit result") + .expect("and it should be ok"); + + let abbrevs_buf = &entries_cursor_tests_abbrev_buf(); + let debug_abbrev = DebugAbbrev::new(abbrevs_buf, LittleEndian); + + let abbrevs = unit + .abbreviations(&debug_abbrev) + .expect("Should parse abbreviations"); + + let mut cursor = unit.entries(&abbrevs); + + assert_next_dfs(&mut cursor, "001", 0); + assert_next_dfs(&mut cursor, "002", 1); + assert_next_dfs(&mut cursor, "003", 1); + assert_next_dfs(&mut cursor, "004", -1); + assert_next_dfs(&mut cursor, "005", 1); + assert_next_dfs(&mut cursor, "006", 0); + assert_next_dfs(&mut cursor, "007", -1); + assert_next_dfs(&mut cursor, "008", 1); + assert_next_dfs(&mut cursor, "009", 1); + assert_next_dfs(&mut cursor, "010", -2); + + assert!(cursor.next_dfs().expect("Should parse next dfs").is_none()); + assert!(cursor.current().is_none()); + } + + #[test] + fn test_cursor_next_sibling_no_sibling_ptr() { + let info_buf = &entries_cursor_tests_debug_info_buf(); + let debug_info = DebugInfo::new(info_buf, LittleEndian); + + let unit = debug_info + .units() + .next() + .expect("should have a unit result") + .expect("and it should be ok"); + + let abbrevs_buf = &entries_cursor_tests_abbrev_buf(); + let debug_abbrev = DebugAbbrev::new(abbrevs_buf, LittleEndian); + + let abbrevs = unit + .abbreviations(&debug_abbrev) + .expect("Should parse abbreviations"); + + let mut cursor = unit.entries(&abbrevs); + + assert_next_dfs(&mut cursor, "001", 0); + + // Down to the first child of the root entry. + + assert_next_dfs(&mut cursor, "002", 1); + + // Now iterate all children of the root via `next_sibling`. + + assert_next_sibling(&mut cursor, "004"); + assert_next_sibling(&mut cursor, "007"); + assert_next_sibling(&mut cursor, "010"); + + // There should be no more siblings. + + assert!(cursor + .next_sibling() + .expect("Should parse next sibling") + .is_none()); + assert!(cursor.current().is_none()); + } + + #[test] + fn test_cursor_next_sibling_continuation() { + let info_buf = &entries_cursor_tests_debug_info_buf(); + let debug_info = DebugInfo::new(info_buf, LittleEndian); + + let unit = debug_info + .units() + .next() + .expect("should have a unit result") + .expect("and it should be ok"); + + let abbrevs_buf = &entries_cursor_tests_abbrev_buf(); + let debug_abbrev = DebugAbbrev::new(abbrevs_buf, LittleEndian); + + let abbrevs = unit + .abbreviations(&debug_abbrev) + .expect("Should parse abbreviations"); + + let mut cursor = unit.entries(&abbrevs); + + assert_next_dfs(&mut cursor, "001", 0); + + // Down to the first child of the root entry. + + assert_next_dfs(&mut cursor, "002", 1); + + // Get the next sibling, then iterate its children + + assert_next_sibling(&mut cursor, "004"); + assert_next_dfs(&mut cursor, "005", 1); + assert_next_sibling(&mut cursor, "006"); + assert!(cursor + .next_sibling() + .expect("Should parse next sibling") + .is_none()); + assert!(cursor + .next_sibling() + .expect("Should parse next sibling") + .is_none()); + assert!(cursor + .next_sibling() + .expect("Should parse next sibling") + .is_none()); + assert!(cursor + .next_sibling() + .expect("Should parse next sibling") + .is_none()); + + // And we should be able to continue with the children of the root entry. + + assert_next_dfs(&mut cursor, "007", -1); + assert_next_sibling(&mut cursor, "010"); + + // There should be no more siblings. + + assert!(cursor + .next_sibling() + .expect("Should parse next sibling") + .is_none()); + assert!(cursor.current().is_none()); + } + + fn entries_cursor_sibling_abbrev_buf() -> Vec<u8> { + #[rustfmt::skip] + let section = Section::with_endian(Endian::Little) + .abbrev(1, DW_TAG_subprogram, DW_CHILDREN_yes) + .abbrev_attr(DW_AT_name, DW_FORM_string) + .abbrev_attr(DW_AT_sibling, DW_FORM_ref1) + .abbrev_attr_null() + .abbrev(2, DW_TAG_subprogram, DW_CHILDREN_yes) + .abbrev_attr(DW_AT_name, DW_FORM_string) + .abbrev_attr_null() + .abbrev_null(); + section.get_contents().unwrap() + } + + fn entries_cursor_sibling_entries_buf(header_size: usize) -> Vec<u8> { + let start = Label::new(); + let sibling004_ref = Label::new(); + let sibling004 = Label::new(); + let sibling009_ref = Label::new(); + let sibling009 = Label::new(); + + #[rustfmt::skip] + let section = Section::with_endian(Endian::Little) + .mark(&start) + .die(2, |s| s.attr_string("001")) + // Valid sibling attribute. + .die(1, |s| s.attr_string("002").D8(&sibling004_ref)) + // Invalid code to ensure the sibling attribute was used. + .die(10, |s| s.attr_string("003")) + .die_null() + .die_null() + .mark(&sibling004) + // Invalid sibling attribute. + .die(1, |s| s.attr_string("004").attr_ref1(255)) + .die(2, |s| s.attr_string("005")) + .die_null() + .die_null() + // Sibling attribute in child only. + .die(2, |s| s.attr_string("006")) + // Valid sibling attribute. + .die(1, |s| s.attr_string("007").D8(&sibling009_ref)) + // Invalid code to ensure the sibling attribute was used. + .die(10, |s| s.attr_string("008")) + .die_null() + .die_null() + .mark(&sibling009) + .die(2, |s| s.attr_string("009")) + .die_null() + .die_null() + // No sibling attribute. + .die(2, |s| s.attr_string("010")) + .die(2, |s| s.attr_string("011")) + .die_null() + .die_null() + .die_null(); + + let offset = header_size as u64 + (&sibling004 - &start) as u64; + sibling004_ref.set_const(offset); + + let offset = header_size as u64 + (&sibling009 - &start) as u64; + sibling009_ref.set_const(offset); + + section.get_contents().unwrap() + } + + fn test_cursor_next_sibling_with_ptr(cursor: &mut EntriesCursor<EndianSlice<LittleEndian>>) { + assert_next_dfs(cursor, "001", 0); + + // Down to the first child of the root. + + assert_next_dfs(cursor, "002", 1); + + // Now iterate all children of the root via `next_sibling`. + + assert_valid_sibling_ptr(&cursor); + assert_next_sibling(cursor, "004"); + assert_next_sibling(cursor, "006"); + assert_next_sibling(cursor, "010"); + + // There should be no more siblings. + + assert!(cursor + .next_sibling() + .expect("Should parse next sibling") + .is_none()); + assert!(cursor.current().is_none()); + } + + #[test] + fn test_debug_info_next_sibling_with_ptr() { + let encoding = Encoding { + format: Format::Dwarf32, + version: 4, + address_size: 4, + }; + + let mut unit = UnitHeader { + encoding, + unit_length: 0, + unit_type: UnitType::Compilation, + debug_abbrev_offset: DebugAbbrevOffset(0), + unit_offset: DebugInfoOffset(0).into(), + entries_buf: EndianSlice::new(&[], LittleEndian), + }; + let header_size = unit.size_of_header(); + let entries_buf = entries_cursor_sibling_entries_buf(header_size); + unit.entries_buf = EndianSlice::new(&entries_buf, LittleEndian); + let section = Section::with_endian(Endian::Little).unit(&mut unit); + let info_buf = section.get_contents().unwrap(); + let debug_info = DebugInfo::new(&info_buf, LittleEndian); + + let unit = debug_info + .units() + .next() + .expect("should have a unit result") + .expect("and it should be ok"); + + let abbrev_buf = entries_cursor_sibling_abbrev_buf(); + let debug_abbrev = DebugAbbrev::new(&abbrev_buf, LittleEndian); + + let abbrevs = unit + .abbreviations(&debug_abbrev) + .expect("Should parse abbreviations"); + + let mut cursor = unit.entries(&abbrevs); + test_cursor_next_sibling_with_ptr(&mut cursor); + } + + #[test] + fn test_debug_types_next_sibling_with_ptr() { + let encoding = Encoding { + format: Format::Dwarf32, + version: 4, + address_size: 4, + }; + let mut unit = UnitHeader { + encoding, + unit_length: 0, + unit_type: UnitType::Type { + type_signature: DebugTypeSignature(0), + type_offset: UnitOffset(0), + }, + debug_abbrev_offset: DebugAbbrevOffset(0), + unit_offset: DebugTypesOffset(0).into(), + entries_buf: EndianSlice::new(&[], LittleEndian), + }; + let header_size = unit.size_of_header(); + let entries_buf = entries_cursor_sibling_entries_buf(header_size); + unit.entries_buf = EndianSlice::new(&entries_buf, LittleEndian); + let section = Section::with_endian(Endian::Little).unit(&mut unit); + let info_buf = section.get_contents().unwrap(); + let debug_types = DebugTypes::new(&info_buf, LittleEndian); + + let unit = debug_types + .units() + .next() + .expect("should have a unit result") + .expect("and it should be ok"); + + let abbrev_buf = entries_cursor_sibling_abbrev_buf(); + let debug_abbrev = DebugAbbrev::new(&abbrev_buf, LittleEndian); + + let abbrevs = unit + .abbreviations(&debug_abbrev) + .expect("Should parse abbreviations"); + + let mut cursor = unit.entries(&abbrevs); + test_cursor_next_sibling_with_ptr(&mut cursor); + } + + #[test] + fn test_entries_at_offset() { + let info_buf = &entries_cursor_tests_debug_info_buf(); + let debug_info = DebugInfo::new(info_buf, LittleEndian); + + let unit = debug_info + .units() + .next() + .expect("should have a unit result") + .expect("and it should be ok"); + + let abbrevs_buf = &entries_cursor_tests_abbrev_buf(); + let debug_abbrev = DebugAbbrev::new(abbrevs_buf, LittleEndian); + + let abbrevs = unit + .abbreviations(&debug_abbrev) + .expect("Should parse abbreviations"); + + let mut cursor = unit + .entries_at_offset(&abbrevs, UnitOffset(unit.header_size())) + .unwrap(); + assert_next_entry(&mut cursor, "001"); + + let cursor = unit.entries_at_offset(&abbrevs, UnitOffset(0)); + match cursor { + Err(Error::OffsetOutOfBounds) => {} + otherwise => { + assert!(false, "Unexpected parse result = {:#?}", otherwise); + } + } + } + + fn entries_tree_tests_debug_abbrevs_buf() -> Vec<u8> { + #[rustfmt::skip] + let section = Section::with_endian(Endian::Little) + .abbrev(1, DW_TAG_subprogram, DW_CHILDREN_yes) + .abbrev_attr(DW_AT_name, DW_FORM_string) + .abbrev_attr_null() + .abbrev(2, DW_TAG_subprogram, DW_CHILDREN_no) + .abbrev_attr(DW_AT_name, DW_FORM_string) + .abbrev_attr_null() + .abbrev_null() + .get_contents() + .unwrap(); + section + } + + fn entries_tree_tests_debug_info_buf(header_size: usize) -> (Vec<u8>, UnitOffset) { + let start = Label::new(); + let entry2 = Label::new(); + #[rustfmt::skip] + let section = Section::with_endian(Endian::Little) + .mark(&start) + .die(1, |s| s.attr_string("root")) + .die(1, |s| s.attr_string("1")) + .die(1, |s| s.attr_string("1a")) + .die_null() + .die(2, |s| s.attr_string("1b")) + .die_null() + .mark(&entry2) + .die(1, |s| s.attr_string("2")) + .die(1, |s| s.attr_string("2a")) + .die(1, |s| s.attr_string("2a1")) + .die_null() + .die_null() + .die(1, |s| s.attr_string("2b")) + .die(2, |s| s.attr_string("2b1")) + .die_null() + .die_null() + .die(1, |s| s.attr_string("3")) + .die(1, |s| s.attr_string("3a")) + .die(2, |s| s.attr_string("3a1")) + .die(2, |s| s.attr_string("3a2")) + .die_null() + .die(2, |s| s.attr_string("3b")) + .die_null() + .die(2, |s| s.attr_string("final")) + .die_null() + .get_contents() + .unwrap(); + let entry2 = UnitOffset(header_size + (&entry2 - &start) as usize); + (section, entry2) + } + + #[test] + fn test_entries_tree() { + fn assert_entry<'input, 'abbrev, 'unit, 'tree, Endian>( + node: Result< + Option<EntriesTreeNode<'abbrev, 'unit, 'tree, EndianSlice<'input, Endian>>>, + >, + name: &str, + ) -> EntriesTreeIter<'abbrev, 'unit, 'tree, EndianSlice<'input, Endian>> + where + Endian: Endianity, + { + let node = node + .expect("Should parse entry") + .expect("Should have entry"); + assert_entry_name(node.entry(), name); + node.children() + } + + fn assert_null<E: Endianity>(node: Result<Option<EntriesTreeNode<EndianSlice<E>>>>) { + match node { + Ok(None) => {} + otherwise => { + assert!(false, "Unexpected parse result = {:#?}", otherwise); + } + } + } + + let abbrevs_buf = entries_tree_tests_debug_abbrevs_buf(); + let debug_abbrev = DebugAbbrev::new(&abbrevs_buf, LittleEndian); + + let encoding = Encoding { + format: Format::Dwarf32, + version: 4, + address_size: 4, + }; + let mut unit = UnitHeader { + encoding, + unit_length: 0, + unit_type: UnitType::Compilation, + debug_abbrev_offset: DebugAbbrevOffset(0), + unit_offset: DebugInfoOffset(0).into(), + entries_buf: EndianSlice::new(&[], LittleEndian), + }; + let header_size = unit.size_of_header(); + let (entries_buf, entry2) = entries_tree_tests_debug_info_buf(header_size); + unit.entries_buf = EndianSlice::new(&entries_buf, LittleEndian); + let info_buf = Section::with_endian(Endian::Little) + .unit(&mut unit) + .get_contents() + .unwrap(); + let debug_info = DebugInfo::new(&info_buf, LittleEndian); + + let unit = debug_info + .units() + .next() + .expect("Should parse unit") + .expect("and it should be some"); + let abbrevs = unit + .abbreviations(&debug_abbrev) + .expect("Should parse abbreviations"); + let mut tree = unit + .entries_tree(&abbrevs, None) + .expect("Should have entries tree"); + + // Test we can restart iteration of the tree. + { + let mut iter = assert_entry(tree.root().map(Some), "root"); + assert_entry(iter.next(), "1"); + } + { + let mut iter = assert_entry(tree.root().map(Some), "root"); + assert_entry(iter.next(), "1"); + } + + let mut iter = assert_entry(tree.root().map(Some), "root"); + { + // Test iteration with children. + let mut iter = assert_entry(iter.next(), "1"); + { + // Test iteration with children flag, but no children. + let mut iter = assert_entry(iter.next(), "1a"); + assert_null(iter.next()); + assert_null(iter.next()); + } + { + // Test iteration without children flag. + let mut iter = assert_entry(iter.next(), "1b"); + assert_null(iter.next()); + assert_null(iter.next()); + } + assert_null(iter.next()); + assert_null(iter.next()); + } + { + // Test skipping over children. + let mut iter = assert_entry(iter.next(), "2"); + assert_entry(iter.next(), "2a"); + assert_entry(iter.next(), "2b"); + assert_null(iter.next()); + } + { + // Test skipping after partial iteration. + let mut iter = assert_entry(iter.next(), "3"); + { + let mut iter = assert_entry(iter.next(), "3a"); + assert_entry(iter.next(), "3a1"); + // Parent iter should be able to skip over "3a2". + } + assert_entry(iter.next(), "3b"); + assert_null(iter.next()); + } + assert_entry(iter.next(), "final"); + assert_null(iter.next()); + + // Test starting at an offset. + let mut tree = unit + .entries_tree(&abbrevs, Some(entry2)) + .expect("Should have entries tree"); + let mut iter = assert_entry(tree.root().map(Some), "2"); + assert_entry(iter.next(), "2a"); + assert_entry(iter.next(), "2b"); + assert_null(iter.next()); + } + + #[test] + fn test_entries_raw() { + fn assert_abbrev<'input, 'abbrev, 'unit, Endian>( + entries: &mut EntriesRaw<'abbrev, 'unit, EndianSlice<'input, Endian>>, + tag: DwTag, + ) -> &'abbrev Abbreviation + where + Endian: Endianity, + { + let abbrev = entries + .read_abbreviation() + .expect("Should parse abbrev") + .expect("Should have abbrev"); + assert_eq!(abbrev.tag(), tag); + abbrev + } + + fn assert_null<'input, 'abbrev, 'unit, Endian>( + entries: &mut EntriesRaw<'abbrev, 'unit, EndianSlice<'input, Endian>>, + ) where + Endian: Endianity, + { + match entries.read_abbreviation() { + Ok(None) => {} + otherwise => { + assert!(false, "Unexpected parse result = {:#?}", otherwise); + } + } + } + + fn assert_attr<'input, 'abbrev, 'unit, Endian>( + entries: &mut EntriesRaw<'abbrev, 'unit, EndianSlice<'input, Endian>>, + spec: Option<AttributeSpecification>, + name: DwAt, + value: &str, + ) where + Endian: Endianity, + { + let spec = spec.expect("Should have attribute specification"); + let attr = entries + .read_attribute(spec) + .expect("Should parse attribute"); + assert_eq!(attr.name(), name); + assert_eq!( + attr.value(), + AttributeValue::String(EndianSlice::new(value.as_bytes(), Endian::default())) + ); + } + + #[rustfmt::skip] + let section = Section::with_endian(Endian::Little) + .abbrev(1, DW_TAG_subprogram, DW_CHILDREN_yes) + .abbrev_attr(DW_AT_name, DW_FORM_string) + .abbrev_attr(DW_AT_linkage_name, DW_FORM_string) + .abbrev_attr_null() + .abbrev(2, DW_TAG_variable, DW_CHILDREN_no) + .abbrev_attr(DW_AT_name, DW_FORM_string) + .abbrev_attr_null() + .abbrev_null(); + let abbrevs_buf = section.get_contents().unwrap(); + let debug_abbrev = DebugAbbrev::new(&abbrevs_buf, LittleEndian); + + #[rustfmt::skip] + let section = Section::with_endian(Endian::Little) + .die(1, |s| s.attr_string("f1").attr_string("l1")) + .die(2, |s| s.attr_string("v1")) + .die(2, |s| s.attr_string("v2")) + .die(1, |s| s.attr_string("f2").attr_string("l2")) + .die_null() + .die_null(); + let entries_buf = section.get_contents().unwrap(); + + let encoding = Encoding { + format: Format::Dwarf32, + version: 4, + address_size: 4, + }; + let mut unit = UnitHeader { + encoding, + unit_length: 0, + unit_type: UnitType::Compilation, + debug_abbrev_offset: DebugAbbrevOffset(0), + unit_offset: DebugInfoOffset(0).into(), + entries_buf: EndianSlice::new(&entries_buf, LittleEndian), + }; + let section = Section::with_endian(Endian::Little).unit(&mut unit); + let info_buf = section.get_contents().unwrap(); + let debug_info = DebugInfo::new(&info_buf, LittleEndian); + + let unit = debug_info + .units() + .next() + .expect("should have a unit result") + .expect("and it should be ok"); + + let abbrevs = unit + .abbreviations(&debug_abbrev) + .expect("Should parse abbreviations"); + + let mut entries = unit + .entries_raw(&abbrevs, None) + .expect("Should have entries"); + + assert_eq!(entries.next_depth(), 0); + let abbrev = assert_abbrev(&mut entries, DW_TAG_subprogram); + let mut attrs = abbrev.attributes().iter().copied(); + assert_attr(&mut entries, attrs.next(), DW_AT_name, "f1"); + assert_attr(&mut entries, attrs.next(), DW_AT_linkage_name, "l1"); + assert!(attrs.next().is_none()); + + assert_eq!(entries.next_depth(), 1); + let abbrev = assert_abbrev(&mut entries, DW_TAG_variable); + let mut attrs = abbrev.attributes().iter().copied(); + assert_attr(&mut entries, attrs.next(), DW_AT_name, "v1"); + assert!(attrs.next().is_none()); + + assert_eq!(entries.next_depth(), 1); + let abbrev = assert_abbrev(&mut entries, DW_TAG_variable); + let mut attrs = abbrev.attributes().iter().copied(); + assert_attr(&mut entries, attrs.next(), DW_AT_name, "v2"); + assert!(attrs.next().is_none()); + + assert_eq!(entries.next_depth(), 1); + let abbrev = assert_abbrev(&mut entries, DW_TAG_subprogram); + let mut attrs = abbrev.attributes().iter().copied(); + assert_attr(&mut entries, attrs.next(), DW_AT_name, "f2"); + assert_attr(&mut entries, attrs.next(), DW_AT_linkage_name, "l2"); + assert!(attrs.next().is_none()); + + assert_eq!(entries.next_depth(), 2); + assert_null(&mut entries); + + assert_eq!(entries.next_depth(), 1); + assert_null(&mut entries); + + assert_eq!(entries.next_depth(), 0); + assert!(entries.is_empty()); + } + + #[test] + fn test_debug_info_offset() { + let padding = &[0; 10]; + let entries = &[0; 20]; + let encoding = Encoding { + format: Format::Dwarf32, + version: 4, + address_size: 4, + }; + let mut unit = UnitHeader { + encoding, + unit_length: 0, + unit_type: UnitType::Compilation, + debug_abbrev_offset: DebugAbbrevOffset(0), + unit_offset: DebugInfoOffset(0).into(), + entries_buf: EndianSlice::new(entries, LittleEndian), + }; + Section::with_endian(Endian::Little) + .append_bytes(padding) + .unit(&mut unit); + let offset = padding.len(); + let header_length = unit.size_of_header(); + let length = unit.length_including_self(); + assert_eq!(DebugInfoOffset(0).to_unit_offset(&unit), None); + assert_eq!(DebugInfoOffset(offset - 1).to_unit_offset(&unit), None); + assert_eq!(DebugInfoOffset(offset).to_unit_offset(&unit), None); + assert_eq!( + DebugInfoOffset(offset + header_length - 1).to_unit_offset(&unit), + None + ); + assert_eq!( + DebugInfoOffset(offset + header_length).to_unit_offset(&unit), + Some(UnitOffset(header_length)) + ); + assert_eq!( + DebugInfoOffset(offset + length - 1).to_unit_offset(&unit), + Some(UnitOffset(length - 1)) + ); + assert_eq!(DebugInfoOffset(offset + length).to_unit_offset(&unit), None); + assert_eq!( + UnitOffset(header_length).to_debug_info_offset(&unit), + Some(DebugInfoOffset(offset + header_length)) + ); + assert_eq!( + UnitOffset(length - 1).to_debug_info_offset(&unit), + Some(DebugInfoOffset(offset + length - 1)) + ); + } + + #[test] + fn test_debug_types_offset() { + let padding = &[0; 10]; + let entries = &[0; 20]; + let encoding = Encoding { + format: Format::Dwarf32, + version: 4, + address_size: 4, + }; + let mut unit = UnitHeader { + encoding, + unit_length: 0, + unit_type: UnitType::Type { + type_signature: DebugTypeSignature(0), + type_offset: UnitOffset(0), + }, + debug_abbrev_offset: DebugAbbrevOffset(0), + unit_offset: DebugTypesOffset(0).into(), + entries_buf: EndianSlice::new(entries, LittleEndian), + }; + Section::with_endian(Endian::Little) + .append_bytes(padding) + .unit(&mut unit); + let offset = padding.len(); + let header_length = unit.size_of_header(); + let length = unit.length_including_self(); + assert_eq!(DebugTypesOffset(0).to_unit_offset(&unit), None); + assert_eq!(DebugTypesOffset(offset - 1).to_unit_offset(&unit), None); + assert_eq!(DebugTypesOffset(offset).to_unit_offset(&unit), None); + assert_eq!( + DebugTypesOffset(offset + header_length - 1).to_unit_offset(&unit), + None + ); + assert_eq!( + DebugTypesOffset(offset + header_length).to_unit_offset(&unit), + Some(UnitOffset(header_length)) + ); + assert_eq!( + DebugTypesOffset(offset + length - 1).to_unit_offset(&unit), + Some(UnitOffset(length - 1)) + ); + assert_eq!( + DebugTypesOffset(offset + length).to_unit_offset(&unit), + None + ); + assert_eq!( + UnitOffset(header_length).to_debug_types_offset(&unit), + Some(DebugTypesOffset(offset + header_length)) + ); + assert_eq!( + UnitOffset(length - 1).to_debug_types_offset(&unit), + Some(DebugTypesOffset(offset + length - 1)) + ); + } + + #[test] + fn test_length_including_self() { + let encoding = Encoding { + format: Format::Dwarf32, + version: 4, + address_size: 4, + }; + let mut unit = UnitHeader { + encoding, + unit_length: 0, + unit_type: UnitType::Compilation, + debug_abbrev_offset: DebugAbbrevOffset(0), + unit_offset: DebugInfoOffset(0).into(), + entries_buf: EndianSlice::new(&[], LittleEndian), + }; + unit.encoding.format = Format::Dwarf32; + assert_eq!(unit.length_including_self(), 4); + unit.encoding.format = Format::Dwarf64; + assert_eq!(unit.length_including_self(), 12); + unit.unit_length = 10; + assert_eq!(unit.length_including_self(), 22); + } + + #[test] + fn test_parse_type_unit_abbrevs() { + let types_buf = [ + // Type unit header + 0x25, 0x00, 0x00, 0x00, // 32-bit unit length = 37 + 0x04, 0x00, // Version 4 + 0x00, 0x00, 0x00, 0x00, // debug_abbrev_offset + 0x04, // Address size + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, // Type signature + 0x01, 0x02, 0x03, 0x04, // Type offset + // DIEs + // Abbreviation code + 0x01, // Attribute of form DW_FORM_string = "foo\0" + 0x66, 0x6f, 0x6f, 0x00, // Children + // Abbreviation code + 0x01, // Attribute of form DW_FORM_string = "foo\0" + 0x66, 0x6f, 0x6f, 0x00, // Children + // Abbreviation code + 0x01, // Attribute of form DW_FORM_string = "foo\0" + 0x66, 0x6f, 0x6f, 0x00, // Children + 0x00, // End of children + 0x00, // End of children + 0x00, // End of children + ]; + let debug_types = DebugTypes::new(&types_buf, LittleEndian); + + let abbrev_buf = [ + // Code + 0x01, // DW_TAG_subprogram + 0x2e, // DW_CHILDREN_yes + 0x01, // Begin attributes + 0x03, // Attribute name = DW_AT_name + 0x08, // Attribute form = DW_FORM_string + 0x00, 0x00, // End attributes + 0x00, // Null terminator + ]; + + let get_some_type_unit = || debug_types.units().next().unwrap().unwrap(); + + let unit = get_some_type_unit(); + + let read_debug_abbrev_section_somehow = || &abbrev_buf; + let debug_abbrev = DebugAbbrev::new(read_debug_abbrev_section_somehow(), LittleEndian); + let _abbrevs_for_unit = unit.abbreviations(&debug_abbrev).unwrap(); + } +} diff --git a/vendor/gimli/src/read/util.rs b/vendor/gimli/src/read/util.rs new file mode 100644 index 0000000..041ca5a --- /dev/null +++ b/vendor/gimli/src/read/util.rs @@ -0,0 +1,283 @@ +#[cfg(feature = "read")] +use alloc::boxed::Box; +#[cfg(feature = "read")] +use alloc::vec::Vec; +use core::fmt; +use core::mem::MaybeUninit; +use core::ops; +use core::ptr; +use core::slice; + +mod sealed { + /// # Safety + /// Implementer must not modify the content in storage. + pub unsafe trait Sealed { + type Storage; + + fn new_storage() -> Self::Storage; + + fn grow(_storage: &mut Self::Storage, _additional: usize) -> Result<(), CapacityFull> { + Err(CapacityFull) + } + } + + #[derive(Clone, Copy, Debug)] + pub struct CapacityFull; +} + +use sealed::*; + +/// Marker trait for types that can be used as backing storage when a growable array type is needed. +/// +/// This trait is sealed and cannot be implemented for types outside this crate. +pub trait ArrayLike: Sealed { + /// Type of the elements being stored. + type Item; + + #[doc(hidden)] + fn as_slice(storage: &Self::Storage) -> &[MaybeUninit<Self::Item>]; + + #[doc(hidden)] + fn as_mut_slice(storage: &mut Self::Storage) -> &mut [MaybeUninit<Self::Item>]; +} + +// Use macro since const generics can't be used due to MSRV. +macro_rules! impl_array { + () => {}; + ($n:literal $($rest:tt)*) => { + // SAFETY: does not modify the content in storage. + unsafe impl<T> Sealed for [T; $n] { + type Storage = [MaybeUninit<T>; $n]; + + fn new_storage() -> Self::Storage { + // SAFETY: An uninitialized `[MaybeUninit<_>; _]` is valid. + unsafe { MaybeUninit::uninit().assume_init() } + } + } + + impl<T> ArrayLike for [T; $n] { + type Item = T; + + fn as_slice(storage: &Self::Storage) -> &[MaybeUninit<T>] { + storage + } + + fn as_mut_slice(storage: &mut Self::Storage) -> &mut [MaybeUninit<T>] { + storage + } + } + + impl_array!($($rest)*); + } +} + +#[cfg(feature = "read")] +macro_rules! impl_box { + () => {}; + ($n:literal $($rest:tt)*) => { + // SAFETY: does not modify the content in storage. + unsafe impl<T> Sealed for Box<[T; $n]> { + type Storage = Box<[MaybeUninit<T>; $n]>; + + fn new_storage() -> Self::Storage { + // SAFETY: An uninitialized `[MaybeUninit<_>; _]` is valid. + Box::new(unsafe { MaybeUninit::uninit().assume_init() }) + } + } + + impl<T> ArrayLike for Box<[T; $n]> { + type Item = T; + + fn as_slice(storage: &Self::Storage) -> &[MaybeUninit<T>] { + &storage[..] + } + + fn as_mut_slice(storage: &mut Self::Storage) -> &mut [MaybeUninit<T>] { + &mut storage[..] + } + } + + impl_box!($($rest)*); + } +} + +impl_array!(0 1 2 3 4 8 16 32 64 128 192); +#[cfg(feature = "read")] +impl_box!(0 1 2 3 4 8 16 32 64 128 192); + +#[cfg(feature = "read")] +unsafe impl<T> Sealed for Vec<T> { + type Storage = Box<[MaybeUninit<T>]>; + + fn new_storage() -> Self::Storage { + Box::new([]) + } + + fn grow(storage: &mut Self::Storage, additional: usize) -> Result<(), CapacityFull> { + let mut vec: Vec<_> = core::mem::replace(storage, Box::new([])).into(); + vec.reserve(additional); + // SAFETY: This is a `Vec` of `MaybeUninit`. + unsafe { vec.set_len(vec.capacity()) }; + *storage = vec.into_boxed_slice(); + Ok(()) + } +} + +#[cfg(feature = "read")] +impl<T> ArrayLike for Vec<T> { + type Item = T; + + fn as_slice(storage: &Self::Storage) -> &[MaybeUninit<T>] { + storage + } + + fn as_mut_slice(storage: &mut Self::Storage) -> &mut [MaybeUninit<T>] { + storage + } +} + +pub(crate) struct ArrayVec<A: ArrayLike> { + storage: A::Storage, + len: usize, +} + +impl<A: ArrayLike> ArrayVec<A> { + pub fn new() -> Self { + Self { + storage: A::new_storage(), + len: 0, + } + } + + pub fn clear(&mut self) { + let ptr: *mut [A::Item] = &mut **self; + // Set length first so the type invariant is upheld even if `drop_in_place` panicks. + self.len = 0; + // SAFETY: `ptr` contains valid elements only and we "forget" them by setting the length. + unsafe { ptr::drop_in_place(ptr) }; + } + + pub fn try_push(&mut self, value: A::Item) -> Result<(), CapacityFull> { + let mut storage = A::as_mut_slice(&mut self.storage); + if self.len >= storage.len() { + A::grow(&mut self.storage, 1)?; + storage = A::as_mut_slice(&mut self.storage); + } + + storage[self.len] = MaybeUninit::new(value); + self.len += 1; + Ok(()) + } + + pub fn try_insert(&mut self, index: usize, element: A::Item) -> Result<(), CapacityFull> { + assert!(index <= self.len); + + let mut storage = A::as_mut_slice(&mut self.storage); + if self.len >= storage.len() { + A::grow(&mut self.storage, 1)?; + storage = A::as_mut_slice(&mut self.storage); + } + + // SAFETY: storage[index] is filled later. + unsafe { + let p = storage.as_mut_ptr().add(index); + core::ptr::copy(p as *const _, p.add(1), self.len - index); + } + storage[index] = MaybeUninit::new(element); + self.len += 1; + Ok(()) + } + + pub fn pop(&mut self) -> Option<A::Item> { + if self.len == 0 { + None + } else { + self.len -= 1; + // SAFETY: this element is valid and we "forget" it by setting the length. + Some(unsafe { A::as_slice(&self.storage)[self.len].as_ptr().read() }) + } + } + + pub fn swap_remove(&mut self, index: usize) -> A::Item { + assert!(self.len > 0); + A::as_mut_slice(&mut self.storage).swap(index, self.len - 1); + self.pop().unwrap() + } +} + +#[cfg(feature = "read")] +impl<T> ArrayVec<Vec<T>> { + pub fn into_vec(mut self) -> Vec<T> { + let len = core::mem::replace(&mut self.len, 0); + let storage = core::mem::replace(&mut self.storage, Box::new([])); + let slice = Box::leak(storage); + debug_assert!(len <= slice.len()); + // SAFETY: valid elements. + unsafe { Vec::from_raw_parts(slice.as_mut_ptr() as *mut T, len, slice.len()) } + } +} + +impl<A: ArrayLike> Drop for ArrayVec<A> { + fn drop(&mut self) { + self.clear(); + } +} + +impl<A: ArrayLike> Default for ArrayVec<A> { + fn default() -> Self { + Self::new() + } +} + +impl<A: ArrayLike> ops::Deref for ArrayVec<A> { + type Target = [A::Item]; + + fn deref(&self) -> &[A::Item] { + let slice = &A::as_slice(&self.storage); + debug_assert!(self.len <= slice.len()); + // SAFETY: valid elements. + unsafe { slice::from_raw_parts(slice.as_ptr() as _, self.len) } + } +} + +impl<A: ArrayLike> ops::DerefMut for ArrayVec<A> { + fn deref_mut(&mut self) -> &mut [A::Item] { + let slice = &mut A::as_mut_slice(&mut self.storage); + debug_assert!(self.len <= slice.len()); + // SAFETY: valid elements. + unsafe { slice::from_raw_parts_mut(slice.as_mut_ptr() as _, self.len) } + } +} + +impl<A: ArrayLike> Clone for ArrayVec<A> +where + A::Item: Clone, +{ + fn clone(&self) -> Self { + let mut new = Self::default(); + for value in &**self { + new.try_push(value.clone()).unwrap(); + } + new + } +} + +impl<A: ArrayLike> PartialEq for ArrayVec<A> +where + A::Item: PartialEq, +{ + fn eq(&self, other: &Self) -> bool { + **self == **other + } +} + +impl<A: ArrayLike> Eq for ArrayVec<A> where A::Item: Eq {} + +impl<A: ArrayLike> fmt::Debug for ArrayVec<A> +where + A::Item: fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&**self, f) + } +} diff --git a/vendor/gimli/src/read/value.rs b/vendor/gimli/src/read/value.rs new file mode 100644 index 0000000..77b08ed --- /dev/null +++ b/vendor/gimli/src/read/value.rs @@ -0,0 +1,1621 @@ +//! Definitions for values used in DWARF expressions. + +use crate::constants; +#[cfg(feature = "read")] +use crate::read::{AttributeValue, DebuggingInformationEntry}; +use crate::read::{Error, Reader, Result}; + +/// Convert a u64 to an i64, with sign extension if required. +/// +/// This is primarily used when needing to treat `Value::Generic` +/// as a signed value. +#[inline] +fn sign_extend(value: u64, mask: u64) -> i64 { + let value = (value & mask) as i64; + let sign = ((mask >> 1) + 1) as i64; + (value ^ sign).wrapping_sub(sign) +} + +#[inline] +fn mask_bit_size(addr_mask: u64) -> u32 { + 64 - addr_mask.leading_zeros() +} + +/// The type of an entry on the DWARF stack. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum ValueType { + /// The generic type, which is address-sized and of unspecified sign, + /// as specified in the DWARF 5 standard, section 2.5.1. + /// This type is also used to represent address base types. + Generic, + /// Signed 8-bit integer type. + I8, + /// Unsigned 8-bit integer type. + U8, + /// Signed 16-bit integer type. + I16, + /// Unsigned 16-bit integer type. + U16, + /// Signed 32-bit integer type. + I32, + /// Unsigned 32-bit integer type. + U32, + /// Signed 64-bit integer type. + I64, + /// Unsigned 64-bit integer type. + U64, + /// 32-bit floating point type. + F32, + /// 64-bit floating point type. + F64, +} + +/// The value of an entry on the DWARF stack. +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum Value { + /// A generic value, which is address-sized and of unspecified sign. + Generic(u64), + /// A signed 8-bit integer value. + I8(i8), + /// An unsigned 8-bit integer value. + U8(u8), + /// A signed 16-bit integer value. + I16(i16), + /// An unsigned 16-bit integer value. + U16(u16), + /// A signed 32-bit integer value. + I32(i32), + /// An unsigned 32-bit integer value. + U32(u32), + /// A signed 64-bit integer value. + I64(i64), + /// An unsigned 64-bit integer value. + U64(u64), + /// A 32-bit floating point value. + F32(f32), + /// A 64-bit floating point value. + F64(f64), +} + +impl ValueType { + /// The size in bits of a value for this type. + pub fn bit_size(self, addr_mask: u64) -> u32 { + match self { + ValueType::Generic => mask_bit_size(addr_mask), + ValueType::I8 | ValueType::U8 => 8, + ValueType::I16 | ValueType::U16 => 16, + ValueType::I32 | ValueType::U32 | ValueType::F32 => 32, + ValueType::I64 | ValueType::U64 | ValueType::F64 => 64, + } + } + + /// Construct a `ValueType` from the attributes of a base type DIE. + pub fn from_encoding(encoding: constants::DwAte, byte_size: u64) -> Option<ValueType> { + Some(match (encoding, byte_size) { + (constants::DW_ATE_signed, 1) => ValueType::I8, + (constants::DW_ATE_signed, 2) => ValueType::I16, + (constants::DW_ATE_signed, 4) => ValueType::I32, + (constants::DW_ATE_signed, 8) => ValueType::I64, + (constants::DW_ATE_unsigned, 1) => ValueType::U8, + (constants::DW_ATE_unsigned, 2) => ValueType::U16, + (constants::DW_ATE_unsigned, 4) => ValueType::U32, + (constants::DW_ATE_unsigned, 8) => ValueType::U64, + (constants::DW_ATE_float, 4) => ValueType::F32, + (constants::DW_ATE_float, 8) => ValueType::F64, + _ => return None, + }) + } + + /// Construct a `ValueType` from a base type DIE. + #[cfg(feature = "read")] + pub fn from_entry<R: Reader>( + entry: &DebuggingInformationEntry<R>, + ) -> Result<Option<ValueType>> { + if entry.tag() != constants::DW_TAG_base_type { + return Ok(None); + } + let mut encoding = None; + let mut byte_size = None; + let mut endianity = constants::DW_END_default; + let mut attrs = entry.attrs(); + while let Some(attr) = attrs.next()? { + match attr.name() { + constants::DW_AT_byte_size => byte_size = attr.udata_value(), + constants::DW_AT_encoding => { + if let AttributeValue::Encoding(x) = attr.value() { + encoding = Some(x); + } + } + constants::DW_AT_endianity => { + if let AttributeValue::Endianity(x) = attr.value() { + endianity = x; + } + } + _ => {} + } + } + + if endianity != constants::DW_END_default { + // TODO: we could check if it matches the reader endianity, + // but normally it would use DW_END_default in that case. + return Ok(None); + } + + if let (Some(encoding), Some(byte_size)) = (encoding, byte_size) { + Ok(ValueType::from_encoding(encoding, byte_size)) + } else { + Ok(None) + } + } +} + +impl Value { + /// Return the `ValueType` corresponding to this `Value`. + pub fn value_type(&self) -> ValueType { + match *self { + Value::Generic(_) => ValueType::Generic, + Value::I8(_) => ValueType::I8, + Value::U8(_) => ValueType::U8, + Value::I16(_) => ValueType::I16, + Value::U16(_) => ValueType::U16, + Value::I32(_) => ValueType::I32, + Value::U32(_) => ValueType::U32, + Value::I64(_) => ValueType::I64, + Value::U64(_) => ValueType::U64, + Value::F32(_) => ValueType::F32, + Value::F64(_) => ValueType::F64, + } + } + + /// Read a `Value` with the given `value_type` from a `Reader`. + pub fn parse<R: Reader>(value_type: ValueType, mut bytes: R) -> Result<Value> { + let value = match value_type { + ValueType::I8 => Value::I8(bytes.read_i8()?), + ValueType::U8 => Value::U8(bytes.read_u8()?), + ValueType::I16 => Value::I16(bytes.read_i16()?), + ValueType::U16 => Value::U16(bytes.read_u16()?), + ValueType::I32 => Value::I32(bytes.read_i32()?), + ValueType::U32 => Value::U32(bytes.read_u32()?), + ValueType::I64 => Value::I64(bytes.read_i64()?), + ValueType::U64 => Value::U64(bytes.read_u64()?), + ValueType::F32 => Value::F32(bytes.read_f32()?), + ValueType::F64 => Value::F64(bytes.read_f64()?), + _ => return Err(Error::UnsupportedTypeOperation), + }; + Ok(value) + } + + /// Convert a `Value` to a `u64`. + /// + /// The `ValueType` of `self` must be integral. + /// Values are sign extended if the source value is signed. + pub fn to_u64(self, addr_mask: u64) -> Result<u64> { + let value = match self { + Value::Generic(value) => value & addr_mask, + Value::I8(value) => value as u64, + Value::U8(value) => u64::from(value), + Value::I16(value) => value as u64, + Value::U16(value) => u64::from(value), + Value::I32(value) => value as u64, + Value::U32(value) => u64::from(value), + Value::I64(value) => value as u64, + Value::U64(value) => value as u64, + _ => return Err(Error::IntegralTypeRequired), + }; + Ok(value) + } + + /// Create a `Value` with the given `value_type` from a `u64` value. + /// + /// The `value_type` may be integral or floating point. + /// The result is truncated if the `u64` value does + /// not fit the bounds of the `value_type`. + pub fn from_u64(value_type: ValueType, value: u64) -> Result<Value> { + let value = match value_type { + ValueType::Generic => Value::Generic(value), + ValueType::I8 => Value::I8(value as i8), + ValueType::U8 => Value::U8(value as u8), + ValueType::I16 => Value::I16(value as i16), + ValueType::U16 => Value::U16(value as u16), + ValueType::I32 => Value::I32(value as i32), + ValueType::U32 => Value::U32(value as u32), + ValueType::I64 => Value::I64(value as i64), + ValueType::U64 => Value::U64(value), + ValueType::F32 => Value::F32(value as f32), + ValueType::F64 => Value::F64(value as f64), + }; + Ok(value) + } + + /// Create a `Value` with the given `value_type` from a `f32` value. + /// + /// The `value_type` may be integral or floating point. + /// The result is not defined if the `f32` value does + /// not fit the bounds of the `value_type`. + fn from_f32(value_type: ValueType, value: f32) -> Result<Value> { + let value = match value_type { + ValueType::Generic => Value::Generic(value as u64), + ValueType::I8 => Value::I8(value as i8), + ValueType::U8 => Value::U8(value as u8), + ValueType::I16 => Value::I16(value as i16), + ValueType::U16 => Value::U16(value as u16), + ValueType::I32 => Value::I32(value as i32), + ValueType::U32 => Value::U32(value as u32), + ValueType::I64 => Value::I64(value as i64), + ValueType::U64 => Value::U64(value as u64), + ValueType::F32 => Value::F32(value), + ValueType::F64 => Value::F64(f64::from(value)), + }; + Ok(value) + } + + /// Create a `Value` with the given `value_type` from a `f64` value. + /// + /// The `value_type` may be integral or floating point. + /// The result is not defined if the `f64` value does + /// not fit the bounds of the `value_type`. + fn from_f64(value_type: ValueType, value: f64) -> Result<Value> { + let value = match value_type { + ValueType::Generic => Value::Generic(value as u64), + ValueType::I8 => Value::I8(value as i8), + ValueType::U8 => Value::U8(value as u8), + ValueType::I16 => Value::I16(value as i16), + ValueType::U16 => Value::U16(value as u16), + ValueType::I32 => Value::I32(value as i32), + ValueType::U32 => Value::U32(value as u32), + ValueType::I64 => Value::I64(value as i64), + ValueType::U64 => Value::U64(value as u64), + ValueType::F32 => Value::F32(value as f32), + ValueType::F64 => Value::F64(value), + }; + Ok(value) + } + + /// Convert a `Value` to the given `value_type`. + /// + /// When converting between integral types, the result is truncated + /// if the source value does not fit the bounds of the `value_type`. + /// When converting from floating point types, the result is not defined + /// if the source value does not fit the bounds of the `value_type`. + /// + /// This corresponds to the DWARF `DW_OP_convert` operation. + pub fn convert(self, value_type: ValueType, addr_mask: u64) -> Result<Value> { + match self { + Value::F32(value) => Value::from_f32(value_type, value), + Value::F64(value) => Value::from_f64(value_type, value), + _ => Value::from_u64(value_type, self.to_u64(addr_mask)?), + } + } + + /// Reinterpret the bits in a `Value` as the given `value_type`. + /// + /// The source and result value types must have equal sizes. + /// + /// This corresponds to the DWARF `DW_OP_reinterpret` operation. + pub fn reinterpret(self, value_type: ValueType, addr_mask: u64) -> Result<Value> { + if self.value_type().bit_size(addr_mask) != value_type.bit_size(addr_mask) { + return Err(Error::TypeMismatch); + } + let bits = match self { + Value::Generic(value) => value, + Value::I8(value) => value as u64, + Value::U8(value) => u64::from(value), + Value::I16(value) => value as u64, + Value::U16(value) => u64::from(value), + Value::I32(value) => value as u64, + Value::U32(value) => u64::from(value), + Value::I64(value) => value as u64, + Value::U64(value) => value, + Value::F32(value) => u64::from(f32::to_bits(value)), + Value::F64(value) => f64::to_bits(value), + }; + let value = match value_type { + ValueType::Generic => Value::Generic(bits), + ValueType::I8 => Value::I8(bits as i8), + ValueType::U8 => Value::U8(bits as u8), + ValueType::I16 => Value::I16(bits as i16), + ValueType::U16 => Value::U16(bits as u16), + ValueType::I32 => Value::I32(bits as i32), + ValueType::U32 => Value::U32(bits as u32), + ValueType::I64 => Value::I64(bits as i64), + ValueType::U64 => Value::U64(bits), + ValueType::F32 => Value::F32(f32::from_bits(bits as u32)), + ValueType::F64 => Value::F64(f64::from_bits(bits)), + }; + Ok(value) + } + + /// Perform an absolute value operation. + /// + /// If the value type is `Generic`, then it is interpreted as a signed value. + /// + /// This corresponds to the DWARF `DW_OP_abs` operation. + pub fn abs(self, addr_mask: u64) -> Result<Value> { + // wrapping_abs() can be used because DWARF specifies that the result is undefined + // for negative minimal values. + let value = match self { + Value::Generic(value) => { + Value::Generic(sign_extend(value, addr_mask).wrapping_abs() as u64) + } + Value::I8(value) => Value::I8(value.wrapping_abs()), + Value::I16(value) => Value::I16(value.wrapping_abs()), + Value::I32(value) => Value::I32(value.wrapping_abs()), + Value::I64(value) => Value::I64(value.wrapping_abs()), + // f32/f64::abs() is not available in libcore + Value::F32(value) => Value::F32(if value < 0. { -value } else { value }), + Value::F64(value) => Value::F64(if value < 0. { -value } else { value }), + Value::U8(_) | Value::U16(_) | Value::U32(_) | Value::U64(_) => self, + }; + Ok(value) + } + + /// Perform a negation operation. + /// + /// If the value type is `Generic`, then it is interpreted as a signed value. + /// + /// This corresponds to the DWARF `DW_OP_neg` operation. + pub fn neg(self, addr_mask: u64) -> Result<Value> { + // wrapping_neg() can be used because DWARF specifies that the result is undefined + // for negative minimal values. + let value = match self { + Value::Generic(value) => { + Value::Generic(sign_extend(value, addr_mask).wrapping_neg() as u64) + } + Value::I8(value) => Value::I8(value.wrapping_neg()), + Value::I16(value) => Value::I16(value.wrapping_neg()), + Value::I32(value) => Value::I32(value.wrapping_neg()), + Value::I64(value) => Value::I64(value.wrapping_neg()), + Value::F32(value) => Value::F32(-value), + Value::F64(value) => Value::F64(-value), + // It's unclear if these should implicitly convert to a signed value. + // For now, we don't support them. + Value::U8(_) | Value::U16(_) | Value::U32(_) | Value::U64(_) => { + return Err(Error::UnsupportedTypeOperation); + } + }; + Ok(value) + } + + /// Perform an addition operation. + /// + /// This operation requires matching types. + /// + /// This corresponds to the DWARF `DW_OP_plus` operation. + pub fn add(self, rhs: Value, addr_mask: u64) -> Result<Value> { + let value = match (self, rhs) { + (Value::Generic(v1), Value::Generic(v2)) => { + Value::Generic(v1.wrapping_add(v2) & addr_mask) + } + (Value::I8(v1), Value::I8(v2)) => Value::I8(v1.wrapping_add(v2)), + (Value::U8(v1), Value::U8(v2)) => Value::U8(v1.wrapping_add(v2)), + (Value::I16(v1), Value::I16(v2)) => Value::I16(v1.wrapping_add(v2)), + (Value::U16(v1), Value::U16(v2)) => Value::U16(v1.wrapping_add(v2)), + (Value::I32(v1), Value::I32(v2)) => Value::I32(v1.wrapping_add(v2)), + (Value::U32(v1), Value::U32(v2)) => Value::U32(v1.wrapping_add(v2)), + (Value::I64(v1), Value::I64(v2)) => Value::I64(v1.wrapping_add(v2)), + (Value::U64(v1), Value::U64(v2)) => Value::U64(v1.wrapping_add(v2)), + (Value::F32(v1), Value::F32(v2)) => Value::F32(v1 + v2), + (Value::F64(v1), Value::F64(v2)) => Value::F64(v1 + v2), + _ => return Err(Error::TypeMismatch), + }; + Ok(value) + } + + /// Perform a subtraction operation. + /// + /// This operation requires matching types. + /// + /// This corresponds to the DWARF `DW_OP_minus` operation. + pub fn sub(self, rhs: Value, addr_mask: u64) -> Result<Value> { + let value = match (self, rhs) { + (Value::Generic(v1), Value::Generic(v2)) => { + Value::Generic(v1.wrapping_sub(v2) & addr_mask) + } + (Value::I8(v1), Value::I8(v2)) => Value::I8(v1.wrapping_sub(v2)), + (Value::U8(v1), Value::U8(v2)) => Value::U8(v1.wrapping_sub(v2)), + (Value::I16(v1), Value::I16(v2)) => Value::I16(v1.wrapping_sub(v2)), + (Value::U16(v1), Value::U16(v2)) => Value::U16(v1.wrapping_sub(v2)), + (Value::I32(v1), Value::I32(v2)) => Value::I32(v1.wrapping_sub(v2)), + (Value::U32(v1), Value::U32(v2)) => Value::U32(v1.wrapping_sub(v2)), + (Value::I64(v1), Value::I64(v2)) => Value::I64(v1.wrapping_sub(v2)), + (Value::U64(v1), Value::U64(v2)) => Value::U64(v1.wrapping_sub(v2)), + (Value::F32(v1), Value::F32(v2)) => Value::F32(v1 - v2), + (Value::F64(v1), Value::F64(v2)) => Value::F64(v1 - v2), + _ => return Err(Error::TypeMismatch), + }; + Ok(value) + } + + /// Perform a multiplication operation. + /// + /// This operation requires matching types. + /// + /// This corresponds to the DWARF `DW_OP_mul` operation. + pub fn mul(self, rhs: Value, addr_mask: u64) -> Result<Value> { + let value = match (self, rhs) { + (Value::Generic(v1), Value::Generic(v2)) => { + Value::Generic(v1.wrapping_mul(v2) & addr_mask) + } + (Value::I8(v1), Value::I8(v2)) => Value::I8(v1.wrapping_mul(v2)), + (Value::U8(v1), Value::U8(v2)) => Value::U8(v1.wrapping_mul(v2)), + (Value::I16(v1), Value::I16(v2)) => Value::I16(v1.wrapping_mul(v2)), + (Value::U16(v1), Value::U16(v2)) => Value::U16(v1.wrapping_mul(v2)), + (Value::I32(v1), Value::I32(v2)) => Value::I32(v1.wrapping_mul(v2)), + (Value::U32(v1), Value::U32(v2)) => Value::U32(v1.wrapping_mul(v2)), + (Value::I64(v1), Value::I64(v2)) => Value::I64(v1.wrapping_mul(v2)), + (Value::U64(v1), Value::U64(v2)) => Value::U64(v1.wrapping_mul(v2)), + (Value::F32(v1), Value::F32(v2)) => Value::F32(v1 * v2), + (Value::F64(v1), Value::F64(v2)) => Value::F64(v1 * v2), + _ => return Err(Error::TypeMismatch), + }; + Ok(value) + } + + /// Perform a division operation. + /// + /// This operation requires matching types. + /// If the value type is `Generic`, then it is interpreted as a signed value. + /// + /// This corresponds to the DWARF `DW_OP_div` operation. + pub fn div(self, rhs: Value, addr_mask: u64) -> Result<Value> { + match rhs { + Value::Generic(v2) if sign_extend(v2, addr_mask) == 0 => { + return Err(Error::DivisionByZero); + } + Value::I8(0) + | Value::U8(0) + | Value::I16(0) + | Value::U16(0) + | Value::I32(0) + | Value::U32(0) + | Value::I64(0) + | Value::U64(0) => { + return Err(Error::DivisionByZero); + } + _ => {} + } + let value = match (self, rhs) { + (Value::Generic(v1), Value::Generic(v2)) => { + // Signed division + Value::Generic( + sign_extend(v1, addr_mask).wrapping_div(sign_extend(v2, addr_mask)) as u64, + ) + } + (Value::I8(v1), Value::I8(v2)) => Value::I8(v1.wrapping_div(v2)), + (Value::U8(v1), Value::U8(v2)) => Value::U8(v1.wrapping_div(v2)), + (Value::I16(v1), Value::I16(v2)) => Value::I16(v1.wrapping_div(v2)), + (Value::U16(v1), Value::U16(v2)) => Value::U16(v1.wrapping_div(v2)), + (Value::I32(v1), Value::I32(v2)) => Value::I32(v1.wrapping_div(v2)), + (Value::U32(v1), Value::U32(v2)) => Value::U32(v1.wrapping_div(v2)), + (Value::I64(v1), Value::I64(v2)) => Value::I64(v1.wrapping_div(v2)), + (Value::U64(v1), Value::U64(v2)) => Value::U64(v1.wrapping_div(v2)), + (Value::F32(v1), Value::F32(v2)) => Value::F32(v1 / v2), + (Value::F64(v1), Value::F64(v2)) => Value::F64(v1 / v2), + _ => return Err(Error::TypeMismatch), + }; + Ok(value) + } + + /// Perform a remainder operation. + /// + /// This operation requires matching integral types. + /// If the value type is `Generic`, then it is interpreted as an unsigned value. + /// + /// This corresponds to the DWARF `DW_OP_mod` operation. + pub fn rem(self, rhs: Value, addr_mask: u64) -> Result<Value> { + match rhs { + Value::Generic(rhs) if (rhs & addr_mask) == 0 => { + return Err(Error::DivisionByZero); + } + Value::I8(0) + | Value::U8(0) + | Value::I16(0) + | Value::U16(0) + | Value::I32(0) + | Value::U32(0) + | Value::I64(0) + | Value::U64(0) => { + return Err(Error::DivisionByZero); + } + _ => {} + } + let value = match (self, rhs) { + (Value::Generic(v1), Value::Generic(v2)) => { + // Unsigned modulus + Value::Generic((v1 & addr_mask).wrapping_rem(v2 & addr_mask)) + } + (Value::I8(v1), Value::I8(v2)) => Value::I8(v1.wrapping_rem(v2)), + (Value::U8(v1), Value::U8(v2)) => Value::U8(v1.wrapping_rem(v2)), + (Value::I16(v1), Value::I16(v2)) => Value::I16(v1.wrapping_rem(v2)), + (Value::U16(v1), Value::U16(v2)) => Value::U16(v1.wrapping_rem(v2)), + (Value::I32(v1), Value::I32(v2)) => Value::I32(v1.wrapping_rem(v2)), + (Value::U32(v1), Value::U32(v2)) => Value::U32(v1.wrapping_rem(v2)), + (Value::I64(v1), Value::I64(v2)) => Value::I64(v1.wrapping_rem(v2)), + (Value::U64(v1), Value::U64(v2)) => Value::U64(v1.wrapping_rem(v2)), + (Value::F32(_), Value::F32(_)) => return Err(Error::IntegralTypeRequired), + (Value::F64(_), Value::F64(_)) => return Err(Error::IntegralTypeRequired), + _ => return Err(Error::TypeMismatch), + }; + Ok(value) + } + + /// Perform a bitwise not operation. + /// + /// This operation requires matching integral types. + /// + /// This corresponds to the DWARF `DW_OP_not` operation. + pub fn not(self, addr_mask: u64) -> Result<Value> { + let value_type = self.value_type(); + let v = self.to_u64(addr_mask)?; + Value::from_u64(value_type, !v) + } + + /// Perform a bitwise and operation. + /// + /// This operation requires matching integral types. + /// + /// This corresponds to the DWARF `DW_OP_and` operation. + pub fn and(self, rhs: Value, addr_mask: u64) -> Result<Value> { + let value_type = self.value_type(); + if value_type != rhs.value_type() { + return Err(Error::TypeMismatch); + } + let v1 = self.to_u64(addr_mask)?; + let v2 = rhs.to_u64(addr_mask)?; + Value::from_u64(value_type, v1 & v2) + } + + /// Perform a bitwise or operation. + /// + /// This operation requires matching integral types. + /// + /// This corresponds to the DWARF `DW_OP_or` operation. + pub fn or(self, rhs: Value, addr_mask: u64) -> Result<Value> { + let value_type = self.value_type(); + if value_type != rhs.value_type() { + return Err(Error::TypeMismatch); + } + let v1 = self.to_u64(addr_mask)?; + let v2 = rhs.to_u64(addr_mask)?; + Value::from_u64(value_type, v1 | v2) + } + + /// Perform a bitwise exclusive-or operation. + /// + /// This operation requires matching integral types. + /// + /// This corresponds to the DWARF `DW_OP_xor` operation. + pub fn xor(self, rhs: Value, addr_mask: u64) -> Result<Value> { + let value_type = self.value_type(); + if value_type != rhs.value_type() { + return Err(Error::TypeMismatch); + } + let v1 = self.to_u64(addr_mask)?; + let v2 = rhs.to_u64(addr_mask)?; + Value::from_u64(value_type, v1 ^ v2) + } + + /// Convert value to bit length suitable for a shift operation. + /// + /// If the value is negative then an error is returned. + fn shift_length(self) -> Result<u64> { + let value = match self { + Value::Generic(value) => value, + Value::I8(value) if value >= 0 => value as u64, + Value::U8(value) => u64::from(value), + Value::I16(value) if value >= 0 => value as u64, + Value::U16(value) => u64::from(value), + Value::I32(value) if value >= 0 => value as u64, + Value::U32(value) => u64::from(value), + Value::I64(value) if value >= 0 => value as u64, + Value::U64(value) => value, + _ => return Err(Error::InvalidShiftExpression), + }; + Ok(value) + } + + /// Perform a shift left operation. + /// + /// This operation requires integral types. + /// If the shift length exceeds the type size, then 0 is returned. + /// If the shift length is negative then an error is returned. + /// + /// This corresponds to the DWARF `DW_OP_shl` operation. + pub fn shl(self, rhs: Value, addr_mask: u64) -> Result<Value> { + let v2 = rhs.shift_length()?; + let value = match self { + Value::Generic(v1) => Value::Generic(if v2 >= u64::from(mask_bit_size(addr_mask)) { + 0 + } else { + (v1 & addr_mask) << v2 + }), + Value::I8(v1) => Value::I8(if v2 >= 8 { 0 } else { v1 << v2 }), + Value::U8(v1) => Value::U8(if v2 >= 8 { 0 } else { v1 << v2 }), + Value::I16(v1) => Value::I16(if v2 >= 16 { 0 } else { v1 << v2 }), + Value::U16(v1) => Value::U16(if v2 >= 16 { 0 } else { v1 << v2 }), + Value::I32(v1) => Value::I32(if v2 >= 32 { 0 } else { v1 << v2 }), + Value::U32(v1) => Value::U32(if v2 >= 32 { 0 } else { v1 << v2 }), + Value::I64(v1) => Value::I64(if v2 >= 64 { 0 } else { v1 << v2 }), + Value::U64(v1) => Value::U64(if v2 >= 64 { 0 } else { v1 << v2 }), + _ => return Err(Error::IntegralTypeRequired), + }; + Ok(value) + } + + /// Perform a logical shift right operation. + /// + /// This operation requires an unsigned integral type for the value. + /// If the value type is `Generic`, then it is interpreted as an unsigned value. + /// + /// This operation requires an integral type for the shift length. + /// If the shift length exceeds the type size, then 0 is returned. + /// If the shift length is negative then an error is returned. + /// + /// This corresponds to the DWARF `DW_OP_shr` operation. + pub fn shr(self, rhs: Value, addr_mask: u64) -> Result<Value> { + let v2 = rhs.shift_length()?; + let value = match self { + Value::Generic(v1) => Value::Generic(if v2 >= u64::from(mask_bit_size(addr_mask)) { + 0 + } else { + (v1 & addr_mask) >> v2 + }), + Value::U8(v1) => Value::U8(if v2 >= 8 { 0 } else { v1 >> v2 }), + Value::U16(v1) => Value::U16(if v2 >= 16 { 0 } else { v1 >> v2 }), + Value::U32(v1) => Value::U32(if v2 >= 32 { 0 } else { v1 >> v2 }), + Value::U64(v1) => Value::U64(if v2 >= 64 { 0 } else { v1 >> v2 }), + // It's unclear if signed values should implicitly convert to an unsigned value. + // For now, we don't support them. + Value::I8(_) | Value::I16(_) | Value::I32(_) | Value::I64(_) => { + return Err(Error::UnsupportedTypeOperation); + } + _ => return Err(Error::IntegralTypeRequired), + }; + Ok(value) + } + + /// Perform an arithmetic shift right operation. + /// + /// This operation requires a signed integral type for the value. + /// If the value type is `Generic`, then it is interpreted as a signed value. + /// + /// This operation requires an integral type for the shift length. + /// If the shift length exceeds the type size, then 0 is returned for positive values, + /// and -1 is returned for negative values. + /// If the shift length is negative then an error is returned. + /// + /// This corresponds to the DWARF `DW_OP_shra` operation. + pub fn shra(self, rhs: Value, addr_mask: u64) -> Result<Value> { + let v2 = rhs.shift_length()?; + let value = match self { + Value::Generic(v1) => { + let v1 = sign_extend(v1, addr_mask); + let value = if v2 >= u64::from(mask_bit_size(addr_mask)) { + if v1 < 0 { + !0 + } else { + 0 + } + } else { + (v1 >> v2) as u64 + }; + Value::Generic(value) + } + Value::I8(v1) => Value::I8(if v2 >= 8 { + if v1 < 0 { + !0 + } else { + 0 + } + } else { + v1 >> v2 + }), + Value::I16(v1) => Value::I16(if v2 >= 16 { + if v1 < 0 { + !0 + } else { + 0 + } + } else { + v1 >> v2 + }), + Value::I32(v1) => Value::I32(if v2 >= 32 { + if v1 < 0 { + !0 + } else { + 0 + } + } else { + v1 >> v2 + }), + Value::I64(v1) => Value::I64(if v2 >= 64 { + if v1 < 0 { + !0 + } else { + 0 + } + } else { + v1 >> v2 + }), + // It's unclear if unsigned values should implicitly convert to a signed value. + // For now, we don't support them. + Value::U8(_) | Value::U16(_) | Value::U32(_) | Value::U64(_) => { + return Err(Error::UnsupportedTypeOperation); + } + _ => return Err(Error::IntegralTypeRequired), + }; + Ok(value) + } + + /// Perform the `==` relational operation. + /// + /// This operation requires matching integral types. + /// If the value type is `Generic`, then it is interpreted as a signed value. + /// + /// This corresponds to the DWARF `DW_OP_eq` operation. + pub fn eq(self, rhs: Value, addr_mask: u64) -> Result<Value> { + let value = match (self, rhs) { + (Value::Generic(v1), Value::Generic(v2)) => { + sign_extend(v1, addr_mask) == sign_extend(v2, addr_mask) + } + (Value::I8(v1), Value::I8(v2)) => v1 == v2, + (Value::U8(v1), Value::U8(v2)) => v1 == v2, + (Value::I16(v1), Value::I16(v2)) => v1 == v2, + (Value::U16(v1), Value::U16(v2)) => v1 == v2, + (Value::I32(v1), Value::I32(v2)) => v1 == v2, + (Value::U32(v1), Value::U32(v2)) => v1 == v2, + (Value::I64(v1), Value::I64(v2)) => v1 == v2, + (Value::U64(v1), Value::U64(v2)) => v1 == v2, + (Value::F32(v1), Value::F32(v2)) => v1 == v2, + (Value::F64(v1), Value::F64(v2)) => v1 == v2, + _ => return Err(Error::TypeMismatch), + }; + Ok(Value::Generic(value as u64)) + } + + /// Perform the `>=` relational operation. + /// + /// This operation requires matching integral types. + /// If the value type is `Generic`, then it is interpreted as a signed value. + /// + /// This corresponds to the DWARF `DW_OP_ge` operation. + pub fn ge(self, rhs: Value, addr_mask: u64) -> Result<Value> { + let value = match (self, rhs) { + (Value::Generic(v1), Value::Generic(v2)) => { + sign_extend(v1, addr_mask) >= sign_extend(v2, addr_mask) + } + (Value::I8(v1), Value::I8(v2)) => v1 >= v2, + (Value::U8(v1), Value::U8(v2)) => v1 >= v2, + (Value::I16(v1), Value::I16(v2)) => v1 >= v2, + (Value::U16(v1), Value::U16(v2)) => v1 >= v2, + (Value::I32(v1), Value::I32(v2)) => v1 >= v2, + (Value::U32(v1), Value::U32(v2)) => v1 >= v2, + (Value::I64(v1), Value::I64(v2)) => v1 >= v2, + (Value::U64(v1), Value::U64(v2)) => v1 >= v2, + (Value::F32(v1), Value::F32(v2)) => v1 >= v2, + (Value::F64(v1), Value::F64(v2)) => v1 >= v2, + _ => return Err(Error::TypeMismatch), + }; + Ok(Value::Generic(value as u64)) + } + + /// Perform the `>` relational operation. + /// + /// This operation requires matching integral types. + /// If the value type is `Generic`, then it is interpreted as a signed value. + /// + /// This corresponds to the DWARF `DW_OP_gt` operation. + pub fn gt(self, rhs: Value, addr_mask: u64) -> Result<Value> { + let value = match (self, rhs) { + (Value::Generic(v1), Value::Generic(v2)) => { + sign_extend(v1, addr_mask) > sign_extend(v2, addr_mask) + } + (Value::I8(v1), Value::I8(v2)) => v1 > v2, + (Value::U8(v1), Value::U8(v2)) => v1 > v2, + (Value::I16(v1), Value::I16(v2)) => v1 > v2, + (Value::U16(v1), Value::U16(v2)) => v1 > v2, + (Value::I32(v1), Value::I32(v2)) => v1 > v2, + (Value::U32(v1), Value::U32(v2)) => v1 > v2, + (Value::I64(v1), Value::I64(v2)) => v1 > v2, + (Value::U64(v1), Value::U64(v2)) => v1 > v2, + (Value::F32(v1), Value::F32(v2)) => v1 > v2, + (Value::F64(v1), Value::F64(v2)) => v1 > v2, + _ => return Err(Error::TypeMismatch), + }; + Ok(Value::Generic(value as u64)) + } + + /// Perform the `<= relational operation. + /// + /// This operation requires matching integral types. + /// If the value type is `Generic`, then it is interpreted as a signed value. + /// + /// This corresponds to the DWARF `DW_OP_le` operation. + pub fn le(self, rhs: Value, addr_mask: u64) -> Result<Value> { + let value = match (self, rhs) { + (Value::Generic(v1), Value::Generic(v2)) => { + sign_extend(v1, addr_mask) <= sign_extend(v2, addr_mask) + } + (Value::I8(v1), Value::I8(v2)) => v1 <= v2, + (Value::U8(v1), Value::U8(v2)) => v1 <= v2, + (Value::I16(v1), Value::I16(v2)) => v1 <= v2, + (Value::U16(v1), Value::U16(v2)) => v1 <= v2, + (Value::I32(v1), Value::I32(v2)) => v1 <= v2, + (Value::U32(v1), Value::U32(v2)) => v1 <= v2, + (Value::I64(v1), Value::I64(v2)) => v1 <= v2, + (Value::U64(v1), Value::U64(v2)) => v1 <= v2, + (Value::F32(v1), Value::F32(v2)) => v1 <= v2, + (Value::F64(v1), Value::F64(v2)) => v1 <= v2, + _ => return Err(Error::TypeMismatch), + }; + Ok(Value::Generic(value as u64)) + } + + /// Perform the `< relational operation. + /// + /// This operation requires matching integral types. + /// If the value type is `Generic`, then it is interpreted as a signed value. + /// + /// This corresponds to the DWARF `DW_OP_lt` operation. + pub fn lt(self, rhs: Value, addr_mask: u64) -> Result<Value> { + let value = match (self, rhs) { + (Value::Generic(v1), Value::Generic(v2)) => { + sign_extend(v1, addr_mask) < sign_extend(v2, addr_mask) + } + (Value::I8(v1), Value::I8(v2)) => v1 < v2, + (Value::U8(v1), Value::U8(v2)) => v1 < v2, + (Value::I16(v1), Value::I16(v2)) => v1 < v2, + (Value::U16(v1), Value::U16(v2)) => v1 < v2, + (Value::I32(v1), Value::I32(v2)) => v1 < v2, + (Value::U32(v1), Value::U32(v2)) => v1 < v2, + (Value::I64(v1), Value::I64(v2)) => v1 < v2, + (Value::U64(v1), Value::U64(v2)) => v1 < v2, + (Value::F32(v1), Value::F32(v2)) => v1 < v2, + (Value::F64(v1), Value::F64(v2)) => v1 < v2, + _ => return Err(Error::TypeMismatch), + }; + Ok(Value::Generic(value as u64)) + } + + /// Perform the `!= relational operation. + /// + /// This operation requires matching integral types. + /// If the value type is `Generic`, then it is interpreted as a signed value. + /// + /// This corresponds to the DWARF `DW_OP_ne` operation. + pub fn ne(self, rhs: Value, addr_mask: u64) -> Result<Value> { + let value = match (self, rhs) { + (Value::Generic(v1), Value::Generic(v2)) => { + sign_extend(v1, addr_mask) != sign_extend(v2, addr_mask) + } + (Value::I8(v1), Value::I8(v2)) => v1 != v2, + (Value::U8(v1), Value::U8(v2)) => v1 != v2, + (Value::I16(v1), Value::I16(v2)) => v1 != v2, + (Value::U16(v1), Value::U16(v2)) => v1 != v2, + (Value::I32(v1), Value::I32(v2)) => v1 != v2, + (Value::U32(v1), Value::U32(v2)) => v1 != v2, + (Value::I64(v1), Value::I64(v2)) => v1 != v2, + (Value::U64(v1), Value::U64(v2)) => v1 != v2, + (Value::F32(v1), Value::F32(v2)) => v1 != v2, + (Value::F64(v1), Value::F64(v2)) => v1 != v2, + _ => return Err(Error::TypeMismatch), + }; + Ok(Value::Generic(value as u64)) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::common::{DebugAbbrevOffset, DebugInfoOffset, Encoding, Format}; + use crate::endianity::LittleEndian; + use crate::read::{ + Abbreviation, AttributeSpecification, DebuggingInformationEntry, EndianSlice, UnitHeader, + UnitOffset, UnitType, + }; + + #[test] + #[rustfmt::skip] + fn valuetype_from_encoding() { + let encoding = Encoding { + format: Format::Dwarf32, + version: 4, + address_size: 4, + }; + let unit = UnitHeader::new( + encoding, + 7, + UnitType::Compilation, + DebugAbbrevOffset(0), + DebugInfoOffset(0).into(), + EndianSlice::new(&[], LittleEndian), + ); + + let abbrev = Abbreviation::new( + 42, + constants::DW_TAG_base_type, + constants::DW_CHILDREN_no, + vec![ + AttributeSpecification::new( + constants::DW_AT_byte_size, + constants::DW_FORM_udata, + None, + ), + AttributeSpecification::new( + constants::DW_AT_encoding, + constants::DW_FORM_udata, + None, + ), + AttributeSpecification::new( + constants::DW_AT_endianity, + constants::DW_FORM_udata, + None, + ), + ].into(), + ); + + for &(attrs, result) in &[ + ([0x01, constants::DW_ATE_signed.0, constants::DW_END_default.0], ValueType::I8), + ([0x02, constants::DW_ATE_signed.0, constants::DW_END_default.0], ValueType::I16), + ([0x04, constants::DW_ATE_signed.0, constants::DW_END_default.0], ValueType::I32), + ([0x08, constants::DW_ATE_signed.0, constants::DW_END_default.0], ValueType::I64), + ([0x01, constants::DW_ATE_unsigned.0, constants::DW_END_default.0], ValueType::U8), + ([0x02, constants::DW_ATE_unsigned.0, constants::DW_END_default.0], ValueType::U16), + ([0x04, constants::DW_ATE_unsigned.0, constants::DW_END_default.0], ValueType::U32), + ([0x08, constants::DW_ATE_unsigned.0, constants::DW_END_default.0], ValueType::U64), + ([0x04, constants::DW_ATE_float.0, constants::DW_END_default.0], ValueType::F32), + ([0x08, constants::DW_ATE_float.0, constants::DW_END_default.0], ValueType::F64), + ] { + let entry = DebuggingInformationEntry::new( + UnitOffset(0), + EndianSlice::new(&attrs, LittleEndian), + &abbrev, + &unit, + ); + assert_eq!(ValueType::from_entry(&entry), Ok(Some(result))); + } + + for attrs in &[ + [0x03, constants::DW_ATE_signed.0, constants::DW_END_default.0], + [0x02, constants::DW_ATE_signed.0, constants::DW_END_big.0], + ] { + let entry = DebuggingInformationEntry::new( + UnitOffset(0), + EndianSlice::new(attrs, LittleEndian), + &abbrev, + &unit, + ); + assert_eq!(ValueType::from_entry(&entry), Ok(None)); + } + } + + #[test] + fn value_convert() { + let addr_mask = !0 >> 32; + for &(v, t, result) in &[ + (Value::Generic(1), ValueType::I8, Ok(Value::I8(1))), + (Value::I8(1), ValueType::U8, Ok(Value::U8(1))), + (Value::U8(1), ValueType::I16, Ok(Value::I16(1))), + (Value::I16(1), ValueType::U16, Ok(Value::U16(1))), + (Value::U16(1), ValueType::I32, Ok(Value::I32(1))), + (Value::I32(1), ValueType::U32, Ok(Value::U32(1))), + (Value::U32(1), ValueType::F32, Ok(Value::F32(1.))), + (Value::F32(1.), ValueType::I64, Ok(Value::I64(1))), + (Value::I64(1), ValueType::U64, Ok(Value::U64(1))), + (Value::U64(1), ValueType::F64, Ok(Value::F64(1.))), + (Value::F64(1.), ValueType::Generic, Ok(Value::Generic(1))), + ] { + assert_eq!(v.convert(t, addr_mask), result); + } + } + + #[test] + #[rustfmt::skip] + fn value_reinterpret() { + let addr_mask = !0 >> 32; + for &(v, t, result) in &[ + // 8-bit + (Value::I8(-1), ValueType::U8, Ok(Value::U8(0xff))), + (Value::U8(0xff), ValueType::I8, Ok(Value::I8(-1))), + // 16-bit + (Value::I16(1), ValueType::U16, Ok(Value::U16(1))), + (Value::U16(1), ValueType::I16, Ok(Value::I16(1))), + // 32-bit + (Value::Generic(1), ValueType::I32, Ok(Value::I32(1))), + (Value::I32(1), ValueType::U32, Ok(Value::U32(1))), + (Value::U32(0x3f80_0000), ValueType::F32, Ok(Value::F32(1.0))), + (Value::F32(1.0), ValueType::Generic, Ok(Value::Generic(0x3f80_0000))), + // Type mismatches + (Value::Generic(1), ValueType::U8, Err(Error::TypeMismatch)), + (Value::U8(1), ValueType::U16, Err(Error::TypeMismatch)), + (Value::U16(1), ValueType::U32, Err(Error::TypeMismatch)), + (Value::U32(1), ValueType::U64, Err(Error::TypeMismatch)), + (Value::U64(1), ValueType::Generic, Err(Error::TypeMismatch)), + ] { + assert_eq!(v.reinterpret(t, addr_mask), result); + } + + let addr_mask = !0; + for &(v, t, result) in &[ + // 64-bit + (Value::Generic(1), ValueType::I64, Ok(Value::I64(1))), + (Value::I64(1), ValueType::U64, Ok(Value::U64(1))), + (Value::U64(0x3ff0_0000_0000_0000), ValueType::F64, Ok(Value::F64(1.0))), + (Value::F64(1.0), ValueType::Generic, Ok(Value::Generic(0x3ff0_0000_0000_0000))), + ] { + assert_eq!(v.reinterpret(t, addr_mask), result); + } + } + + #[test] + #[rustfmt::skip] + fn value_abs() { + let addr_mask = 0xffff_ffff; + for &(v, result) in &[ + (Value::Generic(0xffff_ffff), Ok(Value::Generic(1))), + (Value::I8(-1), Ok(Value::I8(1))), + (Value::U8(1), Ok(Value::U8(1))), + (Value::I16(-1), Ok(Value::I16(1))), + (Value::U16(1), Ok(Value::U16(1))), + (Value::I32(-1), Ok(Value::I32(1))), + (Value::U32(1), Ok(Value::U32(1))), + (Value::I64(-1), Ok(Value::I64(1))), + (Value::U64(1), Ok(Value::U64(1))), + (Value::F32(-1.), Ok(Value::F32(1.))), + (Value::F64(-1.), Ok(Value::F64(1.))), + ] { + assert_eq!(v.abs(addr_mask), result); + } + } + + #[test] + #[rustfmt::skip] + fn value_neg() { + let addr_mask = 0xffff_ffff; + for &(v, result) in &[ + (Value::Generic(0xffff_ffff), Ok(Value::Generic(1))), + (Value::I8(1), Ok(Value::I8(-1))), + (Value::U8(1), Err(Error::UnsupportedTypeOperation)), + (Value::I16(1), Ok(Value::I16(-1))), + (Value::U16(1), Err(Error::UnsupportedTypeOperation)), + (Value::I32(1), Ok(Value::I32(-1))), + (Value::U32(1), Err(Error::UnsupportedTypeOperation)), + (Value::I64(1), Ok(Value::I64(-1))), + (Value::U64(1), Err(Error::UnsupportedTypeOperation)), + (Value::F32(1.), Ok(Value::F32(-1.))), + (Value::F64(1.), Ok(Value::F64(-1.))), + ] { + assert_eq!(v.neg(addr_mask), result); + } + } + + #[test] + #[rustfmt::skip] + fn value_add() { + let addr_mask = 0xffff_ffff; + for &(v1, v2, result) in &[ + (Value::Generic(1), Value::Generic(2), Ok(Value::Generic(3))), + (Value::I8(-1), Value::I8(2), Ok(Value::I8(1))), + (Value::U8(1), Value::U8(2), Ok(Value::U8(3))), + (Value::I16(-1), Value::I16(2), Ok(Value::I16(1))), + (Value::U16(1), Value::U16(2), Ok(Value::U16(3))), + (Value::I32(-1), Value::I32(2), Ok(Value::I32(1))), + (Value::U32(1), Value::U32(2), Ok(Value::U32(3))), + (Value::I64(-1), Value::I64(2), Ok(Value::I64(1))), + (Value::U64(1), Value::U64(2), Ok(Value::U64(3))), + (Value::F32(-1.), Value::F32(2.), Ok(Value::F32(1.))), + (Value::F64(-1.), Value::F64(2.), Ok(Value::F64(1.))), + (Value::Generic(1), Value::U32(2), Err(Error::TypeMismatch)), + ] { + assert_eq!(v1.add(v2, addr_mask), result); + } + } + + #[test] + #[rustfmt::skip] + fn value_sub() { + let addr_mask = 0xffff_ffff; + for &(v1, v2, result) in &[ + (Value::Generic(3), Value::Generic(2), Ok(Value::Generic(1))), + (Value::I8(-1), Value::I8(2), Ok(Value::I8(-3))), + (Value::U8(3), Value::U8(2), Ok(Value::U8(1))), + (Value::I16(-1), Value::I16(2), Ok(Value::I16(-3))), + (Value::U16(3), Value::U16(2), Ok(Value::U16(1))), + (Value::I32(-1), Value::I32(2), Ok(Value::I32(-3))), + (Value::U32(3), Value::U32(2), Ok(Value::U32(1))), + (Value::I64(-1), Value::I64(2), Ok(Value::I64(-3))), + (Value::U64(3), Value::U64(2), Ok(Value::U64(1))), + (Value::F32(-1.), Value::F32(2.), Ok(Value::F32(-3.))), + (Value::F64(-1.), Value::F64(2.), Ok(Value::F64(-3.))), + (Value::Generic(3), Value::U32(2), Err(Error::TypeMismatch)), + ] { + assert_eq!(v1.sub(v2, addr_mask), result); + } + } + + #[test] + #[rustfmt::skip] + fn value_mul() { + let addr_mask = 0xffff_ffff; + for &(v1, v2, result) in &[ + (Value::Generic(2), Value::Generic(3), Ok(Value::Generic(6))), + (Value::I8(-2), Value::I8(3), Ok(Value::I8(-6))), + (Value::U8(2), Value::U8(3), Ok(Value::U8(6))), + (Value::I16(-2), Value::I16(3), Ok(Value::I16(-6))), + (Value::U16(2), Value::U16(3), Ok(Value::U16(6))), + (Value::I32(-2), Value::I32(3), Ok(Value::I32(-6))), + (Value::U32(2), Value::U32(3), Ok(Value::U32(6))), + (Value::I64(-2), Value::I64(3), Ok(Value::I64(-6))), + (Value::U64(2), Value::U64(3), Ok(Value::U64(6))), + (Value::F32(-2.), Value::F32(3.), Ok(Value::F32(-6.))), + (Value::F64(-2.), Value::F64(3.), Ok(Value::F64(-6.))), + (Value::Generic(2), Value::U32(3), Err(Error::TypeMismatch)), + ] { + assert_eq!(v1.mul(v2, addr_mask), result); + } + } + + #[test] + #[rustfmt::skip] + fn value_div() { + let addr_mask = 0xffff_ffff; + for &(v1, v2, result) in &[ + (Value::Generic(6), Value::Generic(3), Ok(Value::Generic(2))), + (Value::I8(-6), Value::I8(3), Ok(Value::I8(-2))), + (Value::U8(6), Value::U8(3), Ok(Value::U8(2))), + (Value::I16(-6), Value::I16(3), Ok(Value::I16(-2))), + (Value::U16(6), Value::U16(3), Ok(Value::U16(2))), + (Value::I32(-6), Value::I32(3), Ok(Value::I32(-2))), + (Value::U32(6), Value::U32(3), Ok(Value::U32(2))), + (Value::I64(-6), Value::I64(3), Ok(Value::I64(-2))), + (Value::U64(6), Value::U64(3), Ok(Value::U64(2))), + (Value::F32(-6.), Value::F32(3.), Ok(Value::F32(-2.))), + (Value::F64(-6.), Value::F64(3.), Ok(Value::F64(-2.))), + (Value::Generic(6), Value::U32(3), Err(Error::TypeMismatch)), + ] { + assert_eq!(v1.div(v2, addr_mask), result); + } + for &(v1, v2, result) in &[ + (Value::Generic(6), Value::Generic(0), Err(Error::DivisionByZero)), + (Value::I8(-6), Value::I8(0), Err(Error::DivisionByZero)), + (Value::U8(6), Value::U8(0), Err(Error::DivisionByZero)), + (Value::I16(-6), Value::I16(0), Err(Error::DivisionByZero)), + (Value::U16(6), Value::U16(0), Err(Error::DivisionByZero)), + (Value::I32(-6), Value::I32(0), Err(Error::DivisionByZero)), + (Value::U32(6), Value::U32(0), Err(Error::DivisionByZero)), + (Value::I64(-6), Value::I64(0), Err(Error::DivisionByZero)), + (Value::U64(6), Value::U64(0), Err(Error::DivisionByZero)), + (Value::F32(-6.), Value::F32(0.), Ok(Value::F32(-6. / 0.))), + (Value::F64(-6.), Value::F64(0.), Ok(Value::F64(-6. / 0.))), + ] { + assert_eq!(v1.div(v2, addr_mask), result); + } + } + + #[test] + #[rustfmt::skip] + fn value_rem() { + let addr_mask = 0xffff_ffff; + for &(v1, v2, result) in &[ + (Value::Generic(3), Value::Generic(2), Ok(Value::Generic(1))), + (Value::I8(-3), Value::I8(2), Ok(Value::I8(-1))), + (Value::U8(3), Value::U8(2), Ok(Value::U8(1))), + (Value::I16(-3), Value::I16(2), Ok(Value::I16(-1))), + (Value::U16(3), Value::U16(2), Ok(Value::U16(1))), + (Value::I32(-3), Value::I32(2), Ok(Value::I32(-1))), + (Value::U32(3), Value::U32(2), Ok(Value::U32(1))), + (Value::I64(-3), Value::I64(2), Ok(Value::I64(-1))), + (Value::U64(3), Value::U64(2), Ok(Value::U64(1))), + (Value::F32(-3.), Value::F32(2.), Err(Error::IntegralTypeRequired)), + (Value::F64(-3.), Value::F64(2.), Err(Error::IntegralTypeRequired)), + (Value::Generic(3), Value::U32(2), Err(Error::TypeMismatch)), + ] { + assert_eq!(v1.rem(v2, addr_mask), result); + } + for &(v1, v2, result) in &[ + (Value::Generic(3), Value::Generic(0), Err(Error::DivisionByZero)), + (Value::I8(-3), Value::I8(0), Err(Error::DivisionByZero)), + (Value::U8(3), Value::U8(0), Err(Error::DivisionByZero)), + (Value::I16(-3), Value::I16(0), Err(Error::DivisionByZero)), + (Value::U16(3), Value::U16(0), Err(Error::DivisionByZero)), + (Value::I32(-3), Value::I32(0), Err(Error::DivisionByZero)), + (Value::U32(3), Value::U32(0), Err(Error::DivisionByZero)), + (Value::I64(-3), Value::I64(0), Err(Error::DivisionByZero)), + (Value::U64(3), Value::U64(0), Err(Error::DivisionByZero)), + ] { + assert_eq!(v1.rem(v2, addr_mask), result); + } + } + + #[test] + #[rustfmt::skip] + fn value_not() { + let addr_mask = 0xffff_ffff; + for &(v, result) in &[ + (Value::Generic(1), Ok(Value::Generic(!1))), + (Value::I8(1), Ok(Value::I8(!1))), + (Value::U8(1), Ok(Value::U8(!1))), + (Value::I16(1), Ok(Value::I16(!1))), + (Value::U16(1), Ok(Value::U16(!1))), + (Value::I32(1), Ok(Value::I32(!1))), + (Value::U32(1), Ok(Value::U32(!1))), + (Value::I64(1), Ok(Value::I64(!1))), + (Value::U64(1), Ok(Value::U64(!1))), + (Value::F32(1.), Err(Error::IntegralTypeRequired)), + (Value::F64(1.), Err(Error::IntegralTypeRequired)), + ] { + assert_eq!(v.not(addr_mask), result); + } + } + + #[test] + #[rustfmt::skip] + fn value_and() { + let addr_mask = 0xffff_ffff; + for &(v1, v2, result) in &[ + (Value::Generic(3), Value::Generic(5), Ok(Value::Generic(1))), + (Value::I8(3), Value::I8(5), Ok(Value::I8(1))), + (Value::U8(3), Value::U8(5), Ok(Value::U8(1))), + (Value::I16(3), Value::I16(5), Ok(Value::I16(1))), + (Value::U16(3), Value::U16(5), Ok(Value::U16(1))), + (Value::I32(3), Value::I32(5), Ok(Value::I32(1))), + (Value::U32(3), Value::U32(5), Ok(Value::U32(1))), + (Value::I64(3), Value::I64(5), Ok(Value::I64(1))), + (Value::U64(3), Value::U64(5), Ok(Value::U64(1))), + (Value::F32(3.), Value::F32(5.), Err(Error::IntegralTypeRequired)), + (Value::F64(3.), Value::F64(5.), Err(Error::IntegralTypeRequired)), + (Value::Generic(3), Value::U32(5), Err(Error::TypeMismatch)), + ] { + assert_eq!(v1.and(v2, addr_mask), result); + } + } + + #[test] + #[rustfmt::skip] + fn value_or() { + let addr_mask = 0xffff_ffff; + for &(v1, v2, result) in &[ + (Value::Generic(3), Value::Generic(5), Ok(Value::Generic(7))), + (Value::I8(3), Value::I8(5), Ok(Value::I8(7))), + (Value::U8(3), Value::U8(5), Ok(Value::U8(7))), + (Value::I16(3), Value::I16(5), Ok(Value::I16(7))), + (Value::U16(3), Value::U16(5), Ok(Value::U16(7))), + (Value::I32(3), Value::I32(5), Ok(Value::I32(7))), + (Value::U32(3), Value::U32(5), Ok(Value::U32(7))), + (Value::I64(3), Value::I64(5), Ok(Value::I64(7))), + (Value::U64(3), Value::U64(5), Ok(Value::U64(7))), + (Value::F32(3.), Value::F32(5.), Err(Error::IntegralTypeRequired)), + (Value::F64(3.), Value::F64(5.), Err(Error::IntegralTypeRequired)), + (Value::Generic(3), Value::U32(5), Err(Error::TypeMismatch)), + ] { + assert_eq!(v1.or(v2, addr_mask), result); + } + } + + #[test] + #[rustfmt::skip] + fn value_xor() { + let addr_mask = 0xffff_ffff; + for &(v1, v2, result) in &[ + (Value::Generic(3), Value::Generic(5), Ok(Value::Generic(6))), + (Value::I8(3), Value::I8(5), Ok(Value::I8(6))), + (Value::U8(3), Value::U8(5), Ok(Value::U8(6))), + (Value::I16(3), Value::I16(5), Ok(Value::I16(6))), + (Value::U16(3), Value::U16(5), Ok(Value::U16(6))), + (Value::I32(3), Value::I32(5), Ok(Value::I32(6))), + (Value::U32(3), Value::U32(5), Ok(Value::U32(6))), + (Value::I64(3), Value::I64(5), Ok(Value::I64(6))), + (Value::U64(3), Value::U64(5), Ok(Value::U64(6))), + (Value::F32(3.), Value::F32(5.), Err(Error::IntegralTypeRequired)), + (Value::F64(3.), Value::F64(5.), Err(Error::IntegralTypeRequired)), + (Value::Generic(3), Value::U32(5), Err(Error::TypeMismatch)), + ] { + assert_eq!(v1.xor(v2, addr_mask), result); + } + } + + #[test] + #[rustfmt::skip] + fn value_shl() { + let addr_mask = 0xffff_ffff; + for &(v1, v2, result) in &[ + // One of each type + (Value::Generic(3), Value::Generic(5), Ok(Value::Generic(96))), + (Value::I8(3), Value::U8(5), Ok(Value::I8(96))), + (Value::U8(3), Value::I8(5), Ok(Value::U8(96))), + (Value::I16(3), Value::U16(5), Ok(Value::I16(96))), + (Value::U16(3), Value::I16(5), Ok(Value::U16(96))), + (Value::I32(3), Value::U32(5), Ok(Value::I32(96))), + (Value::U32(3), Value::I32(5), Ok(Value::U32(96))), + (Value::I64(3), Value::U64(5), Ok(Value::I64(96))), + (Value::U64(3), Value::I64(5), Ok(Value::U64(96))), + (Value::F32(3.), Value::U8(5), Err(Error::IntegralTypeRequired)), + (Value::F64(3.), Value::U8(5), Err(Error::IntegralTypeRequired)), + // Invalid shifts + (Value::U8(3), Value::I8(-5), Err(Error::InvalidShiftExpression)), + (Value::U8(3), Value::I16(-5), Err(Error::InvalidShiftExpression)), + (Value::U8(3), Value::I32(-5), Err(Error::InvalidShiftExpression)), + (Value::U8(3), Value::I64(-5), Err(Error::InvalidShiftExpression)), + (Value::U8(3), Value::F32(5.), Err(Error::InvalidShiftExpression)), + (Value::U8(3), Value::F64(5.), Err(Error::InvalidShiftExpression)), + // Large shifts + (Value::Generic(3), Value::Generic(32), Ok(Value::Generic(0))), + (Value::I8(3), Value::U8(8), Ok(Value::I8(0))), + (Value::U8(3), Value::I8(9), Ok(Value::U8(0))), + (Value::I16(3), Value::U16(17), Ok(Value::I16(0))), + (Value::U16(3), Value::I16(16), Ok(Value::U16(0))), + (Value::I32(3), Value::U32(32), Ok(Value::I32(0))), + (Value::U32(3), Value::I32(33), Ok(Value::U32(0))), + (Value::I64(3), Value::U64(65), Ok(Value::I64(0))), + (Value::U64(3), Value::I64(64), Ok(Value::U64(0))), + ] { + assert_eq!(v1.shl(v2, addr_mask), result); + } + } + + #[test] + #[rustfmt::skip] + fn value_shr() { + let addr_mask = 0xffff_ffff; + for &(v1, v2, result) in &[ + // One of each type + (Value::Generic(96), Value::Generic(5), Ok(Value::Generic(3))), + (Value::I8(96), Value::U8(5), Err(Error::UnsupportedTypeOperation)), + (Value::U8(96), Value::I8(5), Ok(Value::U8(3))), + (Value::I16(96), Value::U16(5), Err(Error::UnsupportedTypeOperation)), + (Value::U16(96), Value::I16(5), Ok(Value::U16(3))), + (Value::I32(96), Value::U32(5), Err(Error::UnsupportedTypeOperation)), + (Value::U32(96), Value::I32(5), Ok(Value::U32(3))), + (Value::I64(96), Value::U64(5), Err(Error::UnsupportedTypeOperation)), + (Value::U64(96), Value::I64(5), Ok(Value::U64(3))), + (Value::F32(96.), Value::U8(5), Err(Error::IntegralTypeRequired)), + (Value::F64(96.), Value::U8(5), Err(Error::IntegralTypeRequired)), + // Invalid shifts + (Value::U8(96), Value::I8(-5), Err(Error::InvalidShiftExpression)), + (Value::U8(96), Value::I16(-5), Err(Error::InvalidShiftExpression)), + (Value::U8(96), Value::I32(-5), Err(Error::InvalidShiftExpression)), + (Value::U8(96), Value::I64(-5), Err(Error::InvalidShiftExpression)), + (Value::U8(96), Value::F32(5.), Err(Error::InvalidShiftExpression)), + (Value::U8(96), Value::F64(5.), Err(Error::InvalidShiftExpression)), + // Large shifts + (Value::Generic(96), Value::Generic(32), Ok(Value::Generic(0))), + (Value::U8(96), Value::I8(9), Ok(Value::U8(0))), + (Value::U16(96), Value::I16(16), Ok(Value::U16(0))), + (Value::U32(96), Value::I32(33), Ok(Value::U32(0))), + (Value::U64(96), Value::I64(64), Ok(Value::U64(0))), + ] { + assert_eq!(v1.shr(v2, addr_mask), result); + } + } + + #[test] + #[rustfmt::skip] + fn value_shra() { + let addr_mask = 0xffff_ffff; + for &(v1, v2, result) in &[ + // One of each type + (Value::Generic(u64::from(-96i32 as u32)), Value::Generic(5), Ok(Value::Generic(-3i64 as u64))), + (Value::I8(-96), Value::U8(5), Ok(Value::I8(-3))), + (Value::U8(96), Value::I8(5), Err(Error::UnsupportedTypeOperation)), + (Value::I16(-96), Value::U16(5), Ok(Value::I16(-3))), + (Value::U16(96), Value::I16(5), Err(Error::UnsupportedTypeOperation)), + (Value::I32(-96), Value::U32(5), Ok(Value::I32(-3))), + (Value::U32(96), Value::I32(5), Err(Error::UnsupportedTypeOperation)), + (Value::I64(-96), Value::U64(5), Ok(Value::I64(-3))), + (Value::U64(96), Value::I64(5), Err(Error::UnsupportedTypeOperation)), + (Value::F32(96.), Value::U8(5), Err(Error::IntegralTypeRequired)), + (Value::F64(96.), Value::U8(5), Err(Error::IntegralTypeRequired)), + // Invalid shifts + (Value::U8(96), Value::I8(-5), Err(Error::InvalidShiftExpression)), + (Value::U8(96), Value::I16(-5), Err(Error::InvalidShiftExpression)), + (Value::U8(96), Value::I32(-5), Err(Error::InvalidShiftExpression)), + (Value::U8(96), Value::I64(-5), Err(Error::InvalidShiftExpression)), + (Value::U8(96), Value::F32(5.), Err(Error::InvalidShiftExpression)), + (Value::U8(96), Value::F64(5.), Err(Error::InvalidShiftExpression)), + // Large shifts + (Value::Generic(96), Value::Generic(32), Ok(Value::Generic(0))), + (Value::I8(96), Value::U8(8), Ok(Value::I8(0))), + (Value::I8(-96), Value::U8(8), Ok(Value::I8(-1))), + (Value::I16(96), Value::U16(17), Ok(Value::I16(0))), + (Value::I16(-96), Value::U16(17), Ok(Value::I16(-1))), + (Value::I32(96), Value::U32(32), Ok(Value::I32(0))), + (Value::I32(-96), Value::U32(32), Ok(Value::I32(-1))), + (Value::I64(96), Value::U64(65), Ok(Value::I64(0))), + (Value::I64(-96), Value::U64(65), Ok(Value::I64(-1))), + ] { + assert_eq!(v1.shra(v2, addr_mask), result); + } + } + + #[test] + fn value_eq() { + let addr_mask = 0xffff_ffff; + for &(v1, v2, result) in &[ + (Value::Generic(3), Value::Generic(3), Ok(Value::Generic(1))), + (Value::Generic(!3), Value::Generic(3), Ok(Value::Generic(0))), + (Value::I8(3), Value::I8(3), Ok(Value::Generic(1))), + (Value::I8(!3), Value::I8(3), Ok(Value::Generic(0))), + (Value::U8(3), Value::U8(3), Ok(Value::Generic(1))), + (Value::U8(!3), Value::U8(3), Ok(Value::Generic(0))), + (Value::I16(3), Value::I16(3), Ok(Value::Generic(1))), + (Value::I16(!3), Value::I16(3), Ok(Value::Generic(0))), + (Value::U16(3), Value::U16(3), Ok(Value::Generic(1))), + (Value::U16(!3), Value::U16(3), Ok(Value::Generic(0))), + (Value::I32(3), Value::I32(3), Ok(Value::Generic(1))), + (Value::I32(!3), Value::I32(3), Ok(Value::Generic(0))), + (Value::U32(3), Value::U32(3), Ok(Value::Generic(1))), + (Value::U32(!3), Value::U32(3), Ok(Value::Generic(0))), + (Value::I64(3), Value::I64(3), Ok(Value::Generic(1))), + (Value::I64(!3), Value::I64(3), Ok(Value::Generic(0))), + (Value::U64(3), Value::U64(3), Ok(Value::Generic(1))), + (Value::U64(!3), Value::U64(3), Ok(Value::Generic(0))), + (Value::F32(3.), Value::F32(3.), Ok(Value::Generic(1))), + (Value::F32(-3.), Value::F32(3.), Ok(Value::Generic(0))), + (Value::F64(3.), Value::F64(3.), Ok(Value::Generic(1))), + (Value::F64(-3.), Value::F64(3.), Ok(Value::Generic(0))), + (Value::Generic(3), Value::U32(3), Err(Error::TypeMismatch)), + ] { + assert_eq!(v1.eq(v2, addr_mask), result); + } + } + + #[test] + fn value_ne() { + let addr_mask = 0xffff_ffff; + for &(v1, v2, result) in &[ + (Value::Generic(3), Value::Generic(3), Ok(Value::Generic(0))), + (Value::Generic(!3), Value::Generic(3), Ok(Value::Generic(1))), + (Value::I8(3), Value::I8(3), Ok(Value::Generic(0))), + (Value::I8(!3), Value::I8(3), Ok(Value::Generic(1))), + (Value::U8(3), Value::U8(3), Ok(Value::Generic(0))), + (Value::U8(!3), Value::U8(3), Ok(Value::Generic(1))), + (Value::I16(3), Value::I16(3), Ok(Value::Generic(0))), + (Value::I16(!3), Value::I16(3), Ok(Value::Generic(1))), + (Value::U16(3), Value::U16(3), Ok(Value::Generic(0))), + (Value::U16(!3), Value::U16(3), Ok(Value::Generic(1))), + (Value::I32(3), Value::I32(3), Ok(Value::Generic(0))), + (Value::I32(!3), Value::I32(3), Ok(Value::Generic(1))), + (Value::U32(3), Value::U32(3), Ok(Value::Generic(0))), + (Value::U32(!3), Value::U32(3), Ok(Value::Generic(1))), + (Value::I64(3), Value::I64(3), Ok(Value::Generic(0))), + (Value::I64(!3), Value::I64(3), Ok(Value::Generic(1))), + (Value::U64(3), Value::U64(3), Ok(Value::Generic(0))), + (Value::U64(!3), Value::U64(3), Ok(Value::Generic(1))), + (Value::F32(3.), Value::F32(3.), Ok(Value::Generic(0))), + (Value::F32(-3.), Value::F32(3.), Ok(Value::Generic(1))), + (Value::F64(3.), Value::F64(3.), Ok(Value::Generic(0))), + (Value::F64(-3.), Value::F64(3.), Ok(Value::Generic(1))), + (Value::Generic(3), Value::U32(3), Err(Error::TypeMismatch)), + ] { + assert_eq!(v1.ne(v2, addr_mask), result); + } + } + + #[test] + fn value_ge() { + let addr_mask = 0xffff_ffff; + for &(v1, v2, result) in &[ + (Value::Generic(3), Value::Generic(!3), Ok(Value::Generic(1))), + (Value::Generic(!3), Value::Generic(3), Ok(Value::Generic(0))), + (Value::I8(3), Value::I8(!3), Ok(Value::Generic(1))), + (Value::I8(!3), Value::I8(3), Ok(Value::Generic(0))), + (Value::U8(3), Value::U8(!3), Ok(Value::Generic(0))), + (Value::U8(!3), Value::U8(3), Ok(Value::Generic(1))), + (Value::I16(3), Value::I16(!3), Ok(Value::Generic(1))), + (Value::I16(!3), Value::I16(3), Ok(Value::Generic(0))), + (Value::U16(3), Value::U16(!3), Ok(Value::Generic(0))), + (Value::U16(!3), Value::U16(3), Ok(Value::Generic(1))), + (Value::I32(3), Value::I32(!3), Ok(Value::Generic(1))), + (Value::I32(!3), Value::I32(3), Ok(Value::Generic(0))), + (Value::U32(3), Value::U32(!3), Ok(Value::Generic(0))), + (Value::U32(!3), Value::U32(3), Ok(Value::Generic(1))), + (Value::I64(3), Value::I64(!3), Ok(Value::Generic(1))), + (Value::I64(!3), Value::I64(3), Ok(Value::Generic(0))), + (Value::U64(3), Value::U64(!3), Ok(Value::Generic(0))), + (Value::U64(!3), Value::U64(3), Ok(Value::Generic(1))), + (Value::F32(3.), Value::F32(-3.), Ok(Value::Generic(1))), + (Value::F32(-3.), Value::F32(3.), Ok(Value::Generic(0))), + (Value::F64(3.), Value::F64(-3.), Ok(Value::Generic(1))), + (Value::F64(-3.), Value::F64(3.), Ok(Value::Generic(0))), + (Value::Generic(3), Value::U32(3), Err(Error::TypeMismatch)), + ] { + assert_eq!(v1.ge(v2, addr_mask), result); + } + } + + #[test] + fn value_gt() { + let addr_mask = 0xffff_ffff; + for &(v1, v2, result) in &[ + (Value::Generic(3), Value::Generic(!3), Ok(Value::Generic(1))), + (Value::Generic(!3), Value::Generic(3), Ok(Value::Generic(0))), + (Value::I8(3), Value::I8(!3), Ok(Value::Generic(1))), + (Value::I8(!3), Value::I8(3), Ok(Value::Generic(0))), + (Value::U8(3), Value::U8(!3), Ok(Value::Generic(0))), + (Value::U8(!3), Value::U8(3), Ok(Value::Generic(1))), + (Value::I16(3), Value::I16(!3), Ok(Value::Generic(1))), + (Value::I16(!3), Value::I16(3), Ok(Value::Generic(0))), + (Value::U16(3), Value::U16(!3), Ok(Value::Generic(0))), + (Value::U16(!3), Value::U16(3), Ok(Value::Generic(1))), + (Value::I32(3), Value::I32(!3), Ok(Value::Generic(1))), + (Value::I32(!3), Value::I32(3), Ok(Value::Generic(0))), + (Value::U32(3), Value::U32(!3), Ok(Value::Generic(0))), + (Value::U32(!3), Value::U32(3), Ok(Value::Generic(1))), + (Value::I64(3), Value::I64(!3), Ok(Value::Generic(1))), + (Value::I64(!3), Value::I64(3), Ok(Value::Generic(0))), + (Value::U64(3), Value::U64(!3), Ok(Value::Generic(0))), + (Value::U64(!3), Value::U64(3), Ok(Value::Generic(1))), + (Value::F32(3.), Value::F32(-3.), Ok(Value::Generic(1))), + (Value::F32(-3.), Value::F32(3.), Ok(Value::Generic(0))), + (Value::F64(3.), Value::F64(-3.), Ok(Value::Generic(1))), + (Value::F64(-3.), Value::F64(3.), Ok(Value::Generic(0))), + (Value::Generic(3), Value::U32(3), Err(Error::TypeMismatch)), + ] { + assert_eq!(v1.gt(v2, addr_mask), result); + } + } + + #[test] + fn value_le() { + let addr_mask = 0xffff_ffff; + for &(v1, v2, result) in &[ + (Value::Generic(3), Value::Generic(!3), Ok(Value::Generic(0))), + (Value::Generic(!3), Value::Generic(3), Ok(Value::Generic(1))), + (Value::I8(3), Value::I8(!3), Ok(Value::Generic(0))), + (Value::I8(!3), Value::I8(3), Ok(Value::Generic(1))), + (Value::U8(3), Value::U8(!3), Ok(Value::Generic(1))), + (Value::U8(!3), Value::U8(3), Ok(Value::Generic(0))), + (Value::I16(3), Value::I16(!3), Ok(Value::Generic(0))), + (Value::I16(!3), Value::I16(3), Ok(Value::Generic(1))), + (Value::U16(3), Value::U16(!3), Ok(Value::Generic(1))), + (Value::U16(!3), Value::U16(3), Ok(Value::Generic(0))), + (Value::I32(3), Value::I32(!3), Ok(Value::Generic(0))), + (Value::I32(!3), Value::I32(3), Ok(Value::Generic(1))), + (Value::U32(3), Value::U32(!3), Ok(Value::Generic(1))), + (Value::U32(!3), Value::U32(3), Ok(Value::Generic(0))), + (Value::I64(3), Value::I64(!3), Ok(Value::Generic(0))), + (Value::I64(!3), Value::I64(3), Ok(Value::Generic(1))), + (Value::U64(3), Value::U64(!3), Ok(Value::Generic(1))), + (Value::U64(!3), Value::U64(3), Ok(Value::Generic(0))), + (Value::F32(3.), Value::F32(-3.), Ok(Value::Generic(0))), + (Value::F32(-3.), Value::F32(3.), Ok(Value::Generic(1))), + (Value::F64(3.), Value::F64(-3.), Ok(Value::Generic(0))), + (Value::F64(-3.), Value::F64(3.), Ok(Value::Generic(1))), + (Value::Generic(3), Value::U32(3), Err(Error::TypeMismatch)), + ] { + assert_eq!(v1.le(v2, addr_mask), result); + } + } + + #[test] + fn value_lt() { + let addr_mask = 0xffff_ffff; + for &(v1, v2, result) in &[ + (Value::Generic(3), Value::Generic(!3), Ok(Value::Generic(0))), + (Value::Generic(!3), Value::Generic(3), Ok(Value::Generic(1))), + (Value::I8(3), Value::I8(!3), Ok(Value::Generic(0))), + (Value::I8(!3), Value::I8(3), Ok(Value::Generic(1))), + (Value::U8(3), Value::U8(!3), Ok(Value::Generic(1))), + (Value::U8(!3), Value::U8(3), Ok(Value::Generic(0))), + (Value::I16(3), Value::I16(!3), Ok(Value::Generic(0))), + (Value::I16(!3), Value::I16(3), Ok(Value::Generic(1))), + (Value::U16(3), Value::U16(!3), Ok(Value::Generic(1))), + (Value::U16(!3), Value::U16(3), Ok(Value::Generic(0))), + (Value::I32(3), Value::I32(!3), Ok(Value::Generic(0))), + (Value::I32(!3), Value::I32(3), Ok(Value::Generic(1))), + (Value::U32(3), Value::U32(!3), Ok(Value::Generic(1))), + (Value::U32(!3), Value::U32(3), Ok(Value::Generic(0))), + (Value::I64(3), Value::I64(!3), Ok(Value::Generic(0))), + (Value::I64(!3), Value::I64(3), Ok(Value::Generic(1))), + (Value::U64(3), Value::U64(!3), Ok(Value::Generic(1))), + (Value::U64(!3), Value::U64(3), Ok(Value::Generic(0))), + (Value::F32(3.), Value::F32(-3.), Ok(Value::Generic(0))), + (Value::F32(-3.), Value::F32(3.), Ok(Value::Generic(1))), + (Value::F64(3.), Value::F64(-3.), Ok(Value::Generic(0))), + (Value::F64(-3.), Value::F64(3.), Ok(Value::Generic(1))), + (Value::Generic(3), Value::U32(3), Err(Error::TypeMismatch)), + ] { + assert_eq!(v1.lt(v2, addr_mask), result); + } + } +} diff --git a/vendor/gimli/src/test_util.rs b/vendor/gimli/src/test_util.rs new file mode 100644 index 0000000..706aaf9 --- /dev/null +++ b/vendor/gimli/src/test_util.rs @@ -0,0 +1,53 @@ +#![allow(missing_docs)] + +use crate::Format; +use test_assembler::{Label, Section}; + +pub trait GimliSectionMethods { + fn sleb(self, val: i64) -> Self; + fn uleb(self, val: u64) -> Self; + fn initial_length(self, format: Format, length: &Label, start: &Label) -> Self; + fn word(self, size: u8, val: u64) -> Self; + fn word_label(self, size: u8, val: &Label) -> Self; +} + +impl GimliSectionMethods for Section { + fn sleb(mut self, mut val: i64) -> Self { + while val & !0x3f != 0 && val | 0x3f != -1 { + self = self.D8(val as u8 | 0x80); + val >>= 7; + } + self.D8(val as u8 & 0x7f) + } + + fn uleb(mut self, mut val: u64) -> Self { + while val & !0x7f != 0 { + self = self.D8(val as u8 | 0x80); + val >>= 7; + } + self.D8(val as u8) + } + + fn initial_length(self, format: Format, length: &Label, start: &Label) -> Self { + match format { + Format::Dwarf32 => self.D32(length).mark(start), + Format::Dwarf64 => self.D32(0xffff_ffff).D64(length).mark(start), + } + } + + fn word(self, size: u8, val: u64) -> Self { + match size { + 4 => self.D32(val as u32), + 8 => self.D64(val), + _ => panic!("unsupported word size"), + } + } + + fn word_label(self, size: u8, val: &Label) -> Self { + match size { + 4 => self.D32(val), + 8 => self.D64(val), + _ => panic!("unsupported word size"), + } + } +} diff --git a/vendor/gimli/src/write/abbrev.rs b/vendor/gimli/src/write/abbrev.rs new file mode 100644 index 0000000..7cdfa96 --- /dev/null +++ b/vendor/gimli/src/write/abbrev.rs @@ -0,0 +1,188 @@ +use alloc::vec::Vec; +use indexmap::IndexSet; +use std::ops::{Deref, DerefMut}; + +use crate::common::{DebugAbbrevOffset, SectionId}; +use crate::constants; +use crate::write::{Result, Section, Writer}; + +/// A table of abbreviations that will be stored in a `.debug_abbrev` section. +// Requirements: +// - values are `Abbreviation` +// - insertion returns an abbreviation code for use in writing a DIE +// - inserting a duplicate returns the code of the existing value +#[derive(Debug, Default)] +pub(crate) struct AbbreviationTable { + abbrevs: IndexSet<Abbreviation>, +} + +impl AbbreviationTable { + /// Add an abbreviation to the table and return its code. + pub fn add(&mut self, abbrev: Abbreviation) -> u64 { + let (code, _) = self.abbrevs.insert_full(abbrev); + // Code must be non-zero + (code + 1) as u64 + } + + /// Write the abbreviation table to the `.debug_abbrev` section. + pub fn write<W: Writer>(&self, w: &mut DebugAbbrev<W>) -> Result<()> { + for (code, abbrev) in self.abbrevs.iter().enumerate() { + w.write_uleb128((code + 1) as u64)?; + abbrev.write(w)?; + } + // Null abbreviation code + w.write_u8(0) + } +} + +/// An abbreviation describes the shape of a `DebuggingInformationEntry`'s type: +/// its tag type, whether it has children, and its set of attributes. +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub(crate) struct Abbreviation { + tag: constants::DwTag, + has_children: bool, + attributes: Vec<AttributeSpecification>, +} + +impl Abbreviation { + /// Construct a new `Abbreviation`. + #[inline] + pub fn new( + tag: constants::DwTag, + has_children: bool, + attributes: Vec<AttributeSpecification>, + ) -> Abbreviation { + Abbreviation { + tag, + has_children, + attributes, + } + } + + /// Write the abbreviation to the `.debug_abbrev` section. + pub fn write<W: Writer>(&self, w: &mut DebugAbbrev<W>) -> Result<()> { + w.write_uleb128(self.tag.0.into())?; + w.write_u8(if self.has_children { + constants::DW_CHILDREN_yes.0 + } else { + constants::DW_CHILDREN_no.0 + })?; + for attr in &self.attributes { + attr.write(w)?; + } + // Null name and form + w.write_u8(0)?; + w.write_u8(0) + } +} + +/// The description of an attribute in an abbreviated type. +// TODO: support implicit const +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub(crate) struct AttributeSpecification { + name: constants::DwAt, + form: constants::DwForm, +} + +impl AttributeSpecification { + /// Construct a new `AttributeSpecification`. + #[inline] + pub fn new(name: constants::DwAt, form: constants::DwForm) -> AttributeSpecification { + AttributeSpecification { name, form } + } + + /// Write the attribute specification to the `.debug_abbrev` section. + #[inline] + pub fn write<W: Writer>(&self, w: &mut DebugAbbrev<W>) -> Result<()> { + w.write_uleb128(self.name.0.into())?; + w.write_uleb128(self.form.0.into()) + } +} + +define_section!( + DebugAbbrev, + DebugAbbrevOffset, + "A writable `.debug_abbrev` section." +); + +#[cfg(test)] +#[cfg(feature = "read")] +mod tests { + use super::*; + use crate::constants; + use crate::read; + use crate::write::EndianVec; + use crate::LittleEndian; + + #[test] + fn test_abbreviation_table() { + let mut abbrevs = AbbreviationTable::default(); + let abbrev1 = Abbreviation::new( + constants::DW_TAG_subprogram, + false, + vec![AttributeSpecification::new( + constants::DW_AT_name, + constants::DW_FORM_string, + )], + ); + let abbrev2 = Abbreviation::new( + constants::DW_TAG_compile_unit, + true, + vec![ + AttributeSpecification::new(constants::DW_AT_producer, constants::DW_FORM_strp), + AttributeSpecification::new(constants::DW_AT_language, constants::DW_FORM_data2), + ], + ); + let code1 = abbrevs.add(abbrev1.clone()); + assert_eq!(code1, 1); + let code2 = abbrevs.add(abbrev2.clone()); + assert_eq!(code2, 2); + assert_eq!(abbrevs.add(abbrev1.clone()), code1); + assert_eq!(abbrevs.add(abbrev2.clone()), code2); + + let mut debug_abbrev = DebugAbbrev::from(EndianVec::new(LittleEndian)); + let debug_abbrev_offset = debug_abbrev.offset(); + assert_eq!(debug_abbrev_offset, DebugAbbrevOffset(0)); + abbrevs.write(&mut debug_abbrev).unwrap(); + assert_eq!(debug_abbrev.offset(), DebugAbbrevOffset(17)); + + let read_debug_abbrev = read::DebugAbbrev::new(debug_abbrev.slice(), LittleEndian); + let read_abbrevs = read_debug_abbrev + .abbreviations(debug_abbrev_offset) + .unwrap(); + + let read_abbrev1 = read_abbrevs.get(code1).unwrap(); + assert_eq!(abbrev1.tag, read_abbrev1.tag()); + assert_eq!(abbrev1.has_children, read_abbrev1.has_children()); + assert_eq!(abbrev1.attributes.len(), read_abbrev1.attributes().len()); + assert_eq!( + abbrev1.attributes[0].name, + read_abbrev1.attributes()[0].name() + ); + assert_eq!( + abbrev1.attributes[0].form, + read_abbrev1.attributes()[0].form() + ); + + let read_abbrev2 = read_abbrevs.get(code2).unwrap(); + assert_eq!(abbrev2.tag, read_abbrev2.tag()); + assert_eq!(abbrev2.has_children, read_abbrev2.has_children()); + assert_eq!(abbrev2.attributes.len(), read_abbrev2.attributes().len()); + assert_eq!( + abbrev2.attributes[0].name, + read_abbrev2.attributes()[0].name() + ); + assert_eq!( + abbrev2.attributes[0].form, + read_abbrev2.attributes()[0].form() + ); + assert_eq!( + abbrev2.attributes[1].name, + read_abbrev2.attributes()[1].name() + ); + assert_eq!( + abbrev2.attributes[1].form, + read_abbrev2.attributes()[1].form() + ); + } +} diff --git a/vendor/gimli/src/write/cfi.rs b/vendor/gimli/src/write/cfi.rs new file mode 100644 index 0000000..5e108f1 --- /dev/null +++ b/vendor/gimli/src/write/cfi.rs @@ -0,0 +1,1050 @@ +use alloc::vec::Vec; +use indexmap::IndexSet; +use std::ops::{Deref, DerefMut}; + +use crate::common::{DebugFrameOffset, EhFrameOffset, Encoding, Format, Register, SectionId}; +use crate::constants; +use crate::write::{Address, BaseId, Error, Expression, Result, Section, Writer}; + +define_section!( + DebugFrame, + DebugFrameOffset, + "A writable `.debug_frame` section." +); + +define_section!(EhFrame, EhFrameOffset, "A writable `.eh_frame` section."); + +define_id!(CieId, "An identifier for a CIE in a `FrameTable`."); + +/// A table of frame description entries. +#[derive(Debug, Default)] +pub struct FrameTable { + /// Base id for CIEs. + base_id: BaseId, + /// The common information entries. + cies: IndexSet<CommonInformationEntry>, + /// The frame description entries. + fdes: Vec<(CieId, FrameDescriptionEntry)>, +} + +impl FrameTable { + /// Add a CIE and return its id. + /// + /// If the CIE already exists, then return the id of the existing CIE. + pub fn add_cie(&mut self, cie: CommonInformationEntry) -> CieId { + let (index, _) = self.cies.insert_full(cie); + CieId::new(self.base_id, index) + } + + /// The number of CIEs. + pub fn cie_count(&self) -> usize { + self.cies.len() + } + + /// Add a FDE. + /// + /// Does not check for duplicates. + /// + /// # Panics + /// + /// Panics if the CIE id is invalid. + pub fn add_fde(&mut self, cie: CieId, fde: FrameDescriptionEntry) { + debug_assert_eq!(self.base_id, cie.base_id); + self.fdes.push((cie, fde)); + } + + /// The number of FDEs. + pub fn fde_count(&self) -> usize { + self.fdes.len() + } + + /// Write the frame table entries to the given `.debug_frame` section. + pub fn write_debug_frame<W: Writer>(&self, w: &mut DebugFrame<W>) -> Result<()> { + self.write(&mut w.0, false) + } + + /// Write the frame table entries to the given `.eh_frame` section. + pub fn write_eh_frame<W: Writer>(&self, w: &mut EhFrame<W>) -> Result<()> { + self.write(&mut w.0, true) + } + + fn write<W: Writer>(&self, w: &mut W, eh_frame: bool) -> Result<()> { + let mut cie_offsets = vec![None; self.cies.len()]; + for (cie_id, fde) in &self.fdes { + let cie_index = cie_id.index; + let cie = self.cies.get_index(cie_index).unwrap(); + let cie_offset = match cie_offsets[cie_index] { + Some(offset) => offset, + None => { + // Only write CIEs as they are referenced. + let offset = cie.write(w, eh_frame)?; + cie_offsets[cie_index] = Some(offset); + offset + } + }; + + fde.write(w, eh_frame, cie_offset, cie)?; + } + // TODO: write length 0 terminator for eh_frame? + Ok(()) + } +} + +/// A common information entry. This contains information that is shared between FDEs. +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct CommonInformationEntry { + encoding: Encoding, + + /// A constant that is factored out of code offsets. + /// + /// This should be set to the minimum instruction length. + /// Writing a code offset that is not a multiple of this factor will generate an error. + code_alignment_factor: u8, + + /// A constant that is factored out of data offsets. + /// + /// This should be set to the minimum data alignment for the frame. + /// Writing a data offset that is not a multiple of this factor will generate an error. + data_alignment_factor: i8, + + /// The return address register. This might not correspond to an actual machine register. + return_address_register: Register, + + /// The address of the personality function and its encoding. + pub personality: Option<(constants::DwEhPe, Address)>, + + /// The encoding to use for the LSDA address in FDEs. + /// + /// If set then all FDEs which use this CIE must have a LSDA address. + pub lsda_encoding: Option<constants::DwEhPe>, + + /// The encoding to use for addresses in FDEs. + pub fde_address_encoding: constants::DwEhPe, + + /// True for signal trampolines. + pub signal_trampoline: bool, + + /// The initial instructions upon entry to this function. + instructions: Vec<CallFrameInstruction>, +} + +impl CommonInformationEntry { + /// Create a new common information entry. + /// + /// The encoding version must be a CFI version, not a DWARF version. + pub fn new( + encoding: Encoding, + code_alignment_factor: u8, + data_alignment_factor: i8, + return_address_register: Register, + ) -> Self { + CommonInformationEntry { + encoding, + code_alignment_factor, + data_alignment_factor, + return_address_register, + personality: None, + lsda_encoding: None, + fde_address_encoding: constants::DW_EH_PE_absptr, + signal_trampoline: false, + instructions: Vec::new(), + } + } + + /// Add an initial instruction. + pub fn add_instruction(&mut self, instruction: CallFrameInstruction) { + self.instructions.push(instruction); + } + + fn has_augmentation(&self) -> bool { + self.personality.is_some() + || self.lsda_encoding.is_some() + || self.signal_trampoline + || self.fde_address_encoding != constants::DW_EH_PE_absptr + } + + /// Returns the section offset of the CIE. + fn write<W: Writer>(&self, w: &mut W, eh_frame: bool) -> Result<usize> { + let encoding = self.encoding; + let offset = w.len(); + + let length_offset = w.write_initial_length(encoding.format)?; + let length_base = w.len(); + + if eh_frame { + w.write_u32(0)?; + } else { + match encoding.format { + Format::Dwarf32 => w.write_u32(0xffff_ffff)?, + Format::Dwarf64 => w.write_u64(0xffff_ffff_ffff_ffff)?, + } + } + + if eh_frame { + if encoding.version != 1 { + return Err(Error::UnsupportedVersion(encoding.version)); + }; + } else { + match encoding.version { + 1 | 3 | 4 => {} + _ => return Err(Error::UnsupportedVersion(encoding.version)), + }; + } + w.write_u8(encoding.version as u8)?; + + let augmentation = self.has_augmentation(); + if augmentation { + w.write_u8(b'z')?; + if self.lsda_encoding.is_some() { + w.write_u8(b'L')?; + } + if self.personality.is_some() { + w.write_u8(b'P')?; + } + if self.fde_address_encoding != constants::DW_EH_PE_absptr { + w.write_u8(b'R')?; + } + if self.signal_trampoline { + w.write_u8(b'S')?; + } + } + w.write_u8(0)?; + + if encoding.version >= 4 { + w.write_u8(encoding.address_size)?; + // TODO: segment_selector_size + w.write_u8(0)?; + } + + w.write_uleb128(self.code_alignment_factor.into())?; + w.write_sleb128(self.data_alignment_factor.into())?; + + if !eh_frame && encoding.version == 1 { + let register = self.return_address_register.0 as u8; + if u16::from(register) != self.return_address_register.0 { + return Err(Error::ValueTooLarge); + } + w.write_u8(register)?; + } else { + w.write_uleb128(self.return_address_register.0.into())?; + } + + if augmentation { + let augmentation_length_offset = w.len(); + w.write_u8(0)?; + let augmentation_length_base = w.len(); + + if let Some(eh_pe) = self.lsda_encoding { + w.write_u8(eh_pe.0)?; + } + if let Some((eh_pe, address)) = self.personality { + w.write_u8(eh_pe.0)?; + w.write_eh_pointer(address, eh_pe, encoding.address_size)?; + } + if self.fde_address_encoding != constants::DW_EH_PE_absptr { + w.write_u8(self.fde_address_encoding.0)?; + } + + let augmentation_length = (w.len() - augmentation_length_base) as u64; + debug_assert!(augmentation_length < 0x80); + w.write_udata_at(augmentation_length_offset, augmentation_length, 1)?; + } + + for instruction in &self.instructions { + instruction.write(w, encoding, self)?; + } + + write_nop( + w, + encoding.format.word_size() as usize + w.len() - length_base, + encoding.address_size, + )?; + + let length = (w.len() - length_base) as u64; + w.write_initial_length_at(length_offset, length, encoding.format)?; + + Ok(offset) + } +} + +/// A frame description entry. There should be one FDE per function. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct FrameDescriptionEntry { + /// The initial address of the function. + address: Address, + + /// The length in bytes of the function. + length: u32, + + /// The address of the LSDA. + pub lsda: Option<Address>, + + /// The instructions for this function, ordered by offset. + instructions: Vec<(u32, CallFrameInstruction)>, +} + +impl FrameDescriptionEntry { + /// Create a new frame description entry for a function. + pub fn new(address: Address, length: u32) -> Self { + FrameDescriptionEntry { + address, + length, + lsda: None, + instructions: Vec::new(), + } + } + + /// Add an instruction. + /// + /// Instructions must be added in increasing order of offset, or writing will fail. + pub fn add_instruction(&mut self, offset: u32, instruction: CallFrameInstruction) { + debug_assert!(self.instructions.last().map(|x| x.0).unwrap_or(0) <= offset); + self.instructions.push((offset, instruction)); + } + + fn write<W: Writer>( + &self, + w: &mut W, + eh_frame: bool, + cie_offset: usize, + cie: &CommonInformationEntry, + ) -> Result<()> { + let encoding = cie.encoding; + let length_offset = w.write_initial_length(encoding.format)?; + let length_base = w.len(); + + if eh_frame { + // .eh_frame uses a relative offset which doesn't need relocation. + w.write_udata((w.len() - cie_offset) as u64, 4)?; + } else { + w.write_offset( + cie_offset, + SectionId::DebugFrame, + encoding.format.word_size(), + )?; + } + + if cie.fde_address_encoding != constants::DW_EH_PE_absptr { + w.write_eh_pointer( + self.address, + cie.fde_address_encoding, + encoding.address_size, + )?; + w.write_eh_pointer_data( + self.length.into(), + cie.fde_address_encoding.format(), + encoding.address_size, + )?; + } else { + w.write_address(self.address, encoding.address_size)?; + w.write_udata(self.length.into(), encoding.address_size)?; + } + + if cie.has_augmentation() { + let mut augmentation_length = 0u64; + if self.lsda.is_some() { + augmentation_length += u64::from(encoding.address_size); + } + w.write_uleb128(augmentation_length)?; + + debug_assert_eq!(self.lsda.is_some(), cie.lsda_encoding.is_some()); + if let (Some(lsda), Some(lsda_encoding)) = (self.lsda, cie.lsda_encoding) { + w.write_eh_pointer(lsda, lsda_encoding, encoding.address_size)?; + } + } + + let mut prev_offset = 0; + for (offset, instruction) in &self.instructions { + write_advance_loc(w, cie.code_alignment_factor, prev_offset, *offset)?; + prev_offset = *offset; + instruction.write(w, encoding, cie)?; + } + + write_nop( + w, + encoding.format.word_size() as usize + w.len() - length_base, + encoding.address_size, + )?; + + let length = (w.len() - length_base) as u64; + w.write_initial_length_at(length_offset, length, encoding.format)?; + + Ok(()) + } +} + +/// An instruction in a frame description entry. +/// +/// This may be a CFA definition, a register rule, or some other directive. +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[non_exhaustive] +pub enum CallFrameInstruction { + /// Define the CFA rule to use the provided register and offset. + Cfa(Register, i32), + /// Update the CFA rule to use the provided register. The offset is unchanged. + CfaRegister(Register), + /// Update the CFA rule to use the provided offset. The register is unchanged. + CfaOffset(i32), + /// Define the CFA rule to use the provided expression. + CfaExpression(Expression), + + /// Restore the initial rule for the register. + Restore(Register), + /// The previous value of the register is not recoverable. + Undefined(Register), + /// The register has not been modified. + SameValue(Register), + /// The previous value of the register is saved at address CFA + offset. + Offset(Register, i32), + /// The previous value of the register is CFA + offset. + ValOffset(Register, i32), + /// The previous value of the register is stored in another register. + Register(Register, Register), + /// The previous value of the register is saved at address given by the expression. + Expression(Register, Expression), + /// The previous value of the register is given by the expression. + ValExpression(Register, Expression), + + /// Push all register rules onto a stack. + RememberState, + /// Pop all register rules off the stack. + RestoreState, + /// The size of the arguments that have been pushed onto the stack. + ArgsSize(u32), + + /// AAarch64 extension: negate the `RA_SIGN_STATE` pseudo-register. + NegateRaState, +} + +impl CallFrameInstruction { + fn write<W: Writer>( + &self, + w: &mut W, + encoding: Encoding, + cie: &CommonInformationEntry, + ) -> Result<()> { + match *self { + CallFrameInstruction::Cfa(register, offset) => { + if offset < 0 { + let offset = factored_data_offset(offset, cie.data_alignment_factor)?; + w.write_u8(constants::DW_CFA_def_cfa_sf.0)?; + w.write_uleb128(register.0.into())?; + w.write_sleb128(offset.into())?; + } else { + // Unfactored offset. + w.write_u8(constants::DW_CFA_def_cfa.0)?; + w.write_uleb128(register.0.into())?; + w.write_uleb128(offset as u64)?; + } + } + CallFrameInstruction::CfaRegister(register) => { + w.write_u8(constants::DW_CFA_def_cfa_register.0)?; + w.write_uleb128(register.0.into())?; + } + CallFrameInstruction::CfaOffset(offset) => { + if offset < 0 { + let offset = factored_data_offset(offset, cie.data_alignment_factor)?; + w.write_u8(constants::DW_CFA_def_cfa_offset_sf.0)?; + w.write_sleb128(offset.into())?; + } else { + // Unfactored offset. + w.write_u8(constants::DW_CFA_def_cfa_offset.0)?; + w.write_uleb128(offset as u64)?; + } + } + CallFrameInstruction::CfaExpression(ref expression) => { + w.write_u8(constants::DW_CFA_def_cfa_expression.0)?; + w.write_uleb128(expression.size(encoding, None) as u64)?; + expression.write(w, None, encoding, None)?; + } + CallFrameInstruction::Restore(register) => { + if register.0 < 0x40 { + w.write_u8(constants::DW_CFA_restore.0 | register.0 as u8)?; + } else { + w.write_u8(constants::DW_CFA_restore_extended.0)?; + w.write_uleb128(register.0.into())?; + } + } + CallFrameInstruction::Undefined(register) => { + w.write_u8(constants::DW_CFA_undefined.0)?; + w.write_uleb128(register.0.into())?; + } + CallFrameInstruction::SameValue(register) => { + w.write_u8(constants::DW_CFA_same_value.0)?; + w.write_uleb128(register.0.into())?; + } + CallFrameInstruction::Offset(register, offset) => { + let offset = factored_data_offset(offset, cie.data_alignment_factor)?; + if offset < 0 { + w.write_u8(constants::DW_CFA_offset_extended_sf.0)?; + w.write_uleb128(register.0.into())?; + w.write_sleb128(offset.into())?; + } else if register.0 < 0x40 { + w.write_u8(constants::DW_CFA_offset.0 | register.0 as u8)?; + w.write_uleb128(offset as u64)?; + } else { + w.write_u8(constants::DW_CFA_offset_extended.0)?; + w.write_uleb128(register.0.into())?; + w.write_uleb128(offset as u64)?; + } + } + CallFrameInstruction::ValOffset(register, offset) => { + let offset = factored_data_offset(offset, cie.data_alignment_factor)?; + if offset < 0 { + w.write_u8(constants::DW_CFA_val_offset_sf.0)?; + w.write_uleb128(register.0.into())?; + w.write_sleb128(offset.into())?; + } else { + w.write_u8(constants::DW_CFA_val_offset.0)?; + w.write_uleb128(register.0.into())?; + w.write_uleb128(offset as u64)?; + } + } + CallFrameInstruction::Register(register1, register2) => { + w.write_u8(constants::DW_CFA_register.0)?; + w.write_uleb128(register1.0.into())?; + w.write_uleb128(register2.0.into())?; + } + CallFrameInstruction::Expression(register, ref expression) => { + w.write_u8(constants::DW_CFA_expression.0)?; + w.write_uleb128(register.0.into())?; + w.write_uleb128(expression.size(encoding, None) as u64)?; + expression.write(w, None, encoding, None)?; + } + CallFrameInstruction::ValExpression(register, ref expression) => { + w.write_u8(constants::DW_CFA_val_expression.0)?; + w.write_uleb128(register.0.into())?; + w.write_uleb128(expression.size(encoding, None) as u64)?; + expression.write(w, None, encoding, None)?; + } + CallFrameInstruction::RememberState => { + w.write_u8(constants::DW_CFA_remember_state.0)?; + } + CallFrameInstruction::RestoreState => { + w.write_u8(constants::DW_CFA_restore_state.0)?; + } + CallFrameInstruction::ArgsSize(size) => { + w.write_u8(constants::DW_CFA_GNU_args_size.0)?; + w.write_uleb128(size.into())?; + } + CallFrameInstruction::NegateRaState => { + w.write_u8(constants::DW_CFA_AARCH64_negate_ra_state.0)?; + } + } + Ok(()) + } +} + +fn write_advance_loc<W: Writer>( + w: &mut W, + code_alignment_factor: u8, + prev_offset: u32, + offset: u32, +) -> Result<()> { + if offset == prev_offset { + return Ok(()); + } + let delta = factored_code_delta(prev_offset, offset, code_alignment_factor)?; + if delta < 0x40 { + w.write_u8(constants::DW_CFA_advance_loc.0 | delta as u8)?; + } else if delta < 0x100 { + w.write_u8(constants::DW_CFA_advance_loc1.0)?; + w.write_u8(delta as u8)?; + } else if delta < 0x10000 { + w.write_u8(constants::DW_CFA_advance_loc2.0)?; + w.write_u16(delta as u16)?; + } else { + w.write_u8(constants::DW_CFA_advance_loc4.0)?; + w.write_u32(delta)?; + } + Ok(()) +} + +fn write_nop<W: Writer>(w: &mut W, len: usize, align: u8) -> Result<()> { + debug_assert_eq!(align & (align - 1), 0); + let tail_len = (!len + 1) & (align as usize - 1); + for _ in 0..tail_len { + w.write_u8(constants::DW_CFA_nop.0)?; + } + Ok(()) +} + +fn factored_code_delta(prev_offset: u32, offset: u32, factor: u8) -> Result<u32> { + if offset < prev_offset { + return Err(Error::InvalidFrameCodeOffset(offset)); + } + let delta = offset - prev_offset; + let factor = u32::from(factor); + let factored_delta = delta / factor; + if delta != factored_delta * factor { + return Err(Error::InvalidFrameCodeOffset(offset)); + } + Ok(factored_delta) +} + +fn factored_data_offset(offset: i32, factor: i8) -> Result<i32> { + let factor = i32::from(factor); + let factored_offset = offset / factor; + if offset != factored_offset * factor { + return Err(Error::InvalidFrameDataOffset(offset)); + } + Ok(factored_offset) +} + +#[cfg(feature = "read")] +pub(crate) mod convert { + use super::*; + use crate::read::{self, Reader}; + use crate::write::{ConvertError, ConvertResult}; + use std::collections::{hash_map, HashMap}; + + impl FrameTable { + /// Create a frame table by reading the data in the given section. + /// + /// `convert_address` is a function to convert read addresses into the `Address` + /// type. For non-relocatable addresses, this function may simply return + /// `Address::Constant(address)`. For relocatable addresses, it is the caller's + /// responsibility to determine the symbol and addend corresponding to the address + /// and return `Address::Symbol { symbol, addend }`. + pub fn from<R, Section>( + frame: &Section, + convert_address: &dyn Fn(u64) -> Option<Address>, + ) -> ConvertResult<FrameTable> + where + R: Reader<Offset = usize>, + Section: read::UnwindSection<R>, + Section::Offset: read::UnwindOffset<usize>, + { + let bases = read::BaseAddresses::default().set_eh_frame(0); + + let mut frame_table = FrameTable::default(); + + let mut cie_ids = HashMap::new(); + let mut entries = frame.entries(&bases); + while let Some(entry) = entries.next()? { + let partial = match entry { + read::CieOrFde::Cie(_) => continue, + read::CieOrFde::Fde(partial) => partial, + }; + + // TODO: is it worth caching the parsed CIEs? It would be better if FDEs only + // stored a reference. + let from_fde = partial.parse(Section::cie_from_offset)?; + let from_cie = from_fde.cie(); + let cie_id = match cie_ids.entry(from_cie.offset()) { + hash_map::Entry::Occupied(o) => *o.get(), + hash_map::Entry::Vacant(e) => { + let cie = + CommonInformationEntry::from(from_cie, frame, &bases, convert_address)?; + let cie_id = frame_table.add_cie(cie); + e.insert(cie_id); + cie_id + } + }; + let fde = FrameDescriptionEntry::from(&from_fde, frame, &bases, convert_address)?; + frame_table.add_fde(cie_id, fde); + } + + Ok(frame_table) + } + } + + impl CommonInformationEntry { + fn from<R, Section>( + from_cie: &read::CommonInformationEntry<R>, + frame: &Section, + bases: &read::BaseAddresses, + convert_address: &dyn Fn(u64) -> Option<Address>, + ) -> ConvertResult<CommonInformationEntry> + where + R: Reader<Offset = usize>, + Section: read::UnwindSection<R>, + Section::Offset: read::UnwindOffset<usize>, + { + let mut cie = CommonInformationEntry::new( + from_cie.encoding(), + from_cie.code_alignment_factor() as u8, + from_cie.data_alignment_factor() as i8, + from_cie.return_address_register(), + ); + + cie.personality = match from_cie.personality_with_encoding() { + // We treat these the same because the encoding already determines + // whether it is indirect. + Some((eh_pe, read::Pointer::Direct(p))) + | Some((eh_pe, read::Pointer::Indirect(p))) => { + let address = convert_address(p).ok_or(ConvertError::InvalidAddress)?; + Some((eh_pe, address)) + } + _ => None, + }; + cie.lsda_encoding = from_cie.lsda_encoding(); + cie.fde_address_encoding = from_cie + .fde_address_encoding() + .unwrap_or(constants::DW_EH_PE_absptr); + cie.signal_trampoline = from_cie.is_signal_trampoline(); + + let mut offset = 0; + let mut from_instructions = from_cie.instructions(frame, bases); + while let Some(from_instruction) = from_instructions.next()? { + if let Some(instruction) = CallFrameInstruction::from( + from_instruction, + from_cie, + convert_address, + &mut offset, + )? { + cie.instructions.push(instruction); + } + } + Ok(cie) + } + } + + impl FrameDescriptionEntry { + fn from<R, Section>( + from_fde: &read::FrameDescriptionEntry<R>, + frame: &Section, + bases: &read::BaseAddresses, + convert_address: &dyn Fn(u64) -> Option<Address>, + ) -> ConvertResult<FrameDescriptionEntry> + where + R: Reader<Offset = usize>, + Section: read::UnwindSection<R>, + Section::Offset: read::UnwindOffset<usize>, + { + let address = + convert_address(from_fde.initial_address()).ok_or(ConvertError::InvalidAddress)?; + let length = from_fde.len() as u32; + let mut fde = FrameDescriptionEntry::new(address, length); + + match from_fde.lsda() { + // We treat these the same because the encoding already determines + // whether it is indirect. + Some(read::Pointer::Direct(p)) | Some(read::Pointer::Indirect(p)) => { + let address = convert_address(p).ok_or(ConvertError::InvalidAddress)?; + fde.lsda = Some(address); + } + None => {} + } + + let from_cie = from_fde.cie(); + let mut offset = 0; + let mut from_instructions = from_fde.instructions(frame, bases); + while let Some(from_instruction) = from_instructions.next()? { + if let Some(instruction) = CallFrameInstruction::from( + from_instruction, + from_cie, + convert_address, + &mut offset, + )? { + fde.instructions.push((offset, instruction)); + } + } + + Ok(fde) + } + } + + impl CallFrameInstruction { + fn from<R: Reader<Offset = usize>>( + from_instruction: read::CallFrameInstruction<R>, + from_cie: &read::CommonInformationEntry<R>, + convert_address: &dyn Fn(u64) -> Option<Address>, + offset: &mut u32, + ) -> ConvertResult<Option<CallFrameInstruction>> { + let convert_expression = + |x| Expression::from(x, from_cie.encoding(), None, None, None, convert_address); + // TODO: validate integer type conversions + Ok(Some(match from_instruction { + read::CallFrameInstruction::SetLoc { .. } => { + return Err(ConvertError::UnsupportedCfiInstruction); + } + read::CallFrameInstruction::AdvanceLoc { delta } => { + *offset += delta * from_cie.code_alignment_factor() as u32; + return Ok(None); + } + read::CallFrameInstruction::DefCfa { register, offset } => { + CallFrameInstruction::Cfa(register, offset as i32) + } + read::CallFrameInstruction::DefCfaSf { + register, + factored_offset, + } => { + let offset = factored_offset * from_cie.data_alignment_factor(); + CallFrameInstruction::Cfa(register, offset as i32) + } + read::CallFrameInstruction::DefCfaRegister { register } => { + CallFrameInstruction::CfaRegister(register) + } + + read::CallFrameInstruction::DefCfaOffset { offset } => { + CallFrameInstruction::CfaOffset(offset as i32) + } + read::CallFrameInstruction::DefCfaOffsetSf { factored_offset } => { + let offset = factored_offset * from_cie.data_alignment_factor(); + CallFrameInstruction::CfaOffset(offset as i32) + } + read::CallFrameInstruction::DefCfaExpression { expression } => { + CallFrameInstruction::CfaExpression(convert_expression(expression)?) + } + read::CallFrameInstruction::Undefined { register } => { + CallFrameInstruction::Undefined(register) + } + read::CallFrameInstruction::SameValue { register } => { + CallFrameInstruction::SameValue(register) + } + read::CallFrameInstruction::Offset { + register, + factored_offset, + } => { + let offset = factored_offset as i64 * from_cie.data_alignment_factor(); + CallFrameInstruction::Offset(register, offset as i32) + } + read::CallFrameInstruction::OffsetExtendedSf { + register, + factored_offset, + } => { + let offset = factored_offset * from_cie.data_alignment_factor(); + CallFrameInstruction::Offset(register, offset as i32) + } + read::CallFrameInstruction::ValOffset { + register, + factored_offset, + } => { + let offset = factored_offset as i64 * from_cie.data_alignment_factor(); + CallFrameInstruction::ValOffset(register, offset as i32) + } + read::CallFrameInstruction::ValOffsetSf { + register, + factored_offset, + } => { + let offset = factored_offset * from_cie.data_alignment_factor(); + CallFrameInstruction::ValOffset(register, offset as i32) + } + read::CallFrameInstruction::Register { + dest_register, + src_register, + } => CallFrameInstruction::Register(dest_register, src_register), + read::CallFrameInstruction::Expression { + register, + expression, + } => CallFrameInstruction::Expression(register, convert_expression(expression)?), + read::CallFrameInstruction::ValExpression { + register, + expression, + } => CallFrameInstruction::ValExpression(register, convert_expression(expression)?), + read::CallFrameInstruction::Restore { register } => { + CallFrameInstruction::Restore(register) + } + read::CallFrameInstruction::RememberState => CallFrameInstruction::RememberState, + read::CallFrameInstruction::RestoreState => CallFrameInstruction::RestoreState, + read::CallFrameInstruction::ArgsSize { size } => { + CallFrameInstruction::ArgsSize(size as u32) + } + read::CallFrameInstruction::NegateRaState => CallFrameInstruction::NegateRaState, + read::CallFrameInstruction::Nop => return Ok(None), + })) + } + } +} + +#[cfg(test)] +#[cfg(feature = "read")] +mod tests { + use super::*; + use crate::arch::X86_64; + use crate::read; + use crate::write::EndianVec; + use crate::{LittleEndian, Vendor}; + + #[test] + fn test_frame_table() { + for &version in &[1, 3, 4] { + for &address_size in &[4, 8] { + for &format in &[Format::Dwarf32, Format::Dwarf64] { + let encoding = Encoding { + format, + version, + address_size, + }; + let mut frames = FrameTable::default(); + + let cie1 = CommonInformationEntry::new(encoding, 1, 8, X86_64::RA); + let cie1_id = frames.add_cie(cie1.clone()); + assert_eq!(cie1_id, frames.add_cie(cie1.clone())); + + let mut cie2 = CommonInformationEntry::new(encoding, 1, 8, X86_64::RA); + cie2.lsda_encoding = Some(constants::DW_EH_PE_absptr); + cie2.personality = + Some((constants::DW_EH_PE_absptr, Address::Constant(0x1234))); + cie2.signal_trampoline = true; + let cie2_id = frames.add_cie(cie2.clone()); + assert_ne!(cie1_id, cie2_id); + assert_eq!(cie2_id, frames.add_cie(cie2.clone())); + + let fde1 = FrameDescriptionEntry::new(Address::Constant(0x1000), 0x10); + frames.add_fde(cie1_id, fde1.clone()); + + let fde2 = FrameDescriptionEntry::new(Address::Constant(0x2000), 0x20); + frames.add_fde(cie1_id, fde2.clone()); + + let mut fde3 = FrameDescriptionEntry::new(Address::Constant(0x3000), 0x30); + fde3.lsda = Some(Address::Constant(0x3300)); + frames.add_fde(cie2_id, fde3.clone()); + + let mut fde4 = FrameDescriptionEntry::new(Address::Constant(0x4000), 0x40); + fde4.lsda = Some(Address::Constant(0x4400)); + frames.add_fde(cie2_id, fde4.clone()); + + let mut cie3 = CommonInformationEntry::new(encoding, 1, 8, X86_64::RA); + cie3.fde_address_encoding = constants::DW_EH_PE_pcrel; + cie3.lsda_encoding = Some(constants::DW_EH_PE_pcrel); + cie3.personality = Some((constants::DW_EH_PE_pcrel, Address::Constant(0x1235))); + cie3.signal_trampoline = true; + let cie3_id = frames.add_cie(cie3.clone()); + assert_ne!(cie2_id, cie3_id); + assert_eq!(cie3_id, frames.add_cie(cie3.clone())); + + let mut fde5 = FrameDescriptionEntry::new(Address::Constant(0x5000), 0x50); + fde5.lsda = Some(Address::Constant(0x5500)); + frames.add_fde(cie3_id, fde5.clone()); + + // Test writing `.debug_frame`. + let mut debug_frame = DebugFrame::from(EndianVec::new(LittleEndian)); + frames.write_debug_frame(&mut debug_frame).unwrap(); + + let mut read_debug_frame = + read::DebugFrame::new(debug_frame.slice(), LittleEndian); + read_debug_frame.set_address_size(address_size); + let convert_frames = FrameTable::from(&read_debug_frame, &|address| { + Some(Address::Constant(address)) + }) + .unwrap(); + assert_eq!(frames.cies, convert_frames.cies); + assert_eq!(frames.fdes.len(), convert_frames.fdes.len()); + for (a, b) in frames.fdes.iter().zip(convert_frames.fdes.iter()) { + assert_eq!(a.1, b.1); + } + + if version == 1 { + // Test writing `.eh_frame`. + let mut eh_frame = EhFrame::from(EndianVec::new(LittleEndian)); + frames.write_eh_frame(&mut eh_frame).unwrap(); + + let mut read_eh_frame = read::EhFrame::new(eh_frame.slice(), LittleEndian); + read_eh_frame.set_address_size(address_size); + let convert_frames = FrameTable::from(&read_eh_frame, &|address| { + Some(Address::Constant(address)) + }) + .unwrap(); + assert_eq!(frames.cies, convert_frames.cies); + assert_eq!(frames.fdes.len(), convert_frames.fdes.len()); + for (a, b) in frames.fdes.iter().zip(convert_frames.fdes.iter()) { + assert_eq!(a.1, b.1); + } + } + } + } + } + } + + #[test] + fn test_frame_instruction() { + let mut expression = Expression::new(); + expression.op_constu(0); + + let cie_instructions = [ + CallFrameInstruction::Cfa(X86_64::RSP, 8), + CallFrameInstruction::Offset(X86_64::RA, -8), + ]; + + let fde_instructions = [ + (0, CallFrameInstruction::Cfa(X86_64::RSP, 0)), + (0, CallFrameInstruction::Cfa(X86_64::RSP, -8)), + (2, CallFrameInstruction::CfaRegister(X86_64::RBP)), + (4, CallFrameInstruction::CfaOffset(8)), + (4, CallFrameInstruction::CfaOffset(0)), + (4, CallFrameInstruction::CfaOffset(-8)), + (6, CallFrameInstruction::CfaExpression(expression.clone())), + (8, CallFrameInstruction::Restore(Register(1))), + (8, CallFrameInstruction::Restore(Register(101))), + (10, CallFrameInstruction::Undefined(Register(2))), + (12, CallFrameInstruction::SameValue(Register(3))), + (14, CallFrameInstruction::Offset(Register(4), 16)), + (14, CallFrameInstruction::Offset(Register(104), 16)), + (16, CallFrameInstruction::ValOffset(Register(5), -24)), + (16, CallFrameInstruction::ValOffset(Register(5), 24)), + (18, CallFrameInstruction::Register(Register(6), Register(7))), + ( + 20, + CallFrameInstruction::Expression(Register(8), expression.clone()), + ), + ( + 22, + CallFrameInstruction::ValExpression(Register(9), expression.clone()), + ), + (24 + 0x80, CallFrameInstruction::RememberState), + (26 + 0x280, CallFrameInstruction::RestoreState), + (28 + 0x20280, CallFrameInstruction::ArgsSize(23)), + ]; + + let fde_instructions_aarch64 = [(0, CallFrameInstruction::NegateRaState)]; + + for &version in &[1, 3, 4] { + for &address_size in &[4, 8] { + for &vendor in &[Vendor::Default, Vendor::AArch64] { + for &format in &[Format::Dwarf32, Format::Dwarf64] { + let encoding = Encoding { + format, + version, + address_size, + }; + let mut frames = FrameTable::default(); + + let mut cie = CommonInformationEntry::new(encoding, 2, 8, X86_64::RA); + for i in &cie_instructions { + cie.add_instruction(i.clone()); + } + let cie_id = frames.add_cie(cie); + + let mut fde = FrameDescriptionEntry::new(Address::Constant(0x1000), 0x10); + for (o, i) in &fde_instructions { + fde.add_instruction(*o, i.clone()); + } + frames.add_fde(cie_id, fde); + + if vendor == Vendor::AArch64 { + let mut fde = + FrameDescriptionEntry::new(Address::Constant(0x2000), 0x10); + for (o, i) in &fde_instructions_aarch64 { + fde.add_instruction(*o, i.clone()); + } + frames.add_fde(cie_id, fde); + } + + let mut debug_frame = DebugFrame::from(EndianVec::new(LittleEndian)); + frames.write_debug_frame(&mut debug_frame).unwrap(); + + let mut read_debug_frame = + read::DebugFrame::new(debug_frame.slice(), LittleEndian); + read_debug_frame.set_address_size(address_size); + read_debug_frame.set_vendor(vendor); + let frames = FrameTable::from(&read_debug_frame, &|address| { + Some(Address::Constant(address)) + }) + .unwrap(); + + assert_eq!( + &frames.cies.get_index(0).unwrap().instructions, + &cie_instructions + ); + assert_eq!(&frames.fdes[0].1.instructions, &fde_instructions); + if vendor == Vendor::AArch64 { + assert_eq!(&frames.fdes[1].1.instructions, &fde_instructions_aarch64); + } + } + } + } + } + } +} diff --git a/vendor/gimli/src/write/dwarf.rs b/vendor/gimli/src/write/dwarf.rs new file mode 100644 index 0000000..ea50712 --- /dev/null +++ b/vendor/gimli/src/write/dwarf.rs @@ -0,0 +1,138 @@ +use alloc::vec::Vec; + +use crate::common::Encoding; +use crate::write::{ + AbbreviationTable, LineProgram, LineStringTable, Result, Sections, StringTable, Unit, + UnitTable, Writer, +}; + +/// Writable DWARF information for more than one unit. +#[derive(Debug, Default)] +pub struct Dwarf { + /// A table of units. These are primarily stored in the `.debug_info` section, + /// but they also contain information that is stored in other sections. + pub units: UnitTable, + + /// Extra line number programs that are not associated with a unit. + /// + /// These should only be used when generating DWARF5 line-only debug + /// information. + pub line_programs: Vec<LineProgram>, + + /// A table of strings that will be stored in the `.debug_line_str` section. + pub line_strings: LineStringTable, + + /// A table of strings that will be stored in the `.debug_str` section. + pub strings: StringTable, +} + +impl Dwarf { + /// Create a new `Dwarf` instance. + #[inline] + pub fn new() -> Self { + Self::default() + } + + /// Write the DWARF information to the given sections. + pub fn write<W: Writer>(&mut self, sections: &mut Sections<W>) -> Result<()> { + let line_strings = self.line_strings.write(&mut sections.debug_line_str)?; + let strings = self.strings.write(&mut sections.debug_str)?; + self.units.write(sections, &line_strings, &strings)?; + for line_program in &self.line_programs { + line_program.write( + &mut sections.debug_line, + line_program.encoding(), + &line_strings, + &strings, + )?; + } + Ok(()) + } +} + +/// Writable DWARF information for a single unit. +#[derive(Debug)] +pub struct DwarfUnit { + /// A unit. This is primarily stored in the `.debug_info` section, + /// but also contains information that is stored in other sections. + pub unit: Unit, + + /// A table of strings that will be stored in the `.debug_line_str` section. + pub line_strings: LineStringTable, + + /// A table of strings that will be stored in the `.debug_str` section. + pub strings: StringTable, +} + +impl DwarfUnit { + /// Create a new `DwarfUnit`. + /// + /// Note: you should set `self.unit.line_program` after creation. + /// This cannot be done earlier because it may need to reference + /// `self.line_strings`. + pub fn new(encoding: Encoding) -> Self { + let unit = Unit::new(encoding, LineProgram::none()); + DwarfUnit { + unit, + line_strings: LineStringTable::default(), + strings: StringTable::default(), + } + } + + /// Write the DWARf information to the given sections. + pub fn write<W: Writer>(&mut self, sections: &mut Sections<W>) -> Result<()> { + let line_strings = self.line_strings.write(&mut sections.debug_line_str)?; + let strings = self.strings.write(&mut sections.debug_str)?; + + let abbrev_offset = sections.debug_abbrev.offset(); + let mut abbrevs = AbbreviationTable::default(); + + self.unit.write( + sections, + abbrev_offset, + &mut abbrevs, + &line_strings, + &strings, + )?; + // None should exist because we didn't give out any UnitId. + assert!(sections.debug_info_refs.is_empty()); + assert!(sections.debug_loc_refs.is_empty()); + assert!(sections.debug_loclists_refs.is_empty()); + + abbrevs.write(&mut sections.debug_abbrev)?; + Ok(()) + } +} + +#[cfg(feature = "read")] +pub(crate) mod convert { + use super::*; + use crate::read::{self, Reader}; + use crate::write::{Address, ConvertResult}; + + impl Dwarf { + /// Create a `write::Dwarf` by converting a `read::Dwarf`. + /// + /// `convert_address` is a function to convert read addresses into the `Address` + /// type. For non-relocatable addresses, this function may simply return + /// `Address::Constant(address)`. For relocatable addresses, it is the caller's + /// responsibility to determine the symbol and addend corresponding to the address + /// and return `Address::Symbol { symbol, addend }`. + pub fn from<R: Reader<Offset = usize>>( + dwarf: &read::Dwarf<R>, + convert_address: &dyn Fn(u64) -> Option<Address>, + ) -> ConvertResult<Dwarf> { + let mut line_strings = LineStringTable::default(); + let mut strings = StringTable::default(); + let units = UnitTable::from(dwarf, &mut line_strings, &mut strings, convert_address)?; + // TODO: convert the line programs that were not referenced by a unit. + let line_programs = Vec::new(); + Ok(Dwarf { + units, + line_programs, + line_strings, + strings, + }) + } + } +} diff --git a/vendor/gimli/src/write/endian_vec.rs b/vendor/gimli/src/write/endian_vec.rs new file mode 100644 index 0000000..7b04060 --- /dev/null +++ b/vendor/gimli/src/write/endian_vec.rs @@ -0,0 +1,117 @@ +use alloc::vec::Vec; +use std::mem; + +use crate::endianity::Endianity; +use crate::write::{Error, Result, Writer}; + +/// A `Vec<u8>` with endianity metadata. +/// +/// This implements the `Writer` trait, which is used for all writing of DWARF sections. +#[derive(Debug, Clone)] +pub struct EndianVec<Endian> +where + Endian: Endianity, +{ + vec: Vec<u8>, + endian: Endian, +} + +impl<Endian> EndianVec<Endian> +where + Endian: Endianity, +{ + /// Construct an empty `EndianVec` with the given endianity. + pub fn new(endian: Endian) -> EndianVec<Endian> { + EndianVec { + vec: Vec::new(), + endian, + } + } + + /// Return a reference to the raw slice. + pub fn slice(&self) -> &[u8] { + &self.vec + } + + /// Convert into a `Vec<u8>`. + pub fn into_vec(self) -> Vec<u8> { + self.vec + } + + /// Take any written data out of the `EndianVec`, leaving an empty `Vec` in its place. + pub fn take(&mut self) -> Vec<u8> { + let mut vec = Vec::new(); + mem::swap(&mut self.vec, &mut vec); + vec + } +} + +impl<Endian> Writer for EndianVec<Endian> +where + Endian: Endianity, +{ + type Endian = Endian; + + #[inline] + fn endian(&self) -> Self::Endian { + self.endian + } + + #[inline] + fn len(&self) -> usize { + self.vec.len() + } + + fn write(&mut self, bytes: &[u8]) -> Result<()> { + self.vec.extend(bytes); + Ok(()) + } + + fn write_at(&mut self, offset: usize, bytes: &[u8]) -> Result<()> { + if offset > self.vec.len() { + return Err(Error::OffsetOutOfBounds); + } + let to = &mut self.vec[offset..]; + if bytes.len() > to.len() { + return Err(Error::LengthOutOfBounds); + } + let to = &mut to[..bytes.len()]; + to.copy_from_slice(bytes); + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::LittleEndian; + + #[test] + fn test_endian_vec() { + let mut w = EndianVec::new(LittleEndian); + assert_eq!(w.endian(), LittleEndian); + assert_eq!(w.len(), 0); + + w.write(&[1, 2]).unwrap(); + assert_eq!(w.slice(), &[1, 2]); + assert_eq!(w.len(), 2); + + w.write(&[3, 4, 5]).unwrap(); + assert_eq!(w.slice(), &[1, 2, 3, 4, 5]); + assert_eq!(w.len(), 5); + + w.write_at(0, &[6, 7]).unwrap(); + assert_eq!(w.slice(), &[6, 7, 3, 4, 5]); + assert_eq!(w.len(), 5); + + w.write_at(3, &[8, 9]).unwrap(); + assert_eq!(w.slice(), &[6, 7, 3, 8, 9]); + assert_eq!(w.len(), 5); + + assert_eq!(w.write_at(4, &[6, 7]), Err(Error::LengthOutOfBounds)); + assert_eq!(w.write_at(5, &[6, 7]), Err(Error::LengthOutOfBounds)); + assert_eq!(w.write_at(6, &[6, 7]), Err(Error::OffsetOutOfBounds)); + + assert_eq!(w.into_vec(), vec![6, 7, 3, 8, 9]); + } +} diff --git a/vendor/gimli/src/write/line.rs b/vendor/gimli/src/write/line.rs new file mode 100644 index 0000000..c88b735 --- /dev/null +++ b/vendor/gimli/src/write/line.rs @@ -0,0 +1,1957 @@ +use alloc::vec::Vec; +use indexmap::{IndexMap, IndexSet}; +use std::ops::{Deref, DerefMut}; + +use crate::common::{DebugLineOffset, Encoding, Format, LineEncoding, SectionId}; +use crate::constants; +use crate::leb128; +use crate::write::{ + Address, DebugLineStrOffsets, DebugStrOffsets, Error, LineStringId, LineStringTable, Result, + Section, StringId, Writer, +}; + +/// The number assigned to the first special opcode. +// +// We output all instructions for all DWARF versions, since readers +// should be able to ignore instructions they don't support. +const OPCODE_BASE: u8 = 13; + +/// A line number program. +#[derive(Debug, Clone)] +pub struct LineProgram { + /// True if this line program was created with `LineProgram::none()`. + none: bool, + encoding: Encoding, + line_encoding: LineEncoding, + + /// A list of source directory path names. + /// + /// If a path is relative, then the directory is located relative to the working + /// directory of the compilation unit. + /// + /// The first entry is for the working directory of the compilation unit. + directories: IndexSet<LineString>, + + /// A list of source file entries. + /// + /// Each entry has a path name and a directory. + /// + /// If a path is a relative, then the file is located relative to the + /// directory. Otherwise the directory is meaningless. + /// + /// Does not include comp_file, even for version >= 5. + files: IndexMap<(LineString, DirectoryId), FileInfo>, + + /// The primary source file of the compilation unit. + /// This is required for version >= 5, but we never reference it elsewhere + /// because DWARF defines DW_AT_decl_file=0 to mean not specified. + comp_file: (LineString, FileInfo), + + /// True if the file entries may have valid timestamps. + /// + /// Entries may still have a timestamp of 0 even if this is set. + /// For version <= 4, this is ignored. + /// For version 5, this controls whether to emit `DW_LNCT_timestamp`. + pub file_has_timestamp: bool, + + /// True if the file entries may have valid sizes. + /// + /// Entries may still have a size of 0 even if this is set. + /// For version <= 4, this is ignored. + /// For version 5, this controls whether to emit `DW_LNCT_size`. + pub file_has_size: bool, + + /// True if the file entries have valid MD5 checksums. + /// + /// For version <= 4, this is ignored. + /// For version 5, this controls whether to emit `DW_LNCT_MD5`. + pub file_has_md5: bool, + + prev_row: LineRow, + row: LineRow, + // TODO: this probably should be either rows or sequences instead + instructions: Vec<LineInstruction>, + in_sequence: bool, +} + +impl LineProgram { + /// Create a new `LineProgram`. + /// + /// `comp_dir` defines the working directory of the compilation unit, + /// and must be the same as the `DW_AT_comp_dir` attribute + /// of the compilation unit DIE. + /// + /// `comp_file` and `comp_file_info` define the primary source file + /// of the compilation unit and must be the same as the `DW_AT_name` + /// attribute of the compilation unit DIE. + /// + /// # Panics + /// + /// Panics if `line_encoding.line_base` > 0. + /// + /// Panics if `line_encoding.line_base` + `line_encoding.line_range` <= 0. + /// + /// Panics if `comp_dir` is empty or contains a null byte. + /// + /// Panics if `comp_file` is empty or contains a null byte. + pub fn new( + encoding: Encoding, + line_encoding: LineEncoding, + comp_dir: LineString, + comp_file: LineString, + comp_file_info: Option<FileInfo>, + ) -> LineProgram { + // We require a special opcode for a line advance of 0. + // See the debug_asserts in generate_row(). + assert!(line_encoding.line_base <= 0); + assert!(line_encoding.line_base + line_encoding.line_range as i8 > 0); + let mut program = LineProgram { + none: false, + encoding, + line_encoding, + directories: IndexSet::new(), + files: IndexMap::new(), + comp_file: (comp_file, comp_file_info.unwrap_or_default()), + prev_row: LineRow::initial_state(line_encoding), + row: LineRow::initial_state(line_encoding), + instructions: Vec::new(), + in_sequence: false, + file_has_timestamp: false, + file_has_size: false, + file_has_md5: false, + }; + // For all DWARF versions, directory index 0 is comp_dir. + // For version <= 4, the entry is implicit. We still add + // it here so that we use it, but we don't emit it. + program.add_directory(comp_dir); + program + } + + /// Create a new `LineProgram` with no fields set. + /// + /// This can be used when the `LineProgram` will not be used. + /// + /// You should not attempt to add files or line instructions to + /// this line program, or write it to the `.debug_line` section. + pub fn none() -> Self { + let line_encoding = LineEncoding::default(); + LineProgram { + none: true, + encoding: Encoding { + format: Format::Dwarf32, + version: 2, + address_size: 0, + }, + line_encoding, + directories: IndexSet::new(), + files: IndexMap::new(), + comp_file: (LineString::String(Vec::new()), FileInfo::default()), + prev_row: LineRow::initial_state(line_encoding), + row: LineRow::initial_state(line_encoding), + instructions: Vec::new(), + in_sequence: false, + file_has_timestamp: false, + file_has_size: false, + file_has_md5: false, + } + } + + /// Return true if this line program was created with `LineProgram::none()`. + #[inline] + pub fn is_none(&self) -> bool { + self.none + } + + /// Return the encoding parameters for this line program. + #[inline] + pub fn encoding(&self) -> Encoding { + self.encoding + } + + /// Return the DWARF version for this line program. + #[inline] + pub fn version(&self) -> u16 { + self.encoding.version + } + + /// Return the address size in bytes for this line program. + #[inline] + pub fn address_size(&self) -> u8 { + self.encoding.address_size + } + + /// Return the DWARF format for this line program. + #[inline] + pub fn format(&self) -> Format { + self.encoding.format + } + + /// Return the id for the working directory of the compilation unit. + #[inline] + pub fn default_directory(&self) -> DirectoryId { + DirectoryId(0) + } + + /// Add a directory entry and return its id. + /// + /// If the directory already exists, then return the id of the existing entry. + /// + /// If the path is relative, then the directory is located relative to the working + /// directory of the compilation unit. + /// + /// # Panics + /// + /// Panics if `directory` is empty or contains a null byte. + pub fn add_directory(&mut self, directory: LineString) -> DirectoryId { + if let LineString::String(ref val) = directory { + // For DWARF version <= 4, directories must not be empty. + // The first directory isn't emitted so skip the check for it. + if self.encoding.version <= 4 && !self.directories.is_empty() { + assert!(!val.is_empty()); + } + assert!(!val.contains(&0)); + } + let (index, _) = self.directories.insert_full(directory); + DirectoryId(index) + } + + /// Get a reference to a directory entry. + /// + /// # Panics + /// + /// Panics if `id` is invalid. + pub fn get_directory(&self, id: DirectoryId) -> &LineString { + self.directories.get_index(id.0).unwrap() + } + + /// Add a file entry and return its id. + /// + /// If the file already exists, then return the id of the existing entry. + /// + /// If the file path is relative, then the file is located relative + /// to the directory. Otherwise the directory is meaningless, but it + /// is still used as a key for file entries. + /// + /// If `info` is `None`, then new entries are assigned + /// default information, and existing entries are unmodified. + /// + /// If `info` is not `None`, then it is always assigned to the + /// entry, even if the entry already exists. + /// + /// # Panics + /// + /// Panics if 'file' is empty or contains a null byte. + pub fn add_file( + &mut self, + file: LineString, + directory: DirectoryId, + info: Option<FileInfo>, + ) -> FileId { + if let LineString::String(ref val) = file { + assert!(!val.is_empty()); + assert!(!val.contains(&0)); + } + + let key = (file, directory); + let index = if let Some(info) = info { + let (index, _) = self.files.insert_full(key, info); + index + } else { + let entry = self.files.entry(key); + let index = entry.index(); + entry.or_default(); + index + }; + FileId::new(index) + } + + /// Get a reference to a file entry. + /// + /// # Panics + /// + /// Panics if `id` is invalid. + pub fn get_file(&self, id: FileId) -> (&LineString, DirectoryId) { + match id.index() { + None => (&self.comp_file.0, DirectoryId(0)), + Some(index) => self + .files + .get_index(index) + .map(|entry| (&(entry.0).0, (entry.0).1)) + .unwrap(), + } + } + + /// Get a reference to the info for a file entry. + /// + /// # Panics + /// + /// Panics if `id` is invalid. + pub fn get_file_info(&self, id: FileId) -> &FileInfo { + match id.index() { + None => &self.comp_file.1, + Some(index) => self.files.get_index(index).map(|entry| entry.1).unwrap(), + } + } + + /// Get a mutable reference to the info for a file entry. + /// + /// # Panics + /// + /// Panics if `id` is invalid. + pub fn get_file_info_mut(&mut self, id: FileId) -> &mut FileInfo { + match id.index() { + None => &mut self.comp_file.1, + Some(index) => self + .files + .get_index_mut(index) + .map(|entry| entry.1) + .unwrap(), + } + } + + /// Begin a new sequence and set its base address. + /// + /// # Panics + /// + /// Panics if a sequence has already begun. + pub fn begin_sequence(&mut self, address: Option<Address>) { + assert!(!self.in_sequence); + self.in_sequence = true; + if let Some(address) = address { + self.instructions.push(LineInstruction::SetAddress(address)); + } + } + + /// End the sequence, and reset the row to its default values. + /// + /// Only the `address_offset` and op_index` fields of the current row are used. + /// + /// # Panics + /// + /// Panics if a sequence has not begun. + pub fn end_sequence(&mut self, address_offset: u64) { + assert!(self.in_sequence); + self.in_sequence = false; + self.row.address_offset = address_offset; + let op_advance = self.op_advance(); + if op_advance != 0 { + self.instructions + .push(LineInstruction::AdvancePc(op_advance)); + } + self.instructions.push(LineInstruction::EndSequence); + self.prev_row = LineRow::initial_state(self.line_encoding); + self.row = LineRow::initial_state(self.line_encoding); + } + + /// Return true if a sequence has begun. + #[inline] + pub fn in_sequence(&self) -> bool { + self.in_sequence + } + + /// Returns a reference to the data for the current row. + #[inline] + pub fn row(&mut self) -> &mut LineRow { + &mut self.row + } + + /// Generates the line number information instructions for the current row. + /// + /// After the instructions are generated, it sets `discriminator` to 0, and sets + /// `basic_block`, `prologue_end`, and `epilogue_begin` to false. + /// + /// # Panics + /// + /// Panics if a sequence has not begun. + /// Panics if the address_offset decreases. + pub fn generate_row(&mut self) { + assert!(self.in_sequence); + + // Output fields that are reset on every row. + if self.row.discriminator != 0 { + self.instructions + .push(LineInstruction::SetDiscriminator(self.row.discriminator)); + self.row.discriminator = 0; + } + if self.row.basic_block { + self.instructions.push(LineInstruction::SetBasicBlock); + self.row.basic_block = false; + } + if self.row.prologue_end { + self.instructions.push(LineInstruction::SetPrologueEnd); + self.row.prologue_end = false; + } + if self.row.epilogue_begin { + self.instructions.push(LineInstruction::SetEpilogueBegin); + self.row.epilogue_begin = false; + } + + // Output fields that are not reset on every row. + if self.row.is_statement != self.prev_row.is_statement { + self.instructions.push(LineInstruction::NegateStatement); + } + if self.row.file != self.prev_row.file { + self.instructions + .push(LineInstruction::SetFile(self.row.file)); + } + if self.row.column != self.prev_row.column { + self.instructions + .push(LineInstruction::SetColumn(self.row.column)); + } + if self.row.isa != self.prev_row.isa { + self.instructions + .push(LineInstruction::SetIsa(self.row.isa)); + } + + // Advance the line, address, and operation index. + let line_base = i64::from(self.line_encoding.line_base) as u64; + let line_range = u64::from(self.line_encoding.line_range); + let line_advance = self.row.line as i64 - self.prev_row.line as i64; + let op_advance = self.op_advance(); + + // Default to special advances of 0. + let special_base = u64::from(OPCODE_BASE); + // TODO: handle lack of special opcodes for 0 line advance + debug_assert!(self.line_encoding.line_base <= 0); + debug_assert!(self.line_encoding.line_base + self.line_encoding.line_range as i8 >= 0); + let special_default = special_base.wrapping_sub(line_base); + let mut special = special_default; + let mut use_special = false; + + if line_advance != 0 { + let special_line = (line_advance as u64).wrapping_sub(line_base); + if special_line < line_range { + special = special_base + special_line; + use_special = true; + } else { + self.instructions + .push(LineInstruction::AdvanceLine(line_advance)); + } + } + + if op_advance != 0 { + // Using ConstAddPc can save a byte. + let (special_op_advance, const_add_pc) = if special + op_advance * line_range <= 255 { + (op_advance, false) + } else { + let op_range = (255 - special_base) / line_range; + (op_advance - op_range, true) + }; + + let special_op = special_op_advance * line_range; + if special + special_op <= 255 { + special += special_op; + use_special = true; + if const_add_pc { + self.instructions.push(LineInstruction::ConstAddPc); + } + } else { + self.instructions + .push(LineInstruction::AdvancePc(op_advance)); + } + } + + if use_special && special != special_default { + debug_assert!(special >= special_base); + debug_assert!(special <= 255); + self.instructions + .push(LineInstruction::Special(special as u8)); + } else { + self.instructions.push(LineInstruction::Copy); + } + + self.prev_row = self.row; + } + + fn op_advance(&self) -> u64 { + debug_assert!(self.row.address_offset >= self.prev_row.address_offset); + let mut address_advance = self.row.address_offset - self.prev_row.address_offset; + if self.line_encoding.minimum_instruction_length != 1 { + debug_assert_eq!( + self.row.address_offset % u64::from(self.line_encoding.minimum_instruction_length), + 0 + ); + address_advance /= u64::from(self.line_encoding.minimum_instruction_length); + } + address_advance * u64::from(self.line_encoding.maximum_operations_per_instruction) + + self.row.op_index + - self.prev_row.op_index + } + + /// Returns true if the line number program has no instructions. + /// + /// Does not check the file or directory entries. + #[inline] + pub fn is_empty(&self) -> bool { + self.instructions.is_empty() + } + + /// Write the line number program to the given section. + /// + /// # Panics + /// + /// Panics if `self.is_none()`. + pub fn write<W: Writer>( + &self, + w: &mut DebugLine<W>, + encoding: Encoding, + debug_line_str_offsets: &DebugLineStrOffsets, + debug_str_offsets: &DebugStrOffsets, + ) -> Result<DebugLineOffset> { + assert!(!self.is_none()); + + if encoding.version < self.version() + || encoding.format != self.format() + || encoding.address_size != self.address_size() + { + return Err(Error::IncompatibleLineProgramEncoding); + } + + let offset = w.offset(); + + let length_offset = w.write_initial_length(self.format())?; + let length_base = w.len(); + + if self.version() < 2 || self.version() > 5 { + return Err(Error::UnsupportedVersion(self.version())); + } + w.write_u16(self.version())?; + + if self.version() >= 5 { + w.write_u8(self.address_size())?; + // Segment selector size. + w.write_u8(0)?; + } + + let header_length_offset = w.len(); + w.write_udata(0, self.format().word_size())?; + let header_length_base = w.len(); + + w.write_u8(self.line_encoding.minimum_instruction_length)?; + if self.version() >= 4 { + w.write_u8(self.line_encoding.maximum_operations_per_instruction)?; + } else if self.line_encoding.maximum_operations_per_instruction != 1 { + return Err(Error::NeedVersion(4)); + }; + w.write_u8(if self.line_encoding.default_is_stmt { + 1 + } else { + 0 + })?; + w.write_u8(self.line_encoding.line_base as u8)?; + w.write_u8(self.line_encoding.line_range)?; + w.write_u8(OPCODE_BASE)?; + w.write(&[0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1])?; + + if self.version() <= 4 { + // The first directory is stored as DW_AT_comp_dir. + for dir in self.directories.iter().skip(1) { + dir.write( + w, + constants::DW_FORM_string, + self.encoding, + debug_line_str_offsets, + debug_str_offsets, + )?; + } + w.write_u8(0)?; + + for ((file, dir), info) in self.files.iter() { + file.write( + w, + constants::DW_FORM_string, + self.encoding, + debug_line_str_offsets, + debug_str_offsets, + )?; + w.write_uleb128(dir.0 as u64)?; + w.write_uleb128(info.timestamp)?; + w.write_uleb128(info.size)?; + } + w.write_u8(0)?; + } else { + // Directory entry formats (only ever 1). + w.write_u8(1)?; + w.write_uleb128(u64::from(constants::DW_LNCT_path.0))?; + let dir_form = self.directories.get_index(0).unwrap().form(); + w.write_uleb128(dir_form.0.into())?; + + // Directory entries. + w.write_uleb128(self.directories.len() as u64)?; + for dir in self.directories.iter() { + dir.write( + w, + dir_form, + self.encoding, + debug_line_str_offsets, + debug_str_offsets, + )?; + } + + // File name entry formats. + let count = 2 + + if self.file_has_timestamp { 1 } else { 0 } + + if self.file_has_size { 1 } else { 0 } + + if self.file_has_md5 { 1 } else { 0 }; + w.write_u8(count)?; + w.write_uleb128(u64::from(constants::DW_LNCT_path.0))?; + let file_form = self.comp_file.0.form(); + w.write_uleb128(file_form.0.into())?; + w.write_uleb128(u64::from(constants::DW_LNCT_directory_index.0))?; + w.write_uleb128(constants::DW_FORM_udata.0.into())?; + if self.file_has_timestamp { + w.write_uleb128(u64::from(constants::DW_LNCT_timestamp.0))?; + w.write_uleb128(constants::DW_FORM_udata.0.into())?; + } + if self.file_has_size { + w.write_uleb128(u64::from(constants::DW_LNCT_size.0))?; + w.write_uleb128(constants::DW_FORM_udata.0.into())?; + } + if self.file_has_md5 { + w.write_uleb128(u64::from(constants::DW_LNCT_MD5.0))?; + w.write_uleb128(constants::DW_FORM_data16.0.into())?; + } + + // File name entries. + w.write_uleb128(self.files.len() as u64 + 1)?; + let mut write_file = |file: &LineString, dir: DirectoryId, info: &FileInfo| { + file.write( + w, + file_form, + self.encoding, + debug_line_str_offsets, + debug_str_offsets, + )?; + w.write_uleb128(dir.0 as u64)?; + if self.file_has_timestamp { + w.write_uleb128(info.timestamp)?; + } + if self.file_has_size { + w.write_uleb128(info.size)?; + } + if self.file_has_md5 { + w.write(&info.md5)?; + } + Ok(()) + }; + write_file(&self.comp_file.0, DirectoryId(0), &self.comp_file.1)?; + for ((file, dir), info) in self.files.iter() { + write_file(file, *dir, info)?; + } + } + + let header_length = (w.len() - header_length_base) as u64; + w.write_udata_at( + header_length_offset, + header_length, + self.format().word_size(), + )?; + + for instruction in &self.instructions { + instruction.write(w, self.address_size())?; + } + + let length = (w.len() - length_base) as u64; + w.write_initial_length_at(length_offset, length, self.format())?; + + Ok(offset) + } +} + +/// A row in the line number table that corresponds to a machine instruction. +#[derive(Debug, Clone, Copy)] +pub struct LineRow { + /// The offset of the instruction from the start address of the sequence. + pub address_offset: u64, + /// The index of an operation within a VLIW instruction. + /// + /// The index of the first operation is 0. + /// Set to 0 for non-VLIW instructions. + pub op_index: u64, + + /// The source file corresponding to the instruction. + pub file: FileId, + /// The line number within the source file. + /// + /// Lines are numbered beginning at 1. Set to 0 if there is no source line. + pub line: u64, + /// The column number within the source line. + /// + /// Columns are numbered beginning at 1. Set to 0 for the "left edge" of the line. + pub column: u64, + /// An additional discriminator used to distinguish between source locations. + /// This value is assigned arbitrarily by the DWARF producer. + pub discriminator: u64, + + /// Set to true if the instruction is a recommended breakpoint for a statement. + pub is_statement: bool, + /// Set to true if the instruction is the beginning of a basic block. + pub basic_block: bool, + /// Set to true if the instruction is a recommended breakpoint at the entry of a + /// function. + pub prologue_end: bool, + /// Set to true if the instruction is a recommended breakpoint prior to the exit of + /// a function. + pub epilogue_begin: bool, + + /// The instruction set architecture of the instruction. + /// + /// Set to 0 for the default ISA. Other values are defined by the architecture ABI. + pub isa: u64, +} + +impl LineRow { + /// Return the initial state as specified in the DWARF standard. + fn initial_state(line_encoding: LineEncoding) -> Self { + LineRow { + address_offset: 0, + op_index: 0, + + file: FileId::initial_state(), + line: 1, + column: 0, + discriminator: 0, + + is_statement: line_encoding.default_is_stmt, + basic_block: false, + prologue_end: false, + epilogue_begin: false, + + isa: 0, + } + } +} + +/// An instruction in a line number program. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum LineInstruction { + // Special opcodes + Special(u8), + + // Standard opcodes + Copy, + AdvancePc(u64), + AdvanceLine(i64), + SetFile(FileId), + SetColumn(u64), + NegateStatement, + SetBasicBlock, + ConstAddPc, + // DW_LNS_fixed_advance_pc is not supported. + SetPrologueEnd, + SetEpilogueBegin, + SetIsa(u64), + + // Extended opcodes + EndSequence, + // TODO: this doubles the size of this enum. + SetAddress(Address), + // DW_LNE_define_file is not supported. + SetDiscriminator(u64), +} + +impl LineInstruction { + /// Write the line number instruction to the given section. + fn write<W: Writer>(self, w: &mut DebugLine<W>, address_size: u8) -> Result<()> { + use self::LineInstruction::*; + match self { + Special(val) => w.write_u8(val)?, + Copy => w.write_u8(constants::DW_LNS_copy.0)?, + AdvancePc(val) => { + w.write_u8(constants::DW_LNS_advance_pc.0)?; + w.write_uleb128(val)?; + } + AdvanceLine(val) => { + w.write_u8(constants::DW_LNS_advance_line.0)?; + w.write_sleb128(val)?; + } + SetFile(val) => { + w.write_u8(constants::DW_LNS_set_file.0)?; + w.write_uleb128(val.raw())?; + } + SetColumn(val) => { + w.write_u8(constants::DW_LNS_set_column.0)?; + w.write_uleb128(val)?; + } + NegateStatement => w.write_u8(constants::DW_LNS_negate_stmt.0)?, + SetBasicBlock => w.write_u8(constants::DW_LNS_set_basic_block.0)?, + ConstAddPc => w.write_u8(constants::DW_LNS_const_add_pc.0)?, + SetPrologueEnd => w.write_u8(constants::DW_LNS_set_prologue_end.0)?, + SetEpilogueBegin => w.write_u8(constants::DW_LNS_set_epilogue_begin.0)?, + SetIsa(val) => { + w.write_u8(constants::DW_LNS_set_isa.0)?; + w.write_uleb128(val)?; + } + EndSequence => { + w.write_u8(0)?; + w.write_uleb128(1)?; + w.write_u8(constants::DW_LNE_end_sequence.0)?; + } + SetAddress(address) => { + w.write_u8(0)?; + w.write_uleb128(1 + u64::from(address_size))?; + w.write_u8(constants::DW_LNE_set_address.0)?; + w.write_address(address, address_size)?; + } + SetDiscriminator(val) => { + let mut bytes = [0u8; 10]; + // bytes is long enough so this will never fail. + let len = leb128::write::unsigned(&mut { &mut bytes[..] }, val).unwrap(); + w.write_u8(0)?; + w.write_uleb128(1 + len as u64)?; + w.write_u8(constants::DW_LNE_set_discriminator.0)?; + w.write(&bytes[..len])?; + } + } + Ok(()) + } +} + +/// A string value for use in defining paths in line number programs. +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum LineString { + /// A slice of bytes representing a string. Must not include null bytes. + /// Not guaranteed to be UTF-8 or anything like that. + String(Vec<u8>), + + /// A reference to a string in the `.debug_str` section. + StringRef(StringId), + + /// A reference to a string in the `.debug_line_str` section. + LineStringRef(LineStringId), +} + +impl LineString { + /// Create a `LineString` using the normal form for the given encoding. + pub fn new<T>(val: T, encoding: Encoding, line_strings: &mut LineStringTable) -> Self + where + T: Into<Vec<u8>>, + { + let val = val.into(); + if encoding.version <= 4 { + LineString::String(val) + } else { + LineString::LineStringRef(line_strings.add(val)) + } + } + + fn form(&self) -> constants::DwForm { + match *self { + LineString::String(..) => constants::DW_FORM_string, + LineString::StringRef(..) => constants::DW_FORM_strp, + LineString::LineStringRef(..) => constants::DW_FORM_line_strp, + } + } + + fn write<W: Writer>( + &self, + w: &mut DebugLine<W>, + form: constants::DwForm, + encoding: Encoding, + debug_line_str_offsets: &DebugLineStrOffsets, + debug_str_offsets: &DebugStrOffsets, + ) -> Result<()> { + if form != self.form() { + return Err(Error::LineStringFormMismatch); + } + + match *self { + LineString::String(ref val) => { + if encoding.version <= 4 { + debug_assert!(!val.is_empty()); + } + w.write(val)?; + w.write_u8(0)?; + } + LineString::StringRef(val) => { + if encoding.version < 5 { + return Err(Error::NeedVersion(5)); + } + w.write_offset( + debug_str_offsets.get(val).0, + SectionId::DebugStr, + encoding.format.word_size(), + )?; + } + LineString::LineStringRef(val) => { + if encoding.version < 5 { + return Err(Error::NeedVersion(5)); + } + w.write_offset( + debug_line_str_offsets.get(val).0, + SectionId::DebugLineStr, + encoding.format.word_size(), + )?; + } + } + Ok(()) + } +} + +/// An identifier for a directory in a `LineProgram`. +/// +/// Defaults to the working directory of the compilation unit. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct DirectoryId(usize); + +// Force FileId access via the methods. +mod id { + /// An identifier for a file in a `LineProgram`. + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] + pub struct FileId(usize); + + impl FileId { + /// Create a FileId given an index into `LineProgram::files`. + pub(crate) fn new(index: usize) -> Self { + FileId(index + 1) + } + + /// The index of the file in `LineProgram::files`. + pub(super) fn index(self) -> Option<usize> { + if self.0 == 0 { + None + } else { + Some(self.0 - 1) + } + } + + /// The initial state of the file register. + pub(super) fn initial_state() -> Self { + FileId(1) + } + + /// The raw value used when writing. + pub(crate) fn raw(self) -> u64 { + self.0 as u64 + } + + /// The id for file index 0 in DWARF version 5. + /// Only used when converting. + // Used for tests only. + #[allow(unused)] + pub(super) fn zero() -> Self { + FileId(0) + } + } +} +pub use self::id::*; + +/// Extra information for file in a `LineProgram`. +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] +pub struct FileInfo { + /// The implementation defined timestamp of the last modification of the file, + /// or 0 if not available. + pub timestamp: u64, + + /// The size of the file in bytes, or 0 if not available. + pub size: u64, + + /// A 16-byte MD5 digest of the file contents. + /// + /// Only used if version >= 5 and `LineProgram::file_has_md5` is `true`. + pub md5: [u8; 16], +} + +define_section!( + DebugLine, + DebugLineOffset, + "A writable `.debug_line` section." +); + +#[cfg(feature = "read")] +mod convert { + use super::*; + use crate::read::{self, Reader}; + use crate::write::{self, ConvertError, ConvertResult}; + + impl LineProgram { + /// Create a line number program by reading the data from the given program. + /// + /// Return the program and a mapping from file index to `FileId`. + pub fn from<R: Reader<Offset = usize>>( + mut from_program: read::IncompleteLineProgram<R>, + dwarf: &read::Dwarf<R>, + line_strings: &mut write::LineStringTable, + strings: &mut write::StringTable, + convert_address: &dyn Fn(u64) -> Option<Address>, + ) -> ConvertResult<(LineProgram, Vec<FileId>)> { + // Create mappings in case the source has duplicate files or directories. + let mut dirs = Vec::new(); + let mut files = Vec::new(); + + let mut program = { + let from_header = from_program.header(); + let encoding = from_header.encoding(); + + let comp_dir = match from_header.directory(0) { + Some(comp_dir) => LineString::from(comp_dir, dwarf, line_strings, strings)?, + None => LineString::new(&[][..], encoding, line_strings), + }; + + let (comp_name, comp_file_info) = match from_header.file(0) { + Some(comp_file) => { + if comp_file.directory_index() != 0 { + return Err(ConvertError::InvalidDirectoryIndex); + } + ( + LineString::from(comp_file.path_name(), dwarf, line_strings, strings)?, + Some(FileInfo { + timestamp: comp_file.timestamp(), + size: comp_file.size(), + md5: *comp_file.md5(), + }), + ) + } + None => (LineString::new(&[][..], encoding, line_strings), None), + }; + + if from_header.line_base() > 0 { + return Err(ConvertError::InvalidLineBase); + } + let mut program = LineProgram::new( + encoding, + from_header.line_encoding(), + comp_dir, + comp_name, + comp_file_info, + ); + + let file_skip; + if from_header.version() <= 4 { + // The first directory is implicit. + dirs.push(DirectoryId(0)); + // A file index of 0 is invalid for version <= 4, but putting + // something there makes the indexing easier. + file_skip = 0; + files.push(FileId::zero()); + } else { + // We don't add the first file to `files`, but still allow + // it to be referenced from converted instructions. + file_skip = 1; + files.push(FileId::zero()); + } + + for from_dir in from_header.include_directories() { + let from_dir = + LineString::from(from_dir.clone(), dwarf, line_strings, strings)?; + dirs.push(program.add_directory(from_dir)); + } + + program.file_has_timestamp = from_header.file_has_timestamp(); + program.file_has_size = from_header.file_has_size(); + program.file_has_md5 = from_header.file_has_md5(); + for from_file in from_header.file_names().iter().skip(file_skip) { + let from_name = + LineString::from(from_file.path_name(), dwarf, line_strings, strings)?; + let from_dir = from_file.directory_index(); + if from_dir >= dirs.len() as u64 { + return Err(ConvertError::InvalidDirectoryIndex); + } + let from_dir = dirs[from_dir as usize]; + let from_info = Some(FileInfo { + timestamp: from_file.timestamp(), + size: from_file.size(), + md5: *from_file.md5(), + }); + files.push(program.add_file(from_name, from_dir, from_info)); + } + + program + }; + + // We can't use the `from_program.rows()` because that wouldn't let + // us preserve address relocations. + let mut from_row = read::LineRow::new(from_program.header()); + let mut instructions = from_program.header().instructions(); + let mut address = None; + while let Some(instruction) = instructions.next_instruction(from_program.header())? { + match instruction { + read::LineInstruction::SetAddress(val) => { + if program.in_sequence() { + return Err(ConvertError::UnsupportedLineInstruction); + } + match convert_address(val) { + Some(val) => address = Some(val), + None => return Err(ConvertError::InvalidAddress), + } + from_row.execute(read::LineInstruction::SetAddress(0), &mut from_program); + } + read::LineInstruction::DefineFile(_) => { + return Err(ConvertError::UnsupportedLineInstruction); + } + _ => { + if from_row.execute(instruction, &mut from_program) { + if !program.in_sequence() { + program.begin_sequence(address); + address = None; + } + if from_row.end_sequence() { + program.end_sequence(from_row.address()); + } else { + program.row().address_offset = from_row.address(); + program.row().op_index = from_row.op_index(); + program.row().file = { + let file = from_row.file_index(); + if file >= files.len() as u64 { + return Err(ConvertError::InvalidFileIndex); + } + if file == 0 && program.version() <= 4 { + return Err(ConvertError::InvalidFileIndex); + } + files[file as usize] + }; + program.row().line = match from_row.line() { + Some(line) => line.get(), + None => 0, + }; + program.row().column = match from_row.column() { + read::ColumnType::LeftEdge => 0, + read::ColumnType::Column(val) => val.get(), + }; + program.row().discriminator = from_row.discriminator(); + program.row().is_statement = from_row.is_stmt(); + program.row().basic_block = from_row.basic_block(); + program.row().prologue_end = from_row.prologue_end(); + program.row().epilogue_begin = from_row.epilogue_begin(); + program.row().isa = from_row.isa(); + program.generate_row(); + } + from_row.reset(from_program.header()); + } + } + }; + } + Ok((program, files)) + } + } + + impl LineString { + fn from<R: Reader<Offset = usize>>( + from_attr: read::AttributeValue<R>, + dwarf: &read::Dwarf<R>, + line_strings: &mut write::LineStringTable, + strings: &mut write::StringTable, + ) -> ConvertResult<LineString> { + Ok(match from_attr { + read::AttributeValue::String(r) => LineString::String(r.to_slice()?.to_vec()), + read::AttributeValue::DebugStrRef(offset) => { + let r = dwarf.debug_str.get_str(offset)?; + let id = strings.add(r.to_slice()?); + LineString::StringRef(id) + } + read::AttributeValue::DebugLineStrRef(offset) => { + let r = dwarf.debug_line_str.get_str(offset)?; + let id = line_strings.add(r.to_slice()?); + LineString::LineStringRef(id) + } + _ => return Err(ConvertError::UnsupportedLineStringForm), + }) + } + } +} + +#[cfg(test)] +#[cfg(feature = "read")] +mod tests { + use super::*; + use crate::read; + use crate::write::{DebugLineStr, DebugStr, EndianVec, StringTable}; + use crate::LittleEndian; + + #[test] + fn test_line_program_table() { + let dir1 = LineString::String(b"dir1".to_vec()); + let file1 = LineString::String(b"file1".to_vec()); + let dir2 = LineString::String(b"dir2".to_vec()); + let file2 = LineString::String(b"file2".to_vec()); + + let mut programs = Vec::new(); + for &version in &[2, 3, 4, 5] { + for &address_size in &[4, 8] { + for &format in &[Format::Dwarf32, Format::Dwarf64] { + let encoding = Encoding { + format, + version, + address_size, + }; + let mut program = LineProgram::new( + encoding, + LineEncoding::default(), + dir1.clone(), + file1.clone(), + None, + ); + + { + assert_eq!(&dir1, program.get_directory(program.default_directory())); + program.file_has_timestamp = true; + program.file_has_size = true; + if encoding.version >= 5 { + program.file_has_md5 = true; + } + + let dir_id = program.add_directory(dir2.clone()); + assert_eq!(&dir2, program.get_directory(dir_id)); + assert_eq!(dir_id, program.add_directory(dir2.clone())); + + let file_info = FileInfo { + timestamp: 1, + size: 2, + md5: if encoding.version >= 5 { + [3; 16] + } else { + [0; 16] + }, + }; + let file_id = program.add_file(file2.clone(), dir_id, Some(file_info)); + assert_eq!((&file2, dir_id), program.get_file(file_id)); + assert_eq!(file_info, *program.get_file_info(file_id)); + + program.get_file_info_mut(file_id).size = 3; + assert_ne!(file_info, *program.get_file_info(file_id)); + assert_eq!(file_id, program.add_file(file2.clone(), dir_id, None)); + assert_ne!(file_info, *program.get_file_info(file_id)); + assert_eq!( + file_id, + program.add_file(file2.clone(), dir_id, Some(file_info)) + ); + assert_eq!(file_info, *program.get_file_info(file_id)); + + programs.push((program, file_id, encoding)); + } + } + } + } + + let debug_line_str_offsets = DebugLineStrOffsets::none(); + let debug_str_offsets = DebugStrOffsets::none(); + let mut debug_line = DebugLine::from(EndianVec::new(LittleEndian)); + let mut debug_line_offsets = Vec::new(); + for (program, _, encoding) in &programs { + debug_line_offsets.push( + program + .write( + &mut debug_line, + *encoding, + &debug_line_str_offsets, + &debug_str_offsets, + ) + .unwrap(), + ); + } + + let read_debug_line = read::DebugLine::new(debug_line.slice(), LittleEndian); + + let convert_address = &|address| Some(Address::Constant(address)); + for ((program, file_id, encoding), offset) in programs.iter().zip(debug_line_offsets.iter()) + { + let read_program = read_debug_line + .program( + *offset, + encoding.address_size, + Some(read::EndianSlice::new(b"dir1", LittleEndian)), + Some(read::EndianSlice::new(b"file1", LittleEndian)), + ) + .unwrap(); + + let dwarf = read::Dwarf::default(); + let mut convert_line_strings = LineStringTable::default(); + let mut convert_strings = StringTable::default(); + let (convert_program, convert_files) = LineProgram::from( + read_program, + &dwarf, + &mut convert_line_strings, + &mut convert_strings, + convert_address, + ) + .unwrap(); + assert_eq!(convert_program.version(), program.version()); + assert_eq!(convert_program.address_size(), program.address_size()); + assert_eq!(convert_program.format(), program.format()); + + let convert_file_id = convert_files[file_id.raw() as usize]; + let (file, dir) = program.get_file(*file_id); + let (convert_file, convert_dir) = convert_program.get_file(convert_file_id); + assert_eq!(file, convert_file); + assert_eq!( + program.get_directory(dir), + convert_program.get_directory(convert_dir) + ); + assert_eq!( + program.get_file_info(*file_id), + convert_program.get_file_info(convert_file_id) + ); + } + } + + #[test] + fn test_line_row() { + let dir1 = &b"dir1"[..]; + let file1 = &b"file1"[..]; + let file2 = &b"file2"[..]; + let convert_address = &|address| Some(Address::Constant(address)); + + let debug_line_str_offsets = DebugLineStrOffsets::none(); + let debug_str_offsets = DebugStrOffsets::none(); + + for &version in &[2, 3, 4, 5] { + for &address_size in &[4, 8] { + for &format in &[Format::Dwarf32, Format::Dwarf64] { + let encoding = Encoding { + format, + version, + address_size, + }; + let line_base = -5; + let line_range = 14; + let neg_line_base = (-line_base) as u8; + let mut program = LineProgram::new( + encoding, + LineEncoding { + line_base, + line_range, + ..Default::default() + }, + LineString::String(dir1.to_vec()), + LineString::String(file1.to_vec()), + None, + ); + let dir_id = program.default_directory(); + program.add_file(LineString::String(file1.to_vec()), dir_id, None); + let file_id = + program.add_file(LineString::String(file2.to_vec()), dir_id, None); + + // Test sequences. + { + let mut program = program.clone(); + let address = Address::Constant(0x12); + program.begin_sequence(Some(address)); + assert_eq!( + program.instructions, + vec![LineInstruction::SetAddress(address)] + ); + } + + { + let mut program = program.clone(); + program.begin_sequence(None); + assert_eq!(program.instructions, Vec::new()); + } + + { + let mut program = program.clone(); + program.begin_sequence(None); + program.end_sequence(0x1234); + assert_eq!( + program.instructions, + vec![ + LineInstruction::AdvancePc(0x1234), + LineInstruction::EndSequence + ] + ); + } + + // Create a base program. + program.begin_sequence(None); + program.row.line = 0x1000; + program.generate_row(); + let base_row = program.row; + let base_instructions = program.instructions.clone(); + + // Create test cases. + let mut tests = Vec::new(); + + let row = base_row; + tests.push((row, vec![LineInstruction::Copy])); + + let mut row = base_row; + row.line -= u64::from(neg_line_base); + tests.push((row, vec![LineInstruction::Special(OPCODE_BASE)])); + + let mut row = base_row; + row.line += u64::from(line_range) - 1; + row.line -= u64::from(neg_line_base); + tests.push(( + row, + vec![LineInstruction::Special(OPCODE_BASE + line_range - 1)], + )); + + let mut row = base_row; + row.line += u64::from(line_range); + row.line -= u64::from(neg_line_base); + tests.push(( + row, + vec![ + LineInstruction::AdvanceLine(i64::from(line_range - neg_line_base)), + LineInstruction::Copy, + ], + )); + + let mut row = base_row; + row.address_offset = 1; + row.line -= u64::from(neg_line_base); + tests.push(( + row, + vec![LineInstruction::Special(OPCODE_BASE + line_range)], + )); + + let op_range = (255 - OPCODE_BASE) / line_range; + let mut row = base_row; + row.address_offset = u64::from(op_range); + row.line -= u64::from(neg_line_base); + tests.push(( + row, + vec![LineInstruction::Special( + OPCODE_BASE + op_range * line_range, + )], + )); + + let mut row = base_row; + row.address_offset = u64::from(op_range); + row.line += u64::from(255 - OPCODE_BASE - op_range * line_range); + row.line -= u64::from(neg_line_base); + tests.push((row, vec![LineInstruction::Special(255)])); + + let mut row = base_row; + row.address_offset = u64::from(op_range); + row.line += u64::from(255 - OPCODE_BASE - op_range * line_range) + 1; + row.line -= u64::from(neg_line_base); + tests.push(( + row, + vec![LineInstruction::ConstAddPc, LineInstruction::Copy], + )); + + let mut row = base_row; + row.address_offset = u64::from(op_range); + row.line += u64::from(255 - OPCODE_BASE - op_range * line_range) + 2; + row.line -= u64::from(neg_line_base); + tests.push(( + row, + vec![ + LineInstruction::ConstAddPc, + LineInstruction::Special(OPCODE_BASE + 6), + ], + )); + + let mut row = base_row; + row.address_offset = u64::from(op_range) * 2; + row.line += u64::from(255 - OPCODE_BASE - op_range * line_range); + row.line -= u64::from(neg_line_base); + tests.push(( + row, + vec![LineInstruction::ConstAddPc, LineInstruction::Special(255)], + )); + + let mut row = base_row; + row.address_offset = u64::from(op_range) * 2; + row.line += u64::from(255 - OPCODE_BASE - op_range * line_range) + 1; + row.line -= u64::from(neg_line_base); + tests.push(( + row, + vec![ + LineInstruction::AdvancePc(row.address_offset), + LineInstruction::Copy, + ], + )); + + let mut row = base_row; + row.address_offset = u64::from(op_range) * 2; + row.line += u64::from(255 - OPCODE_BASE - op_range * line_range) + 2; + row.line -= u64::from(neg_line_base); + tests.push(( + row, + vec![ + LineInstruction::AdvancePc(row.address_offset), + LineInstruction::Special(OPCODE_BASE + 6), + ], + )); + + let mut row = base_row; + row.address_offset = 0x1234; + tests.push(( + row, + vec![LineInstruction::AdvancePc(0x1234), LineInstruction::Copy], + )); + + let mut row = base_row; + row.line += 0x1234; + tests.push(( + row, + vec![LineInstruction::AdvanceLine(0x1234), LineInstruction::Copy], + )); + + let mut row = base_row; + row.file = file_id; + tests.push(( + row, + vec![LineInstruction::SetFile(file_id), LineInstruction::Copy], + )); + + let mut row = base_row; + row.column = 0x1234; + tests.push(( + row, + vec![LineInstruction::SetColumn(0x1234), LineInstruction::Copy], + )); + + let mut row = base_row; + row.discriminator = 0x1234; + tests.push(( + row, + vec![ + LineInstruction::SetDiscriminator(0x1234), + LineInstruction::Copy, + ], + )); + + let mut row = base_row; + row.is_statement = !row.is_statement; + tests.push(( + row, + vec![LineInstruction::NegateStatement, LineInstruction::Copy], + )); + + let mut row = base_row; + row.basic_block = true; + tests.push(( + row, + vec![LineInstruction::SetBasicBlock, LineInstruction::Copy], + )); + + let mut row = base_row; + row.prologue_end = true; + tests.push(( + row, + vec![LineInstruction::SetPrologueEnd, LineInstruction::Copy], + )); + + let mut row = base_row; + row.epilogue_begin = true; + tests.push(( + row, + vec![LineInstruction::SetEpilogueBegin, LineInstruction::Copy], + )); + + let mut row = base_row; + row.isa = 0x1234; + tests.push(( + row, + vec![LineInstruction::SetIsa(0x1234), LineInstruction::Copy], + )); + + for test in tests { + // Test generate_row(). + let mut program = program.clone(); + program.row = test.0; + program.generate_row(); + assert_eq!( + &program.instructions[base_instructions.len()..], + &test.1[..] + ); + + // Test LineProgram::from(). + let mut debug_line = DebugLine::from(EndianVec::new(LittleEndian)); + let debug_line_offset = program + .write( + &mut debug_line, + encoding, + &debug_line_str_offsets, + &debug_str_offsets, + ) + .unwrap(); + + let read_debug_line = + read::DebugLine::new(debug_line.slice(), LittleEndian); + let read_program = read_debug_line + .program( + debug_line_offset, + address_size, + Some(read::EndianSlice::new(dir1, LittleEndian)), + Some(read::EndianSlice::new(file1, LittleEndian)), + ) + .unwrap(); + + let dwarf = read::Dwarf::default(); + let mut convert_line_strings = LineStringTable::default(); + let mut convert_strings = StringTable::default(); + let (convert_program, _convert_files) = LineProgram::from( + read_program, + &dwarf, + &mut convert_line_strings, + &mut convert_strings, + convert_address, + ) + .unwrap(); + assert_eq!( + &convert_program.instructions[base_instructions.len()..], + &test.1[..] + ); + } + } + } + } + } + + #[test] + fn test_line_instruction() { + let dir1 = &b"dir1"[..]; + let file1 = &b"file1"[..]; + + let debug_line_str_offsets = DebugLineStrOffsets::none(); + let debug_str_offsets = DebugStrOffsets::none(); + + for &version in &[2, 3, 4, 5] { + for &address_size in &[4, 8] { + for &format in &[Format::Dwarf32, Format::Dwarf64] { + let encoding = Encoding { + format, + version, + address_size, + }; + let mut program = LineProgram::new( + encoding, + LineEncoding::default(), + LineString::String(dir1.to_vec()), + LineString::String(file1.to_vec()), + None, + ); + let dir_id = program.default_directory(); + let file_id = + program.add_file(LineString::String(file1.to_vec()), dir_id, None); + + for &(ref inst, ref expect_inst) in &[ + ( + LineInstruction::Special(OPCODE_BASE), + read::LineInstruction::Special(OPCODE_BASE), + ), + ( + LineInstruction::Special(255), + read::LineInstruction::Special(255), + ), + (LineInstruction::Copy, read::LineInstruction::Copy), + ( + LineInstruction::AdvancePc(0x12), + read::LineInstruction::AdvancePc(0x12), + ), + ( + LineInstruction::AdvanceLine(0x12), + read::LineInstruction::AdvanceLine(0x12), + ), + ( + LineInstruction::SetFile(file_id), + read::LineInstruction::SetFile(file_id.raw()), + ), + ( + LineInstruction::SetColumn(0x12), + read::LineInstruction::SetColumn(0x12), + ), + ( + LineInstruction::NegateStatement, + read::LineInstruction::NegateStatement, + ), + ( + LineInstruction::SetBasicBlock, + read::LineInstruction::SetBasicBlock, + ), + ( + LineInstruction::ConstAddPc, + read::LineInstruction::ConstAddPc, + ), + ( + LineInstruction::SetPrologueEnd, + read::LineInstruction::SetPrologueEnd, + ), + ( + LineInstruction::SetEpilogueBegin, + read::LineInstruction::SetEpilogueBegin, + ), + ( + LineInstruction::SetIsa(0x12), + read::LineInstruction::SetIsa(0x12), + ), + ( + LineInstruction::EndSequence, + read::LineInstruction::EndSequence, + ), + ( + LineInstruction::SetAddress(Address::Constant(0x12)), + read::LineInstruction::SetAddress(0x12), + ), + ( + LineInstruction::SetDiscriminator(0x12), + read::LineInstruction::SetDiscriminator(0x12), + ), + ][..] + { + let mut program = program.clone(); + program.instructions.push(*inst); + + let mut debug_line = DebugLine::from(EndianVec::new(LittleEndian)); + let debug_line_offset = program + .write( + &mut debug_line, + encoding, + &debug_line_str_offsets, + &debug_str_offsets, + ) + .unwrap(); + + let read_debug_line = + read::DebugLine::new(debug_line.slice(), LittleEndian); + let read_program = read_debug_line + .program( + debug_line_offset, + address_size, + Some(read::EndianSlice::new(dir1, LittleEndian)), + Some(read::EndianSlice::new(file1, LittleEndian)), + ) + .unwrap(); + let read_header = read_program.header(); + let mut read_insts = read_header.instructions(); + assert_eq!( + *expect_inst, + read_insts.next_instruction(read_header).unwrap().unwrap() + ); + assert_eq!(None, read_insts.next_instruction(read_header).unwrap()); + } + } + } + } + } + + // Test that the address/line advance is correct. We don't test for optimality. + #[test] + fn test_advance() { + let encoding = Encoding { + format: Format::Dwarf32, + version: 4, + address_size: 8, + }; + + let dir1 = &b"dir1"[..]; + let file1 = &b"file1"[..]; + + let addresses = 0..50; + let lines = -10..25i64; + + let debug_line_str_offsets = DebugLineStrOffsets::none(); + let debug_str_offsets = DebugStrOffsets::none(); + + for minimum_instruction_length in vec![1, 4] { + for maximum_operations_per_instruction in vec![1, 3] { + for line_base in vec![-5, 0] { + for line_range in vec![10, 20] { + let line_encoding = LineEncoding { + minimum_instruction_length, + maximum_operations_per_instruction, + line_base, + line_range, + default_is_stmt: true, + }; + let mut program = LineProgram::new( + encoding, + line_encoding, + LineString::String(dir1.to_vec()), + LineString::String(file1.to_vec()), + None, + ); + for address_advance in addresses.clone() { + program.begin_sequence(Some(Address::Constant(0x1000))); + program.row().line = 0x10000; + program.generate_row(); + for line_advance in lines.clone() { + { + let row = program.row(); + row.address_offset += + address_advance * u64::from(minimum_instruction_length); + row.line = row.line.wrapping_add(line_advance as u64); + } + program.generate_row(); + } + let address_offset = program.row().address_offset + + u64::from(minimum_instruction_length); + program.end_sequence(address_offset); + } + + let mut debug_line = DebugLine::from(EndianVec::new(LittleEndian)); + let debug_line_offset = program + .write( + &mut debug_line, + encoding, + &debug_line_str_offsets, + &debug_str_offsets, + ) + .unwrap(); + + let read_debug_line = + read::DebugLine::new(debug_line.slice(), LittleEndian); + let read_program = read_debug_line + .program( + debug_line_offset, + 8, + Some(read::EndianSlice::new(dir1, LittleEndian)), + Some(read::EndianSlice::new(file1, LittleEndian)), + ) + .unwrap(); + + let mut rows = read_program.rows(); + for address_advance in addresses.clone() { + let mut address; + let mut line; + { + let row = rows.next_row().unwrap().unwrap().1; + address = row.address(); + line = row.line().unwrap().get(); + } + assert_eq!(address, 0x1000); + assert_eq!(line, 0x10000); + for line_advance in lines.clone() { + let row = rows.next_row().unwrap().unwrap().1; + assert_eq!( + row.address() - address, + address_advance * u64::from(minimum_instruction_length) + ); + assert_eq!( + (row.line().unwrap().get() as i64) - (line as i64), + line_advance + ); + address = row.address(); + line = row.line().unwrap().get(); + } + let row = rows.next_row().unwrap().unwrap().1; + assert!(row.end_sequence()); + } + } + } + } + } + } + + #[test] + fn test_line_string() { + let version = 5; + + let file = b"file1"; + + let mut strings = StringTable::default(); + let string_id = strings.add("file2"); + let mut debug_str = DebugStr::from(EndianVec::new(LittleEndian)); + let debug_str_offsets = strings.write(&mut debug_str).unwrap(); + + let mut line_strings = LineStringTable::default(); + let line_string_id = line_strings.add("file3"); + let mut debug_line_str = DebugLineStr::from(EndianVec::new(LittleEndian)); + let debug_line_str_offsets = line_strings.write(&mut debug_line_str).unwrap(); + + for &address_size in &[4, 8] { + for &format in &[Format::Dwarf32, Format::Dwarf64] { + let encoding = Encoding { + format, + version, + address_size, + }; + + for (file, expect_file) in vec![ + ( + LineString::String(file.to_vec()), + read::AttributeValue::String(read::EndianSlice::new(file, LittleEndian)), + ), + ( + LineString::StringRef(string_id), + read::AttributeValue::DebugStrRef(debug_str_offsets.get(string_id)), + ), + ( + LineString::LineStringRef(line_string_id), + read::AttributeValue::DebugLineStrRef( + debug_line_str_offsets.get(line_string_id), + ), + ), + ] { + let program = LineProgram::new( + encoding, + LineEncoding::default(), + LineString::String(b"dir".to_vec()), + file, + None, + ); + + let mut debug_line = DebugLine::from(EndianVec::new(LittleEndian)); + let debug_line_offset = program + .write( + &mut debug_line, + encoding, + &debug_line_str_offsets, + &debug_str_offsets, + ) + .unwrap(); + + let read_debug_line = read::DebugLine::new(debug_line.slice(), LittleEndian); + let read_program = read_debug_line + .program(debug_line_offset, address_size, None, None) + .unwrap(); + let read_header = read_program.header(); + assert_eq!(read_header.file(0).unwrap().path_name(), expect_file); + } + } + } + } + + #[test] + fn test_missing_comp_dir() { + let debug_line_str_offsets = DebugLineStrOffsets::none(); + let debug_str_offsets = DebugStrOffsets::none(); + + for &version in &[2, 3, 4, 5] { + for &address_size in &[4, 8] { + for &format in &[Format::Dwarf32, Format::Dwarf64] { + let encoding = Encoding { + format, + version, + address_size, + }; + let program = LineProgram::new( + encoding, + LineEncoding::default(), + LineString::String(Vec::new()), + LineString::String(Vec::new()), + None, + ); + + let mut debug_line = DebugLine::from(EndianVec::new(LittleEndian)); + let debug_line_offset = program + .write( + &mut debug_line, + encoding, + &debug_line_str_offsets, + &debug_str_offsets, + ) + .unwrap(); + + let read_debug_line = read::DebugLine::new(debug_line.slice(), LittleEndian); + let read_program = read_debug_line + .program( + debug_line_offset, + address_size, + // Testing missing comp_dir/comp_name. + None, + None, + ) + .unwrap(); + + let dwarf = read::Dwarf::default(); + let mut convert_line_strings = LineStringTable::default(); + let mut convert_strings = StringTable::default(); + let convert_address = &|address| Some(Address::Constant(address)); + LineProgram::from( + read_program, + &dwarf, + &mut convert_line_strings, + &mut convert_strings, + convert_address, + ) + .unwrap(); + } + } + } + } +} diff --git a/vendor/gimli/src/write/loc.rs b/vendor/gimli/src/write/loc.rs new file mode 100644 index 0000000..6dfe45a --- /dev/null +++ b/vendor/gimli/src/write/loc.rs @@ -0,0 +1,550 @@ +use alloc::vec::Vec; +use indexmap::IndexSet; +use std::ops::{Deref, DerefMut}; + +use crate::common::{Encoding, LocationListsOffset, SectionId}; +use crate::write::{ + Address, BaseId, DebugInfoReference, Error, Expression, Result, Section, Sections, UnitOffsets, + Writer, +}; + +define_section!( + DebugLoc, + LocationListsOffset, + "A writable `.debug_loc` section." +); +define_section!( + DebugLocLists, + LocationListsOffset, + "A writable `.debug_loclists` section." +); + +define_offsets!( + LocationListOffsets: LocationListId => LocationListsOffset, + "The section offsets of a series of location lists within the `.debug_loc` or `.debug_loclists` sections." +); + +define_id!( + LocationListId, + "An identifier for a location list in a `LocationListTable`." +); + +/// A table of location lists that will be stored in a `.debug_loc` or `.debug_loclists` section. +#[derive(Debug, Default)] +pub struct LocationListTable { + base_id: BaseId, + locations: IndexSet<LocationList>, +} + +impl LocationListTable { + /// Add a location list to the table. + pub fn add(&mut self, loc_list: LocationList) -> LocationListId { + let (index, _) = self.locations.insert_full(loc_list); + LocationListId::new(self.base_id, index) + } + + /// Write the location list table to the appropriate section for the given DWARF version. + pub(crate) fn write<W: Writer>( + &self, + sections: &mut Sections<W>, + encoding: Encoding, + unit_offsets: Option<&UnitOffsets>, + ) -> Result<LocationListOffsets> { + if self.locations.is_empty() { + return Ok(LocationListOffsets::none()); + } + + match encoding.version { + 2..=4 => self.write_loc( + &mut sections.debug_loc, + &mut sections.debug_loc_refs, + encoding, + unit_offsets, + ), + 5 => self.write_loclists( + &mut sections.debug_loclists, + &mut sections.debug_loclists_refs, + encoding, + unit_offsets, + ), + _ => Err(Error::UnsupportedVersion(encoding.version)), + } + } + + /// Write the location list table to the `.debug_loc` section. + fn write_loc<W: Writer>( + &self, + w: &mut DebugLoc<W>, + refs: &mut Vec<DebugInfoReference>, + encoding: Encoding, + unit_offsets: Option<&UnitOffsets>, + ) -> Result<LocationListOffsets> { + let address_size = encoding.address_size; + let mut offsets = Vec::new(); + for loc_list in self.locations.iter() { + offsets.push(w.offset()); + for loc in &loc_list.0 { + // Note that we must ensure none of the ranges have both begin == 0 and end == 0. + // We do this by ensuring that begin != end, which is a bit more restrictive + // than required, but still seems reasonable. + match *loc { + Location::BaseAddress { address } => { + let marker = !0 >> (64 - address_size * 8); + w.write_udata(marker, address_size)?; + w.write_address(address, address_size)?; + } + Location::OffsetPair { + begin, + end, + ref data, + } => { + if begin == end { + return Err(Error::InvalidRange); + } + w.write_udata(begin, address_size)?; + w.write_udata(end, address_size)?; + write_expression(&mut w.0, refs, encoding, unit_offsets, data)?; + } + Location::StartEnd { + begin, + end, + ref data, + } => { + if begin == end { + return Err(Error::InvalidRange); + } + w.write_address(begin, address_size)?; + w.write_address(end, address_size)?; + write_expression(&mut w.0, refs, encoding, unit_offsets, data)?; + } + Location::StartLength { + begin, + length, + ref data, + } => { + let end = match begin { + Address::Constant(begin) => Address::Constant(begin + length), + Address::Symbol { symbol, addend } => Address::Symbol { + symbol, + addend: addend + length as i64, + }, + }; + if begin == end { + return Err(Error::InvalidRange); + } + w.write_address(begin, address_size)?; + w.write_address(end, address_size)?; + write_expression(&mut w.0, refs, encoding, unit_offsets, data)?; + } + Location::DefaultLocation { .. } => { + return Err(Error::InvalidRange); + } + } + } + w.write_udata(0, address_size)?; + w.write_udata(0, address_size)?; + } + Ok(LocationListOffsets { + base_id: self.base_id, + offsets, + }) + } + + /// Write the location list table to the `.debug_loclists` section. + fn write_loclists<W: Writer>( + &self, + w: &mut DebugLocLists<W>, + refs: &mut Vec<DebugInfoReference>, + encoding: Encoding, + unit_offsets: Option<&UnitOffsets>, + ) -> Result<LocationListOffsets> { + let mut offsets = Vec::new(); + + if encoding.version != 5 { + return Err(Error::NeedVersion(5)); + } + + let length_offset = w.write_initial_length(encoding.format)?; + let length_base = w.len(); + + w.write_u16(encoding.version)?; + w.write_u8(encoding.address_size)?; + w.write_u8(0)?; // segment_selector_size + w.write_u32(0)?; // offset_entry_count (when set to zero DW_FORM_rnglistx can't be used, see section 7.28) + // FIXME implement DW_FORM_rnglistx writing and implement the offset entry list + + for loc_list in self.locations.iter() { + offsets.push(w.offset()); + for loc in &loc_list.0 { + match *loc { + Location::BaseAddress { address } => { + w.write_u8(crate::constants::DW_LLE_base_address.0)?; + w.write_address(address, encoding.address_size)?; + } + Location::OffsetPair { + begin, + end, + ref data, + } => { + w.write_u8(crate::constants::DW_LLE_offset_pair.0)?; + w.write_uleb128(begin)?; + w.write_uleb128(end)?; + write_expression(&mut w.0, refs, encoding, unit_offsets, data)?; + } + Location::StartEnd { + begin, + end, + ref data, + } => { + w.write_u8(crate::constants::DW_LLE_start_end.0)?; + w.write_address(begin, encoding.address_size)?; + w.write_address(end, encoding.address_size)?; + write_expression(&mut w.0, refs, encoding, unit_offsets, data)?; + } + Location::StartLength { + begin, + length, + ref data, + } => { + w.write_u8(crate::constants::DW_LLE_start_length.0)?; + w.write_address(begin, encoding.address_size)?; + w.write_uleb128(length)?; + write_expression(&mut w.0, refs, encoding, unit_offsets, data)?; + } + Location::DefaultLocation { ref data } => { + w.write_u8(crate::constants::DW_LLE_default_location.0)?; + write_expression(&mut w.0, refs, encoding, unit_offsets, data)?; + } + } + } + + w.write_u8(crate::constants::DW_LLE_end_of_list.0)?; + } + + let length = (w.len() - length_base) as u64; + w.write_initial_length_at(length_offset, length, encoding.format)?; + + Ok(LocationListOffsets { + base_id: self.base_id, + offsets, + }) + } +} + +/// A locations list that will be stored in a `.debug_loc` or `.debug_loclists` section. +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub struct LocationList(pub Vec<Location>); + +/// A single location. +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub enum Location { + /// DW_LLE_base_address + BaseAddress { + /// Base address. + address: Address, + }, + /// DW_LLE_offset_pair + OffsetPair { + /// Start of range relative to base address. + begin: u64, + /// End of range relative to base address. + end: u64, + /// Location description. + data: Expression, + }, + /// DW_LLE_start_end + StartEnd { + /// Start of range. + begin: Address, + /// End of range. + end: Address, + /// Location description. + data: Expression, + }, + /// DW_LLE_start_length + StartLength { + /// Start of range. + begin: Address, + /// Length of range. + length: u64, + /// Location description. + data: Expression, + }, + /// DW_LLE_default_location + DefaultLocation { + /// Location description. + data: Expression, + }, +} + +fn write_expression<W: Writer>( + w: &mut W, + refs: &mut Vec<DebugInfoReference>, + encoding: Encoding, + unit_offsets: Option<&UnitOffsets>, + val: &Expression, +) -> Result<()> { + let size = val.size(encoding, unit_offsets) as u64; + if encoding.version <= 4 { + w.write_udata(size, 2)?; + } else { + w.write_uleb128(size)?; + } + val.write(w, Some(refs), encoding, unit_offsets)?; + Ok(()) +} + +#[cfg(feature = "read")] +mod convert { + use super::*; + + use crate::read::{self, Reader}; + use crate::write::{ConvertError, ConvertResult, ConvertUnitContext}; + + impl LocationList { + /// Create a location list by reading the data from the give location list iter. + pub(crate) fn from<R: Reader<Offset = usize>>( + mut from: read::RawLocListIter<R>, + context: &ConvertUnitContext<R>, + ) -> ConvertResult<Self> { + let mut have_base_address = context.base_address != Address::Constant(0); + let convert_address = + |x| (context.convert_address)(x).ok_or(ConvertError::InvalidAddress); + let convert_expression = |x| { + Expression::from( + x, + context.unit.encoding(), + Some(context.dwarf), + Some(context.unit), + Some(context.entry_ids), + context.convert_address, + ) + }; + let mut loc_list = Vec::new(); + while let Some(from_loc) = from.next()? { + let loc = match from_loc { + read::RawLocListEntry::AddressOrOffsetPair { begin, end, data } => { + // These were parsed as addresses, even if they are offsets. + let begin = convert_address(begin)?; + let end = convert_address(end)?; + let data = convert_expression(data)?; + match (begin, end) { + (Address::Constant(begin_offset), Address::Constant(end_offset)) => { + if have_base_address { + Location::OffsetPair { + begin: begin_offset, + end: end_offset, + data, + } + } else { + Location::StartEnd { begin, end, data } + } + } + _ => { + if have_base_address { + // At least one of begin/end is an address, but we also have + // a base address. Adding addresses is undefined. + return Err(ConvertError::InvalidRangeRelativeAddress); + } + Location::StartEnd { begin, end, data } + } + } + } + read::RawLocListEntry::BaseAddress { addr } => { + have_base_address = true; + let address = convert_address(addr)?; + Location::BaseAddress { address } + } + read::RawLocListEntry::BaseAddressx { addr } => { + have_base_address = true; + let address = convert_address(context.dwarf.address(context.unit, addr)?)?; + Location::BaseAddress { address } + } + read::RawLocListEntry::StartxEndx { begin, end, data } => { + let begin = convert_address(context.dwarf.address(context.unit, begin)?)?; + let end = convert_address(context.dwarf.address(context.unit, end)?)?; + let data = convert_expression(data)?; + Location::StartEnd { begin, end, data } + } + read::RawLocListEntry::StartxLength { + begin, + length, + data, + } => { + let begin = convert_address(context.dwarf.address(context.unit, begin)?)?; + let data = convert_expression(data)?; + Location::StartLength { + begin, + length, + data, + } + } + read::RawLocListEntry::OffsetPair { begin, end, data } => { + let data = convert_expression(data)?; + Location::OffsetPair { begin, end, data } + } + read::RawLocListEntry::StartEnd { begin, end, data } => { + let begin = convert_address(begin)?; + let end = convert_address(end)?; + let data = convert_expression(data)?; + Location::StartEnd { begin, end, data } + } + read::RawLocListEntry::StartLength { + begin, + length, + data, + } => { + let begin = convert_address(begin)?; + let data = convert_expression(data)?; + Location::StartLength { + begin, + length, + data, + } + } + read::RawLocListEntry::DefaultLocation { data } => { + let data = convert_expression(data)?; + Location::DefaultLocation { data } + } + }; + // In some cases, existing data may contain begin == end, filtering + // these out. + match loc { + Location::StartLength { length, .. } if length == 0 => continue, + Location::StartEnd { begin, end, .. } if begin == end => continue, + Location::OffsetPair { begin, end, .. } if begin == end => continue, + _ => (), + } + loc_list.push(loc); + } + Ok(LocationList(loc_list)) + } + } +} + +#[cfg(test)] +#[cfg(feature = "read")] +mod tests { + use super::*; + use crate::common::{ + DebugAbbrevOffset, DebugAddrBase, DebugInfoOffset, DebugLocListsBase, DebugRngListsBase, + DebugStrOffsetsBase, Format, + }; + use crate::read; + use crate::write::{ + ConvertUnitContext, EndianVec, LineStringTable, RangeListTable, StringTable, + }; + use crate::LittleEndian; + use std::collections::HashMap; + use std::sync::Arc; + + #[test] + fn test_loc_list() { + let mut line_strings = LineStringTable::default(); + let mut strings = StringTable::default(); + let mut expression = Expression::new(); + expression.op_constu(0); + + for &version in &[2, 3, 4, 5] { + for &address_size in &[4, 8] { + for &format in &[Format::Dwarf32, Format::Dwarf64] { + let encoding = Encoding { + format, + version, + address_size, + }; + + let mut loc_list = LocationList(vec![ + Location::StartLength { + begin: Address::Constant(6666), + length: 7777, + data: expression.clone(), + }, + Location::StartEnd { + begin: Address::Constant(4444), + end: Address::Constant(5555), + data: expression.clone(), + }, + Location::BaseAddress { + address: Address::Constant(1111), + }, + Location::OffsetPair { + begin: 2222, + end: 3333, + data: expression.clone(), + }, + ]); + if version >= 5 { + loc_list.0.push(Location::DefaultLocation { + data: expression.clone(), + }); + } + + let mut locations = LocationListTable::default(); + let loc_list_id = locations.add(loc_list.clone()); + + let mut sections = Sections::new(EndianVec::new(LittleEndian)); + let loc_list_offsets = locations.write(&mut sections, encoding, None).unwrap(); + assert!(sections.debug_loc_refs.is_empty()); + assert!(sections.debug_loclists_refs.is_empty()); + + let read_debug_loc = + read::DebugLoc::new(sections.debug_loc.slice(), LittleEndian); + let read_debug_loclists = + read::DebugLocLists::new(sections.debug_loclists.slice(), LittleEndian); + let read_loc = read::LocationLists::new(read_debug_loc, read_debug_loclists); + let offset = loc_list_offsets.get(loc_list_id); + let read_loc_list = read_loc.raw_locations(offset, encoding).unwrap(); + + let dwarf = read::Dwarf { + locations: read_loc, + ..Default::default() + }; + let unit = read::Unit { + header: read::UnitHeader::new( + encoding, + 0, + read::UnitType::Compilation, + DebugAbbrevOffset(0), + DebugInfoOffset(0).into(), + read::EndianSlice::default(), + ), + abbreviations: Arc::new(read::Abbreviations::default()), + name: None, + comp_dir: None, + low_pc: 0, + str_offsets_base: DebugStrOffsetsBase(0), + addr_base: DebugAddrBase(0), + loclists_base: DebugLocListsBase(0), + rnglists_base: DebugRngListsBase(0), + line_program: None, + dwo_id: None, + }; + let context = ConvertUnitContext { + dwarf: &dwarf, + unit: &unit, + line_strings: &mut line_strings, + strings: &mut strings, + ranges: &mut RangeListTable::default(), + locations: &mut locations, + convert_address: &|address| Some(Address::Constant(address)), + base_address: Address::Constant(0), + line_program_offset: None, + line_program_files: Vec::new(), + entry_ids: &HashMap::new(), + }; + let convert_loc_list = LocationList::from(read_loc_list, &context).unwrap(); + + if version <= 4 { + loc_list.0[0] = Location::StartEnd { + begin: Address::Constant(6666), + end: Address::Constant(6666 + 7777), + data: expression.clone(), + }; + } + assert_eq!(loc_list, convert_loc_list); + } + } + } + } +} diff --git a/vendor/gimli/src/write/mod.rs b/vendor/gimli/src/write/mod.rs new file mode 100644 index 0000000..47ba631 --- /dev/null +++ b/vendor/gimli/src/write/mod.rs @@ -0,0 +1,425 @@ +//! Write DWARF debugging information. +//! +//! ## API Structure +//! +//! This module works by building up a representation of the debugging information +//! in memory, and then writing it all at once. It supports two major use cases: +//! +//! * Use the [`DwarfUnit`](./struct.DwarfUnit.html) type when writing DWARF +//! for a single compilation unit. +//! +//! * Use the [`Dwarf`](./struct.Dwarf.html) type when writing DWARF for multiple +//! compilation units. +//! +//! The module also supports reading in DWARF debugging information and writing it out +//! again, possibly after modifying it. Create a [`read::Dwarf`](../read/struct.Dwarf.html) +//! instance, and then use [`Dwarf::from`](./struct.Dwarf.html#method.from) to convert +//! it to a writable instance. +//! +//! ## Example Usage +//! +//! Write a compilation unit containing only the top level DIE. +//! +//! ```rust +//! use gimli::write::{ +//! Address, AttributeValue, DwarfUnit, EndianVec, Error, Range, RangeList, Sections, +//! }; +//! +//! fn example() -> Result<(), Error> { +//! // Choose the encoding parameters. +//! let encoding = gimli::Encoding { +//! format: gimli::Format::Dwarf32, +//! version: 5, +//! address_size: 8, +//! }; +//! // Create a container for a single compilation unit. +//! let mut dwarf = DwarfUnit::new(encoding); +//! // Set a range attribute on the root DIE. +//! let range_list = RangeList(vec![Range::StartLength { +//! begin: Address::Constant(0x100), +//! length: 42, +//! }]); +//! let range_list_id = dwarf.unit.ranges.add(range_list); +//! let root = dwarf.unit.root(); +//! dwarf.unit.get_mut(root).set( +//! gimli::DW_AT_ranges, +//! AttributeValue::RangeListRef(range_list_id), +//! ); +//! // Create a `Vec` for each DWARF section. +//! let mut sections = Sections::new(EndianVec::new(gimli::LittleEndian)); +//! // Finally, write the DWARF data to the sections. +//! dwarf.write(&mut sections)?; +//! sections.for_each(|id, data| { +//! // Here you can add the data to the output object file. +//! Ok(()) +//! }) +//! } +//! # fn main() { +//! # example().unwrap(); +//! # } + +use std::error; +use std::fmt; +use std::result; + +use crate::constants; + +mod endian_vec; +pub use self::endian_vec::*; + +mod writer; +pub use self::writer::*; + +#[macro_use] +mod section; +pub use self::section::*; + +macro_rules! define_id { + ($name:ident, $docs:expr) => { + #[doc=$docs] + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] + pub struct $name { + base_id: BaseId, + index: usize, + } + + impl $name { + #[inline] + fn new(base_id: BaseId, index: usize) -> Self { + $name { base_id, index } + } + } + }; +} + +macro_rules! define_offsets { + ($offsets:ident: $id:ident => $offset:ident, $off_doc:expr) => { + #[doc=$off_doc] + #[derive(Debug)] + pub struct $offsets { + base_id: BaseId, + // We know ids start at 0. + offsets: Vec<$offset>, + } + + impl $offsets { + /// Return an empty list of offsets. + #[inline] + pub fn none() -> Self { + $offsets { + base_id: BaseId::default(), + offsets: Vec::new(), + } + } + + /// Get the offset + /// + /// # Panics + /// + /// Panics if `id` is invalid. + #[inline] + pub fn get(&self, id: $id) -> $offset { + debug_assert_eq!(self.base_id, id.base_id); + self.offsets[id.index] + } + + /// Return the number of offsets. + #[inline] + pub fn count(&self) -> usize { + self.offsets.len() + } + } + }; +} + +mod abbrev; +pub use self::abbrev::*; + +mod cfi; +pub use self::cfi::*; + +mod dwarf; +pub use self::dwarf::*; + +mod line; +pub use self::line::*; + +mod loc; +pub use self::loc::*; + +mod op; +pub use self::op::*; + +mod range; +pub use self::range::*; + +mod str; +pub use self::str::*; + +mod unit; +pub use self::unit::*; + +/// An error that occurred when writing. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum Error { + /// The given offset is out of bounds. + OffsetOutOfBounds, + /// The given length is out of bounds. + LengthOutOfBounds, + /// The attribute value is an invalid for writing. + InvalidAttributeValue, + /// The value is too large for the encoding form. + ValueTooLarge, + /// Unsupported word size. + UnsupportedWordSize(u8), + /// Unsupported DWARF version. + UnsupportedVersion(u16), + /// The unit length is too large for the requested DWARF format. + InitialLengthOverflow, + /// The address is invalid. + InvalidAddress, + /// The reference is invalid. + InvalidReference, + /// A requested feature requires a different DWARF version. + NeedVersion(u16), + /// Strings in line number program have mismatched forms. + LineStringFormMismatch, + /// The range is empty or otherwise invalid. + InvalidRange, + /// The line number program encoding is incompatible with the unit encoding. + IncompatibleLineProgramEncoding, + /// Could not encode code offset for a frame instruction. + InvalidFrameCodeOffset(u32), + /// Could not encode data offset for a frame instruction. + InvalidFrameDataOffset(i32), + /// Unsupported eh_frame pointer encoding. + UnsupportedPointerEncoding(constants::DwEhPe), + /// Unsupported reference in CFI expression. + UnsupportedCfiExpressionReference, + /// Unsupported forward reference in expression. + UnsupportedExpressionForwardReference, +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> result::Result<(), fmt::Error> { + match *self { + Error::OffsetOutOfBounds => write!(f, "The given offset is out of bounds."), + Error::LengthOutOfBounds => write!(f, "The given length is out of bounds."), + Error::InvalidAttributeValue => { + write!(f, "The attribute value is an invalid for writing.") + } + Error::ValueTooLarge => write!(f, "The value is too large for the encoding form."), + Error::UnsupportedWordSize(size) => write!(f, "Unsupported word size: {}", size), + Error::UnsupportedVersion(version) => { + write!(f, "Unsupported DWARF version: {}", version) + } + Error::InitialLengthOverflow => write!( + f, + "The unit length is too large for the requested DWARF format." + ), + Error::InvalidAddress => write!(f, "The address is invalid."), + Error::InvalidReference => write!(f, "The reference is invalid."), + Error::NeedVersion(version) => write!( + f, + "A requested feature requires a DWARF version {}.", + version + ), + Error::LineStringFormMismatch => { + write!(f, "Strings in line number program have mismatched forms.") + } + Error::InvalidRange => write!(f, "The range is empty or otherwise invalid."), + Error::IncompatibleLineProgramEncoding => write!( + f, + "The line number program encoding is incompatible with the unit encoding." + ), + Error::InvalidFrameCodeOffset(offset) => write!( + f, + "Could not encode code offset ({}) for a frame instruction.", + offset, + ), + Error::InvalidFrameDataOffset(offset) => write!( + f, + "Could not encode data offset ({}) for a frame instruction.", + offset, + ), + Error::UnsupportedPointerEncoding(eh_pe) => { + write!(f, "Unsupported eh_frame pointer encoding ({}).", eh_pe) + } + Error::UnsupportedCfiExpressionReference => { + write!(f, "Unsupported reference in CFI expression.") + } + Error::UnsupportedExpressionForwardReference => { + write!(f, "Unsupported forward reference in expression.") + } + } + } +} + +impl error::Error for Error {} + +/// The result of a write. +pub type Result<T> = result::Result<T, Error>; + +/// An address. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum Address { + /// A fixed address that does not require relocation. + Constant(u64), + /// An address that is relative to a symbol which may be relocated. + Symbol { + /// The symbol that the address is relative to. + /// + /// The meaning of this value is decided by the writer, but + /// will typically be an index into a symbol table. + symbol: usize, + /// The offset of the address relative to the symbol. + /// + /// This will typically be used as the addend in a relocation. + addend: i64, + }, +} + +/// A reference to a `.debug_info` entry. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum Reference { + /// An external symbol. + /// + /// The meaning of this value is decided by the writer, but + /// will typically be an index into a symbol table. + Symbol(usize), + /// An entry in the same section. + /// + /// This only supports references in units that are emitted together. + Entry(UnitId, UnitEntryId), +} + +// This type is only used in debug assertions. +#[cfg(not(debug_assertions))] +type BaseId = (); + +#[cfg(debug_assertions)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +struct BaseId(usize); + +#[cfg(debug_assertions)] +impl Default for BaseId { + fn default() -> Self { + use std::sync::atomic; + static BASE_ID: atomic::AtomicUsize = atomic::AtomicUsize::new(0); + BaseId(BASE_ID.fetch_add(1, atomic::Ordering::Relaxed)) + } +} + +#[cfg(feature = "read")] +mod convert { + use super::*; + use crate::read; + + pub(crate) use super::unit::convert::*; + + /// An error that occurred when converting a read value into a write value. + #[derive(Debug, Clone, Copy, PartialEq, Eq)] + pub enum ConvertError { + /// An error occurred when reading. + Read(read::Error), + /// Writing of this attribute value is not implemented yet. + UnsupportedAttributeValue, + /// This attribute value is an invalid name/form combination. + InvalidAttributeValue, + /// A `.debug_info` reference does not refer to a valid entry. + InvalidDebugInfoOffset, + /// An address could not be converted. + InvalidAddress, + /// Writing this line number instruction is not implemented yet. + UnsupportedLineInstruction, + /// Writing this form of line string is not implemented yet. + UnsupportedLineStringForm, + /// A `.debug_line` file index is invalid. + InvalidFileIndex, + /// A `.debug_line` directory index is invalid. + InvalidDirectoryIndex, + /// A `.debug_line` line base is invalid. + InvalidLineBase, + /// A `.debug_line` reference is invalid. + InvalidLineRef, + /// A `.debug_info` unit entry reference is invalid. + InvalidUnitRef, + /// A `.debug_info` reference is invalid. + InvalidDebugInfoRef, + /// Invalid relative address in a range list. + InvalidRangeRelativeAddress, + /// Writing this CFI instruction is not implemented yet. + UnsupportedCfiInstruction, + /// Writing indirect pointers is not implemented yet. + UnsupportedIndirectAddress, + /// Writing this expression operation is not implemented yet. + UnsupportedOperation, + /// Operation branch target is invalid. + InvalidBranchTarget, + /// Writing this unit type is not supported yet. + UnsupportedUnitType, + } + + impl fmt::Display for ConvertError { + fn fmt(&self, f: &mut fmt::Formatter) -> result::Result<(), fmt::Error> { + use self::ConvertError::*; + match *self { + Read(ref e) => e.fmt(f), + UnsupportedAttributeValue => { + write!(f, "Writing of this attribute value is not implemented yet.") + } + InvalidAttributeValue => write!( + f, + "This attribute value is an invalid name/form combination." + ), + InvalidDebugInfoOffset => write!( + f, + "A `.debug_info` reference does not refer to a valid entry." + ), + InvalidAddress => write!(f, "An address could not be converted."), + UnsupportedLineInstruction => write!( + f, + "Writing this line number instruction is not implemented yet." + ), + UnsupportedLineStringForm => write!( + f, + "Writing this form of line string is not implemented yet." + ), + InvalidFileIndex => write!(f, "A `.debug_line` file index is invalid."), + InvalidDirectoryIndex => write!(f, "A `.debug_line` directory index is invalid."), + InvalidLineBase => write!(f, "A `.debug_line` line base is invalid."), + InvalidLineRef => write!(f, "A `.debug_line` reference is invalid."), + InvalidUnitRef => write!(f, "A `.debug_info` unit entry reference is invalid."), + InvalidDebugInfoRef => write!(f, "A `.debug_info` reference is invalid."), + InvalidRangeRelativeAddress => { + write!(f, "Invalid relative address in a range list.") + } + UnsupportedCfiInstruction => { + write!(f, "Writing this CFI instruction is not implemented yet.") + } + UnsupportedIndirectAddress => { + write!(f, "Writing indirect pointers is not implemented yet.") + } + UnsupportedOperation => write!( + f, + "Writing this expression operation is not implemented yet." + ), + InvalidBranchTarget => write!(f, "Operation branch target is invalid."), + UnsupportedUnitType => write!(f, "Writing this unit type is not supported yet."), + } + } + } + + impl error::Error for ConvertError {} + + impl From<read::Error> for ConvertError { + fn from(e: read::Error) -> Self { + ConvertError::Read(e) + } + } + + /// The result of a conversion. + pub type ConvertResult<T> = result::Result<T, ConvertError>; +} +#[cfg(feature = "read")] +pub use self::convert::*; diff --git a/vendor/gimli/src/write/op.rs b/vendor/gimli/src/write/op.rs new file mode 100644 index 0000000..287083b --- /dev/null +++ b/vendor/gimli/src/write/op.rs @@ -0,0 +1,1618 @@ +use alloc::boxed::Box; +use alloc::vec::Vec; + +use crate::common::{Encoding, Register}; +use crate::constants::{self, DwOp}; +use crate::leb128::write::{sleb128_size, uleb128_size}; +use crate::write::{ + Address, DebugInfoReference, Error, Reference, Result, UnitEntryId, UnitOffsets, Writer, +}; + +/// The bytecode for a DWARF expression or location description. +#[derive(Debug, Default, Clone, PartialEq, Eq, Hash)] +pub struct Expression { + operations: Vec<Operation>, +} + +impl Expression { + /// Create an empty expression. + #[inline] + pub fn new() -> Self { + Self::default() + } + + /// Create an expression from raw bytecode. + /// + /// This does not support operations that require references, such as `DW_OP_addr`. + #[inline] + pub fn raw(bytecode: Vec<u8>) -> Self { + Expression { + operations: vec![Operation::Raw(bytecode)], + } + } + + /// Add an operation to the expression. + /// + /// This should only be used for operations that have no explicit operands. + pub fn op(&mut self, opcode: DwOp) { + self.operations.push(Operation::Simple(opcode)); + } + + /// Add a `DW_OP_addr` operation to the expression. + pub fn op_addr(&mut self, address: Address) { + self.operations.push(Operation::Address(address)); + } + + /// Add a `DW_OP_constu` operation to the expression. + /// + /// This may be emitted as a smaller equivalent operation. + pub fn op_constu(&mut self, value: u64) { + self.operations.push(Operation::UnsignedConstant(value)); + } + + /// Add a `DW_OP_consts` operation to the expression. + /// + /// This may be emitted as a smaller equivalent operation. + pub fn op_consts(&mut self, value: i64) { + self.operations.push(Operation::SignedConstant(value)); + } + + /// Add a `DW_OP_const_type` or `DW_OP_GNU_const_type` operation to the expression. + pub fn op_const_type(&mut self, base: UnitEntryId, value: Box<[u8]>) { + self.operations.push(Operation::ConstantType(base, value)); + } + + /// Add a `DW_OP_fbreg` operation to the expression. + pub fn op_fbreg(&mut self, offset: i64) { + self.operations.push(Operation::FrameOffset(offset)); + } + + /// Add a `DW_OP_bregx` operation to the expression. + /// + /// This may be emitted as a smaller equivalent operation. + pub fn op_breg(&mut self, register: Register, offset: i64) { + self.operations + .push(Operation::RegisterOffset(register, offset)); + } + + /// Add a `DW_OP_regval_type` or `DW_OP_GNU_regval_type` operation to the expression. + /// + /// This may be emitted as a smaller equivalent operation. + pub fn op_regval_type(&mut self, register: Register, base: UnitEntryId) { + self.operations + .push(Operation::RegisterType(register, base)); + } + + /// Add a `DW_OP_pick` operation to the expression. + /// + /// This may be emitted as a `DW_OP_dup` or `DW_OP_over` operation. + pub fn op_pick(&mut self, index: u8) { + self.operations.push(Operation::Pick(index)); + } + + /// Add a `DW_OP_deref` operation to the expression. + pub fn op_deref(&mut self) { + self.operations.push(Operation::Deref { space: false }); + } + + /// Add a `DW_OP_xderef` operation to the expression. + pub fn op_xderef(&mut self) { + self.operations.push(Operation::Deref { space: true }); + } + + /// Add a `DW_OP_deref_size` operation to the expression. + pub fn op_deref_size(&mut self, size: u8) { + self.operations + .push(Operation::DerefSize { size, space: false }); + } + + /// Add a `DW_OP_xderef_size` operation to the expression. + pub fn op_xderef_size(&mut self, size: u8) { + self.operations + .push(Operation::DerefSize { size, space: true }); + } + + /// Add a `DW_OP_deref_type` or `DW_OP_GNU_deref_type` operation to the expression. + pub fn op_deref_type(&mut self, size: u8, base: UnitEntryId) { + self.operations.push(Operation::DerefType { + size, + base, + space: false, + }); + } + + /// Add a `DW_OP_xderef_type` operation to the expression. + pub fn op_xderef_type(&mut self, size: u8, base: UnitEntryId) { + self.operations.push(Operation::DerefType { + size, + base, + space: true, + }); + } + + /// Add a `DW_OP_plus_uconst` operation to the expression. + pub fn op_plus_uconst(&mut self, value: u64) { + self.operations.push(Operation::PlusConstant(value)); + } + + /// Add a `DW_OP_skip` operation to the expression. + /// + /// Returns the index of the operation. The caller must call `set_target` with + /// this index to set the target of the branch. + pub fn op_skip(&mut self) -> usize { + let index = self.next_index(); + self.operations.push(Operation::Skip(!0)); + index + } + + /// Add a `DW_OP_bra` operation to the expression. + /// + /// Returns the index of the operation. The caller must call `set_target` with + /// this index to set the target of the branch. + pub fn op_bra(&mut self) -> usize { + let index = self.next_index(); + self.operations.push(Operation::Branch(!0)); + index + } + + /// Return the index that will be assigned to the next operation. + /// + /// This can be passed to `set_target`. + #[inline] + pub fn next_index(&self) -> usize { + self.operations.len() + } + + /// Set the target of a `DW_OP_skip` or `DW_OP_bra` operation . + pub fn set_target(&mut self, operation: usize, new_target: usize) { + debug_assert!(new_target <= self.next_index()); + debug_assert_ne!(operation, new_target); + match self.operations[operation] { + Operation::Skip(ref mut target) | Operation::Branch(ref mut target) => { + *target = new_target; + } + _ => unimplemented!(), + } + } + + /// Add a `DW_OP_call4` operation to the expression. + pub fn op_call(&mut self, entry: UnitEntryId) { + self.operations.push(Operation::Call(entry)); + } + + /// Add a `DW_OP_call_ref` operation to the expression. + pub fn op_call_ref(&mut self, entry: Reference) { + self.operations.push(Operation::CallRef(entry)); + } + + /// Add a `DW_OP_convert` or `DW_OP_GNU_convert` operation to the expression. + /// + /// `base` is the DIE of the base type, or `None` for the generic type. + pub fn op_convert(&mut self, base: Option<UnitEntryId>) { + self.operations.push(Operation::Convert(base)); + } + + /// Add a `DW_OP_reinterpret` or `DW_OP_GNU_reinterpret` operation to the expression. + /// + /// `base` is the DIE of the base type, or `None` for the generic type. + pub fn op_reinterpret(&mut self, base: Option<UnitEntryId>) { + self.operations.push(Operation::Reinterpret(base)); + } + + /// Add a `DW_OP_entry_value` or `DW_OP_GNU_entry_value` operation to the expression. + pub fn op_entry_value(&mut self, expression: Expression) { + self.operations.push(Operation::EntryValue(expression)); + } + + /// Add a `DW_OP_regx` operation to the expression. + /// + /// This may be emitted as a smaller equivalent operation. + pub fn op_reg(&mut self, register: Register) { + self.operations.push(Operation::Register(register)); + } + + /// Add a `DW_OP_implicit_value` operation to the expression. + pub fn op_implicit_value(&mut self, data: Box<[u8]>) { + self.operations.push(Operation::ImplicitValue(data)); + } + + /// Add a `DW_OP_implicit_pointer` or `DW_OP_GNU_implicit_pointer` operation to the expression. + pub fn op_implicit_pointer(&mut self, entry: Reference, byte_offset: i64) { + self.operations + .push(Operation::ImplicitPointer { entry, byte_offset }); + } + + /// Add a `DW_OP_piece` operation to the expression. + pub fn op_piece(&mut self, size_in_bytes: u64) { + self.operations.push(Operation::Piece { size_in_bytes }); + } + + /// Add a `DW_OP_bit_piece` operation to the expression. + pub fn op_bit_piece(&mut self, size_in_bits: u64, bit_offset: u64) { + self.operations.push(Operation::BitPiece { + size_in_bits, + bit_offset, + }); + } + + /// Add a `DW_OP_GNU_parameter_ref` operation to the expression. + pub fn op_gnu_parameter_ref(&mut self, entry: UnitEntryId) { + self.operations.push(Operation::ParameterRef(entry)); + } + + /// Add a `DW_OP_WASM_location 0x0` operation to the expression. + pub fn op_wasm_local(&mut self, index: u32) { + self.operations.push(Operation::WasmLocal(index)); + } + + /// Add a `DW_OP_WASM_location 0x1` operation to the expression. + pub fn op_wasm_global(&mut self, index: u32) { + self.operations.push(Operation::WasmGlobal(index)); + } + + /// Add a `DW_OP_WASM_location 0x2` operation to the expression. + pub fn op_wasm_stack(&mut self, index: u32) { + self.operations.push(Operation::WasmStack(index)); + } + + pub(crate) fn size(&self, encoding: Encoding, unit_offsets: Option<&UnitOffsets>) -> usize { + let mut size = 0; + for operation in &self.operations { + size += operation.size(encoding, unit_offsets); + } + size + } + + pub(crate) fn write<W: Writer>( + &self, + w: &mut W, + mut refs: Option<&mut Vec<DebugInfoReference>>, + encoding: Encoding, + unit_offsets: Option<&UnitOffsets>, + ) -> Result<()> { + // TODO: only calculate offsets if needed? + let mut offsets = Vec::with_capacity(self.operations.len()); + let mut offset = w.len(); + for operation in &self.operations { + offsets.push(offset); + offset += operation.size(encoding, unit_offsets); + } + offsets.push(offset); + for (operation, offset) in self.operations.iter().zip(offsets.iter().copied()) { + debug_assert_eq!(w.len(), offset); + operation.write(w, refs.as_deref_mut(), encoding, unit_offsets, &offsets)?; + } + Ok(()) + } +} + +/// A single DWARF operation. +// +// This type is intentionally not public so that we can change the +// representation of expressions as needed. +// +// Variants are listed in the order they appear in Section 2.5. +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +enum Operation { + /// Raw bytecode. + /// + /// Does not support references. + Raw(Vec<u8>), + /// An operation that has no explicit operands. + /// + /// Represents: + /// - `DW_OP_drop`, `DW_OP_swap`, `DW_OP_rot` + /// - `DW_OP_push_object_address`, `DW_OP_form_tls_address`, `DW_OP_call_frame_cfa` + /// - `DW_OP_abs`, `DW_OP_and`, `DW_OP_div`, `DW_OP_minus`, `DW_OP_mod`, `DW_OP_mul`, + /// `DW_OP_neg`, `DW_OP_not`, `DW_OP_or`, `DW_OP_plus`, `DW_OP_shl`, `DW_OP_shr`, + /// `DW_OP_shra`, `DW_OP_xor` + /// - `DW_OP_le`, `DW_OP_ge`, `DW_OP_eq`, `DW_OP_lt`, `DW_OP_gt`, `DW_OP_ne` + /// - `DW_OP_nop` + /// - `DW_OP_stack_value` + Simple(DwOp), + /// Relocate the address if needed, and push it on the stack. + /// + /// Represents `DW_OP_addr`. + Address(Address), + /// Push an unsigned constant value on the stack. + /// + /// Represents `DW_OP_constu`. + UnsignedConstant(u64), + /// Push a signed constant value on the stack. + /// + /// Represents `DW_OP_consts`. + SignedConstant(i64), + /* TODO: requires .debug_addr write support + /// Read the address at the given index in `.debug_addr, relocate the address if needed, + /// and push it on the stack. + /// + /// Represents `DW_OP_addrx`. + AddressIndex(DebugAddrIndex<Offset>), + /// Read the address at the given index in `.debug_addr, and push it on the stack. + /// Do not relocate the address. + /// + /// Represents `DW_OP_constx`. + ConstantIndex(DebugAddrIndex<Offset>), + */ + /// Interpret the value bytes as a constant of a given type, and push it on the stack. + /// + /// Represents `DW_OP_const_type`. + ConstantType(UnitEntryId, Box<[u8]>), + /// Compute the frame base (using `DW_AT_frame_base`), add the + /// given offset, and then push the resulting sum on the stack. + /// + /// Represents `DW_OP_fbreg`. + FrameOffset(i64), + /// Find the contents of the given register, add the offset, and then + /// push the resulting sum on the stack. + /// + /// Represents `DW_OP_bregx`. + RegisterOffset(Register, i64), + /// Interpret the contents of the given register as a value of the given type, + /// and push it on the stack. + /// + /// Represents `DW_OP_regval_type`. + RegisterType(Register, UnitEntryId), + /// Copy the item at a stack index and push it on top of the stack. + /// + /// Represents `DW_OP_pick`, `DW_OP_dup`, and `DW_OP_over`. + Pick(u8), + /// Pop the topmost value of the stack, dereference it, and push the + /// resulting value. + /// + /// Represents `DW_OP_deref` and `DW_OP_xderef`. + Deref { + /// True if the dereference operation takes an address space + /// argument from the stack; false otherwise. + space: bool, + }, + /// Pop the topmost value of the stack, dereference it to obtain a value + /// of the given size, and push the resulting value. + /// + /// Represents `DW_OP_deref_size` and `DW_OP_xderef_size`. + DerefSize { + /// True if the dereference operation takes an address space + /// argument from the stack; false otherwise. + space: bool, + /// The size of the data to dereference. + size: u8, + }, + /// Pop the topmost value of the stack, dereference it to obtain a value + /// of the given type, and push the resulting value. + /// + /// Represents `DW_OP_deref_type` and `DW_OP_xderef_type`. + DerefType { + /// True if the dereference operation takes an address space + /// argument from the stack; false otherwise. + space: bool, + /// The size of the data to dereference. + size: u8, + /// The DIE of the base type, or `None` for the generic type. + base: UnitEntryId, + }, + /// Add an unsigned constant to the topmost value on the stack. + /// + /// Represents `DW_OP_plus_uconst`. + PlusConstant(u64), + /// Unconditional branch to the target location. + /// + /// The value is the index within the expression of the operation to branch to. + /// This will be converted to a relative offset when writing. + /// + /// Represents `DW_OP_skip`. + Skip(usize), + /// Branch to the target location if the top of stack is nonzero. + /// + /// The value is the index within the expression of the operation to branch to. + /// This will be converted to a relative offset when writing. + /// + /// Represents `DW_OP_bra`. + Branch(usize), + /// Evaluate a DWARF expression as a subroutine. + /// + /// The expression comes from the `DW_AT_location` attribute of the indicated DIE. + /// + /// Represents `DW_OP_call4`. + Call(UnitEntryId), + /// Evaluate an external DWARF expression as a subroutine. + /// + /// The expression comes from the `DW_AT_location` attribute of the indicated DIE, + /// which may be in another compilation unit or shared object. + /// + /// Represents `DW_OP_call_ref`. + CallRef(Reference), + /// Pop the top stack entry, convert it to a different type, and push it on the stack. + /// + /// Represents `DW_OP_convert`. + Convert(Option<UnitEntryId>), + /// Pop the top stack entry, reinterpret the bits in its value as a different type, + /// and push it on the stack. + /// + /// Represents `DW_OP_reinterpret`. + Reinterpret(Option<UnitEntryId>), + /// Evaluate an expression at the entry to the current subprogram, and push it on the stack. + /// + /// Represents `DW_OP_entry_value`. + EntryValue(Expression), + // FIXME: EntryRegister + /// Indicate that this piece's location is in the given register. + /// + /// Completes the piece or expression. + /// + /// Represents `DW_OP_regx`. + Register(Register), + /// The object has no location, but has a known constant value. + /// + /// Completes the piece or expression. + /// + /// Represents `DW_OP_implicit_value`. + ImplicitValue(Box<[u8]>), + /// The object is a pointer to a value which has no actual location, such as + /// an implicit value or a stack value. + /// + /// Completes the piece or expression. + /// + /// Represents `DW_OP_implicit_pointer`. + ImplicitPointer { + /// The DIE of the value that this is an implicit pointer into. + entry: Reference, + /// The byte offset into the value that the implicit pointer points to. + byte_offset: i64, + }, + /// Terminate a piece. + /// + /// Represents `DW_OP_piece`. + Piece { + /// The size of this piece in bytes. + size_in_bytes: u64, + }, + /// Terminate a piece with a size in bits. + /// + /// Represents `DW_OP_bit_piece`. + BitPiece { + /// The size of this piece in bits. + size_in_bits: u64, + /// The bit offset of this piece. + bit_offset: u64, + }, + /// This represents a parameter that was optimized out. + /// + /// The entry is the definition of the parameter, and is matched to + /// the `DW_TAG_GNU_call_site_parameter` in the caller that also + /// points to the same definition of the parameter. + /// + /// Represents `DW_OP_GNU_parameter_ref`. + ParameterRef(UnitEntryId), + /// The index of a local in the currently executing function. + /// + /// Represents `DW_OP_WASM_location 0x00`. + WasmLocal(u32), + /// The index of a global. + /// + /// Represents `DW_OP_WASM_location 0x01`. + WasmGlobal(u32), + /// The index of an item on the operand stack. + /// + /// Represents `DW_OP_WASM_location 0x02`. + WasmStack(u32), +} + +impl Operation { + fn size(&self, encoding: Encoding, unit_offsets: Option<&UnitOffsets>) -> usize { + let base_size = |base| { + // Errors are handled during writes. + match unit_offsets { + Some(offsets) => uleb128_size(offsets.unit_offset(base)), + None => 0, + } + }; + 1 + match *self { + Operation::Raw(ref bytecode) => return bytecode.len(), + Operation::Simple(_) => 0, + Operation::Address(_) => encoding.address_size as usize, + Operation::UnsignedConstant(value) => { + if value < 32 { + 0 + } else { + uleb128_size(value) + } + } + Operation::SignedConstant(value) => sleb128_size(value), + Operation::ConstantType(base, ref value) => base_size(base) + 1 + value.len(), + Operation::FrameOffset(offset) => sleb128_size(offset), + Operation::RegisterOffset(register, offset) => { + if register.0 < 32 { + sleb128_size(offset) + } else { + uleb128_size(register.0.into()) + sleb128_size(offset) + } + } + Operation::RegisterType(register, base) => { + uleb128_size(register.0.into()) + base_size(base) + } + Operation::Pick(index) => { + if index > 1 { + 1 + } else { + 0 + } + } + Operation::Deref { .. } => 0, + Operation::DerefSize { .. } => 1, + Operation::DerefType { base, .. } => 1 + base_size(base), + Operation::PlusConstant(value) => uleb128_size(value), + Operation::Skip(_) => 2, + Operation::Branch(_) => 2, + Operation::Call(_) => 4, + Operation::CallRef(_) => encoding.format.word_size() as usize, + Operation::Convert(base) => match base { + Some(base) => base_size(base), + None => 1, + }, + Operation::Reinterpret(base) => match base { + Some(base) => base_size(base), + None => 1, + }, + Operation::EntryValue(ref expression) => { + let length = expression.size(encoding, unit_offsets); + uleb128_size(length as u64) + length + } + Operation::Register(register) => { + if register.0 < 32 { + 0 + } else { + uleb128_size(register.0.into()) + } + } + Operation::ImplicitValue(ref data) => uleb128_size(data.len() as u64) + data.len(), + Operation::ImplicitPointer { byte_offset, .. } => { + encoding.format.word_size() as usize + sleb128_size(byte_offset) + } + Operation::Piece { size_in_bytes } => uleb128_size(size_in_bytes), + Operation::BitPiece { + size_in_bits, + bit_offset, + } => uleb128_size(size_in_bits) + uleb128_size(bit_offset), + Operation::ParameterRef(_) => 4, + Operation::WasmLocal(index) + | Operation::WasmGlobal(index) + | Operation::WasmStack(index) => 1 + uleb128_size(index.into()), + } + } + + pub(crate) fn write<W: Writer>( + &self, + w: &mut W, + refs: Option<&mut Vec<DebugInfoReference>>, + encoding: Encoding, + unit_offsets: Option<&UnitOffsets>, + offsets: &[usize], + ) -> Result<()> { + let entry_offset = |entry| match unit_offsets { + Some(offsets) => { + let offset = offsets.unit_offset(entry); + if offset == 0 { + Err(Error::UnsupportedExpressionForwardReference) + } else { + Ok(offset) + } + } + None => Err(Error::UnsupportedCfiExpressionReference), + }; + match *self { + Operation::Raw(ref bytecode) => w.write(bytecode)?, + Operation::Simple(opcode) => w.write_u8(opcode.0)?, + Operation::Address(address) => { + w.write_u8(constants::DW_OP_addr.0)?; + w.write_address(address, encoding.address_size)?; + } + Operation::UnsignedConstant(value) => { + if value < 32 { + w.write_u8(constants::DW_OP_lit0.0 + value as u8)?; + } else { + w.write_u8(constants::DW_OP_constu.0)?; + w.write_uleb128(value)?; + } + } + Operation::SignedConstant(value) => { + w.write_u8(constants::DW_OP_consts.0)?; + w.write_sleb128(value)?; + } + Operation::ConstantType(base, ref value) => { + if encoding.version >= 5 { + w.write_u8(constants::DW_OP_const_type.0)?; + } else { + w.write_u8(constants::DW_OP_GNU_const_type.0)?; + } + w.write_uleb128(entry_offset(base)?)?; + w.write_udata(value.len() as u64, 1)?; + w.write(value)?; + } + Operation::FrameOffset(offset) => { + w.write_u8(constants::DW_OP_fbreg.0)?; + w.write_sleb128(offset)?; + } + Operation::RegisterOffset(register, offset) => { + if register.0 < 32 { + w.write_u8(constants::DW_OP_breg0.0 + register.0 as u8)?; + } else { + w.write_u8(constants::DW_OP_bregx.0)?; + w.write_uleb128(register.0.into())?; + } + w.write_sleb128(offset)?; + } + Operation::RegisterType(register, base) => { + if encoding.version >= 5 { + w.write_u8(constants::DW_OP_regval_type.0)?; + } else { + w.write_u8(constants::DW_OP_GNU_regval_type.0)?; + } + w.write_uleb128(register.0.into())?; + w.write_uleb128(entry_offset(base)?)?; + } + Operation::Pick(index) => match index { + 0 => w.write_u8(constants::DW_OP_dup.0)?, + 1 => w.write_u8(constants::DW_OP_over.0)?, + _ => { + w.write_u8(constants::DW_OP_pick.0)?; + w.write_u8(index)?; + } + }, + Operation::Deref { space } => { + if space { + w.write_u8(constants::DW_OP_xderef.0)?; + } else { + w.write_u8(constants::DW_OP_deref.0)?; + } + } + Operation::DerefSize { space, size } => { + if space { + w.write_u8(constants::DW_OP_xderef_size.0)?; + } else { + w.write_u8(constants::DW_OP_deref_size.0)?; + } + w.write_u8(size)?; + } + Operation::DerefType { space, size, base } => { + if space { + w.write_u8(constants::DW_OP_xderef_type.0)?; + } else { + if encoding.version >= 5 { + w.write_u8(constants::DW_OP_deref_type.0)?; + } else { + w.write_u8(constants::DW_OP_GNU_deref_type.0)?; + } + } + w.write_u8(size)?; + w.write_uleb128(entry_offset(base)?)?; + } + Operation::PlusConstant(value) => { + w.write_u8(constants::DW_OP_plus_uconst.0)?; + w.write_uleb128(value)?; + } + Operation::Skip(target) => { + w.write_u8(constants::DW_OP_skip.0)?; + let offset = offsets[target] as i64 - (w.len() as i64 + 2); + w.write_sdata(offset, 2)?; + } + Operation::Branch(target) => { + w.write_u8(constants::DW_OP_bra.0)?; + let offset = offsets[target] as i64 - (w.len() as i64 + 2); + w.write_sdata(offset, 2)?; + } + Operation::Call(entry) => { + w.write_u8(constants::DW_OP_call4.0)?; + // TODO: this probably won't work in practice, because we may + // only know the offsets of base type DIEs at this point. + w.write_udata(entry_offset(entry)?, 4)?; + } + Operation::CallRef(entry) => { + w.write_u8(constants::DW_OP_call_ref.0)?; + let size = encoding.format.word_size(); + match entry { + Reference::Symbol(symbol) => w.write_reference(symbol, size)?, + Reference::Entry(unit, entry) => { + let refs = refs.ok_or(Error::InvalidReference)?; + refs.push(DebugInfoReference { + offset: w.len(), + unit, + entry, + size, + }); + w.write_udata(0, size)?; + } + } + } + Operation::Convert(base) => { + if encoding.version >= 5 { + w.write_u8(constants::DW_OP_convert.0)?; + } else { + w.write_u8(constants::DW_OP_GNU_convert.0)?; + } + match base { + Some(base) => w.write_uleb128(entry_offset(base)?)?, + None => w.write_u8(0)?, + } + } + Operation::Reinterpret(base) => { + if encoding.version >= 5 { + w.write_u8(constants::DW_OP_reinterpret.0)?; + } else { + w.write_u8(constants::DW_OP_GNU_reinterpret.0)?; + } + match base { + Some(base) => w.write_uleb128(entry_offset(base)?)?, + None => w.write_u8(0)?, + } + } + Operation::EntryValue(ref expression) => { + if encoding.version >= 5 { + w.write_u8(constants::DW_OP_entry_value.0)?; + } else { + w.write_u8(constants::DW_OP_GNU_entry_value.0)?; + } + let length = expression.size(encoding, unit_offsets); + w.write_uleb128(length as u64)?; + expression.write(w, refs, encoding, unit_offsets)?; + } + Operation::Register(register) => { + if register.0 < 32 { + w.write_u8(constants::DW_OP_reg0.0 + register.0 as u8)?; + } else { + w.write_u8(constants::DW_OP_regx.0)?; + w.write_uleb128(register.0.into())?; + } + } + Operation::ImplicitValue(ref data) => { + w.write_u8(constants::DW_OP_implicit_value.0)?; + w.write_uleb128(data.len() as u64)?; + w.write(data)?; + } + Operation::ImplicitPointer { entry, byte_offset } => { + if encoding.version >= 5 { + w.write_u8(constants::DW_OP_implicit_pointer.0)?; + } else { + w.write_u8(constants::DW_OP_GNU_implicit_pointer.0)?; + } + let size = if encoding.version == 2 { + encoding.address_size + } else { + encoding.format.word_size() + }; + match entry { + Reference::Symbol(symbol) => { + w.write_reference(symbol, size)?; + } + Reference::Entry(unit, entry) => { + let refs = refs.ok_or(Error::InvalidReference)?; + refs.push(DebugInfoReference { + offset: w.len(), + unit, + entry, + size, + }); + w.write_udata(0, size)?; + } + } + w.write_sleb128(byte_offset)?; + } + Operation::Piece { size_in_bytes } => { + w.write_u8(constants::DW_OP_piece.0)?; + w.write_uleb128(size_in_bytes)?; + } + Operation::BitPiece { + size_in_bits, + bit_offset, + } => { + w.write_u8(constants::DW_OP_bit_piece.0)?; + w.write_uleb128(size_in_bits)?; + w.write_uleb128(bit_offset)?; + } + Operation::ParameterRef(entry) => { + w.write_u8(constants::DW_OP_GNU_parameter_ref.0)?; + w.write_udata(entry_offset(entry)?, 4)?; + } + Operation::WasmLocal(index) => { + w.write(&[constants::DW_OP_WASM_location.0, 0])?; + w.write_uleb128(index.into())?; + } + Operation::WasmGlobal(index) => { + w.write(&[constants::DW_OP_WASM_location.0, 1])?; + w.write_uleb128(index.into())?; + } + Operation::WasmStack(index) => { + w.write(&[constants::DW_OP_WASM_location.0, 2])?; + w.write_uleb128(index.into())?; + } + } + Ok(()) + } +} + +#[cfg(feature = "read")] +pub(crate) mod convert { + use super::*; + use crate::common::UnitSectionOffset; + use crate::read::{self, Reader}; + use crate::write::{ConvertError, ConvertResult, UnitEntryId, UnitId}; + use std::collections::HashMap; + + impl Expression { + /// Create an expression from the input expression. + pub fn from<R: Reader<Offset = usize>>( + from_expression: read::Expression<R>, + encoding: Encoding, + dwarf: Option<&read::Dwarf<R>>, + unit: Option<&read::Unit<R>>, + entry_ids: Option<&HashMap<UnitSectionOffset, (UnitId, UnitEntryId)>>, + convert_address: &dyn Fn(u64) -> Option<Address>, + ) -> ConvertResult<Expression> { + let convert_unit_offset = |offset: read::UnitOffset| -> ConvertResult<_> { + let entry_ids = entry_ids.ok_or(ConvertError::UnsupportedOperation)?; + let unit = unit.ok_or(ConvertError::UnsupportedOperation)?; + let id = entry_ids + .get(&offset.to_unit_section_offset(unit)) + .ok_or(ConvertError::InvalidUnitRef)?; + Ok(id.1) + }; + let convert_debug_info_offset = |offset| -> ConvertResult<_> { + // TODO: support relocations + let entry_ids = entry_ids.ok_or(ConvertError::UnsupportedOperation)?; + let id = entry_ids + .get(&UnitSectionOffset::DebugInfoOffset(offset)) + .ok_or(ConvertError::InvalidDebugInfoRef)?; + Ok(Reference::Entry(id.0, id.1)) + }; + + // Calculate offsets for use in branch/skip operations. + let mut offsets = Vec::new(); + let mut offset = 0; + let mut from_operations = from_expression.clone().operations(encoding); + while from_operations.next()?.is_some() { + offsets.push(offset); + offset = from_operations.offset_from(&from_expression); + } + offsets.push(from_expression.0.len()); + + let mut from_operations = from_expression.clone().operations(encoding); + let mut operations = Vec::new(); + while let Some(from_operation) = from_operations.next()? { + let operation = match from_operation { + read::Operation::Deref { + base_type, + size, + space, + } => { + if base_type.0 != 0 { + let base = convert_unit_offset(base_type)?; + Operation::DerefType { space, size, base } + } else if size != encoding.address_size { + Operation::DerefSize { space, size } + } else { + Operation::Deref { space } + } + } + read::Operation::Drop => Operation::Simple(constants::DW_OP_drop), + read::Operation::Pick { index } => Operation::Pick(index), + read::Operation::Swap => Operation::Simple(constants::DW_OP_swap), + read::Operation::Rot => Operation::Simple(constants::DW_OP_rot), + read::Operation::Abs => Operation::Simple(constants::DW_OP_abs), + read::Operation::And => Operation::Simple(constants::DW_OP_and), + read::Operation::Div => Operation::Simple(constants::DW_OP_div), + read::Operation::Minus => Operation::Simple(constants::DW_OP_minus), + read::Operation::Mod => Operation::Simple(constants::DW_OP_mod), + read::Operation::Mul => Operation::Simple(constants::DW_OP_mul), + read::Operation::Neg => Operation::Simple(constants::DW_OP_neg), + read::Operation::Not => Operation::Simple(constants::DW_OP_not), + read::Operation::Or => Operation::Simple(constants::DW_OP_or), + read::Operation::Plus => Operation::Simple(constants::DW_OP_plus), + read::Operation::PlusConstant { value } => Operation::PlusConstant(value), + read::Operation::Shl => Operation::Simple(constants::DW_OP_shl), + read::Operation::Shr => Operation::Simple(constants::DW_OP_shr), + read::Operation::Shra => Operation::Simple(constants::DW_OP_shra), + read::Operation::Xor => Operation::Simple(constants::DW_OP_xor), + read::Operation::Eq => Operation::Simple(constants::DW_OP_eq), + read::Operation::Ge => Operation::Simple(constants::DW_OP_ge), + read::Operation::Gt => Operation::Simple(constants::DW_OP_gt), + read::Operation::Le => Operation::Simple(constants::DW_OP_le), + read::Operation::Lt => Operation::Simple(constants::DW_OP_lt), + read::Operation::Ne => Operation::Simple(constants::DW_OP_ne), + read::Operation::Bra { target } => { + let offset = from_operations + .offset_from(&from_expression) + .wrapping_add(i64::from(target) as usize); + let index = offsets + .binary_search(&offset) + .map_err(|_| ConvertError::InvalidBranchTarget)?; + Operation::Branch(index) + } + read::Operation::Skip { target } => { + let offset = from_operations + .offset_from(&from_expression) + .wrapping_add(i64::from(target) as usize); + let index = offsets + .binary_search(&offset) + .map_err(|_| ConvertError::InvalidBranchTarget)?; + Operation::Skip(index) + } + read::Operation::UnsignedConstant { value } => { + Operation::UnsignedConstant(value) + } + read::Operation::SignedConstant { value } => Operation::SignedConstant(value), + read::Operation::Register { register } => Operation::Register(register), + read::Operation::RegisterOffset { + register, + offset, + base_type, + } => { + if base_type.0 != 0 { + Operation::RegisterType(register, convert_unit_offset(base_type)?) + } else { + Operation::RegisterOffset(register, offset) + } + } + read::Operation::FrameOffset { offset } => Operation::FrameOffset(offset), + read::Operation::Nop => Operation::Simple(constants::DW_OP_nop), + read::Operation::PushObjectAddress => { + Operation::Simple(constants::DW_OP_push_object_address) + } + read::Operation::Call { offset } => match offset { + read::DieReference::UnitRef(offset) => { + Operation::Call(convert_unit_offset(offset)?) + } + read::DieReference::DebugInfoRef(offset) => { + Operation::CallRef(convert_debug_info_offset(offset)?) + } + }, + read::Operation::TLS => Operation::Simple(constants::DW_OP_form_tls_address), + read::Operation::CallFrameCFA => { + Operation::Simple(constants::DW_OP_call_frame_cfa) + } + read::Operation::Piece { + size_in_bits, + bit_offset: None, + } => Operation::Piece { + size_in_bytes: size_in_bits / 8, + }, + read::Operation::Piece { + size_in_bits, + bit_offset: Some(bit_offset), + } => Operation::BitPiece { + size_in_bits, + bit_offset, + }, + read::Operation::ImplicitValue { data } => { + Operation::ImplicitValue(data.to_slice()?.into_owned().into()) + } + read::Operation::StackValue => Operation::Simple(constants::DW_OP_stack_value), + read::Operation::ImplicitPointer { value, byte_offset } => { + let entry = convert_debug_info_offset(value)?; + Operation::ImplicitPointer { entry, byte_offset } + } + read::Operation::EntryValue { expression } => { + let expression = Expression::from( + read::Expression(expression), + encoding, + dwarf, + unit, + entry_ids, + convert_address, + )?; + Operation::EntryValue(expression) + } + read::Operation::ParameterRef { offset } => { + let entry = convert_unit_offset(offset)?; + Operation::ParameterRef(entry) + } + read::Operation::Address { address } => { + let address = + convert_address(address).ok_or(ConvertError::InvalidAddress)?; + Operation::Address(address) + } + read::Operation::AddressIndex { index } => { + let dwarf = dwarf.ok_or(ConvertError::UnsupportedOperation)?; + let unit = unit.ok_or(ConvertError::UnsupportedOperation)?; + let val = dwarf.address(unit, index)?; + let address = convert_address(val).ok_or(ConvertError::InvalidAddress)?; + Operation::Address(address) + } + read::Operation::ConstantIndex { index } => { + let dwarf = dwarf.ok_or(ConvertError::UnsupportedOperation)?; + let unit = unit.ok_or(ConvertError::UnsupportedOperation)?; + let val = dwarf.address(unit, index)?; + Operation::UnsignedConstant(val) + } + read::Operation::TypedLiteral { base_type, value } => { + let entry = convert_unit_offset(base_type)?; + Operation::ConstantType(entry, value.to_slice()?.into_owned().into()) + } + read::Operation::Convert { base_type } => { + if base_type.0 == 0 { + Operation::Convert(None) + } else { + let entry = convert_unit_offset(base_type)?; + Operation::Convert(Some(entry)) + } + } + read::Operation::Reinterpret { base_type } => { + if base_type.0 == 0 { + Operation::Reinterpret(None) + } else { + let entry = convert_unit_offset(base_type)?; + Operation::Reinterpret(Some(entry)) + } + } + read::Operation::WasmLocal { index } => Operation::WasmLocal(index), + read::Operation::WasmGlobal { index } => Operation::WasmGlobal(index), + read::Operation::WasmStack { index } => Operation::WasmStack(index), + }; + operations.push(operation); + } + Ok(Expression { operations }) + } + } +} + +#[cfg(test)] +#[cfg(feature = "read")] +mod tests { + use super::*; + use crate::common::{ + DebugAbbrevOffset, DebugAddrBase, DebugInfoOffset, DebugLocListsBase, DebugRngListsBase, + DebugStrOffsetsBase, Format, SectionId, + }; + use crate::read; + use crate::write::{ + DebugLineStrOffsets, DebugStrOffsets, EndianVec, LineProgram, Sections, Unit, UnitTable, + }; + use crate::LittleEndian; + use std::collections::HashMap; + use std::sync::Arc; + + #[test] + fn test_operation() { + for &version in &[3, 4, 5] { + for &address_size in &[4, 8] { + for &format in &[Format::Dwarf32, Format::Dwarf64] { + let encoding = Encoding { + format, + version, + address_size, + }; + + let mut units = UnitTable::default(); + let unit_id = units.add(Unit::new(encoding, LineProgram::none())); + let unit = units.get_mut(unit_id); + let entry_id = unit.add(unit.root(), constants::DW_TAG_base_type); + let reference = Reference::Entry(unit_id, entry_id); + + let mut sections = Sections::new(EndianVec::new(LittleEndian)); + let debug_line_str_offsets = DebugLineStrOffsets::none(); + let debug_str_offsets = DebugStrOffsets::none(); + let debug_info_offsets = units + .write(&mut sections, &debug_line_str_offsets, &debug_str_offsets) + .unwrap(); + let unit_offsets = debug_info_offsets.unit_offsets(unit_id); + let debug_info_offset = unit_offsets.debug_info_offset(entry_id); + let entry_offset = + read::UnitOffset(unit_offsets.unit_offset(entry_id) as usize); + + let mut reg_expression = Expression::new(); + reg_expression.op_reg(Register(23)); + + let operations: &[(&dyn Fn(&mut Expression), Operation, read::Operation<_>)] = + &[ + ( + &|x| x.op_deref(), + Operation::Deref { space: false }, + read::Operation::Deref { + base_type: read::UnitOffset(0), + size: address_size, + space: false, + }, + ), + ( + &|x| x.op_xderef(), + Operation::Deref { space: true }, + read::Operation::Deref { + base_type: read::UnitOffset(0), + size: address_size, + space: true, + }, + ), + ( + &|x| x.op_deref_size(2), + Operation::DerefSize { + space: false, + size: 2, + }, + read::Operation::Deref { + base_type: read::UnitOffset(0), + size: 2, + space: false, + }, + ), + ( + &|x| x.op_xderef_size(2), + Operation::DerefSize { + space: true, + size: 2, + }, + read::Operation::Deref { + base_type: read::UnitOffset(0), + size: 2, + space: true, + }, + ), + ( + &|x| x.op_deref_type(2, entry_id), + Operation::DerefType { + space: false, + size: 2, + base: entry_id, + }, + read::Operation::Deref { + base_type: entry_offset, + size: 2, + space: false, + }, + ), + ( + &|x| x.op_xderef_type(2, entry_id), + Operation::DerefType { + space: true, + size: 2, + base: entry_id, + }, + read::Operation::Deref { + base_type: entry_offset, + size: 2, + space: true, + }, + ), + ( + &|x| x.op(constants::DW_OP_drop), + Operation::Simple(constants::DW_OP_drop), + read::Operation::Drop, + ), + ( + &|x| x.op_pick(0), + Operation::Pick(0), + read::Operation::Pick { index: 0 }, + ), + ( + &|x| x.op_pick(1), + Operation::Pick(1), + read::Operation::Pick { index: 1 }, + ), + ( + &|x| x.op_pick(2), + Operation::Pick(2), + read::Operation::Pick { index: 2 }, + ), + ( + &|x| x.op(constants::DW_OP_swap), + Operation::Simple(constants::DW_OP_swap), + read::Operation::Swap, + ), + ( + &|x| x.op(constants::DW_OP_rot), + Operation::Simple(constants::DW_OP_rot), + read::Operation::Rot, + ), + ( + &|x| x.op(constants::DW_OP_abs), + Operation::Simple(constants::DW_OP_abs), + read::Operation::Abs, + ), + ( + &|x| x.op(constants::DW_OP_and), + Operation::Simple(constants::DW_OP_and), + read::Operation::And, + ), + ( + &|x| x.op(constants::DW_OP_div), + Operation::Simple(constants::DW_OP_div), + read::Operation::Div, + ), + ( + &|x| x.op(constants::DW_OP_minus), + Operation::Simple(constants::DW_OP_minus), + read::Operation::Minus, + ), + ( + &|x| x.op(constants::DW_OP_mod), + Operation::Simple(constants::DW_OP_mod), + read::Operation::Mod, + ), + ( + &|x| x.op(constants::DW_OP_mul), + Operation::Simple(constants::DW_OP_mul), + read::Operation::Mul, + ), + ( + &|x| x.op(constants::DW_OP_neg), + Operation::Simple(constants::DW_OP_neg), + read::Operation::Neg, + ), + ( + &|x| x.op(constants::DW_OP_not), + Operation::Simple(constants::DW_OP_not), + read::Operation::Not, + ), + ( + &|x| x.op(constants::DW_OP_or), + Operation::Simple(constants::DW_OP_or), + read::Operation::Or, + ), + ( + &|x| x.op(constants::DW_OP_plus), + Operation::Simple(constants::DW_OP_plus), + read::Operation::Plus, + ), + ( + &|x| x.op_plus_uconst(23), + Operation::PlusConstant(23), + read::Operation::PlusConstant { value: 23 }, + ), + ( + &|x| x.op(constants::DW_OP_shl), + Operation::Simple(constants::DW_OP_shl), + read::Operation::Shl, + ), + ( + &|x| x.op(constants::DW_OP_shr), + Operation::Simple(constants::DW_OP_shr), + read::Operation::Shr, + ), + ( + &|x| x.op(constants::DW_OP_shra), + Operation::Simple(constants::DW_OP_shra), + read::Operation::Shra, + ), + ( + &|x| x.op(constants::DW_OP_xor), + Operation::Simple(constants::DW_OP_xor), + read::Operation::Xor, + ), + ( + &|x| x.op(constants::DW_OP_eq), + Operation::Simple(constants::DW_OP_eq), + read::Operation::Eq, + ), + ( + &|x| x.op(constants::DW_OP_ge), + Operation::Simple(constants::DW_OP_ge), + read::Operation::Ge, + ), + ( + &|x| x.op(constants::DW_OP_gt), + Operation::Simple(constants::DW_OP_gt), + read::Operation::Gt, + ), + ( + &|x| x.op(constants::DW_OP_le), + Operation::Simple(constants::DW_OP_le), + read::Operation::Le, + ), + ( + &|x| x.op(constants::DW_OP_lt), + Operation::Simple(constants::DW_OP_lt), + read::Operation::Lt, + ), + ( + &|x| x.op(constants::DW_OP_ne), + Operation::Simple(constants::DW_OP_ne), + read::Operation::Ne, + ), + ( + &|x| x.op_constu(23), + Operation::UnsignedConstant(23), + read::Operation::UnsignedConstant { value: 23 }, + ), + ( + &|x| x.op_consts(-23), + Operation::SignedConstant(-23), + read::Operation::SignedConstant { value: -23 }, + ), + ( + &|x| x.op_reg(Register(23)), + Operation::Register(Register(23)), + read::Operation::Register { + register: Register(23), + }, + ), + ( + &|x| x.op_reg(Register(123)), + Operation::Register(Register(123)), + read::Operation::Register { + register: Register(123), + }, + ), + ( + &|x| x.op_breg(Register(23), 34), + Operation::RegisterOffset(Register(23), 34), + read::Operation::RegisterOffset { + register: Register(23), + offset: 34, + base_type: read::UnitOffset(0), + }, + ), + ( + &|x| x.op_breg(Register(123), 34), + Operation::RegisterOffset(Register(123), 34), + read::Operation::RegisterOffset { + register: Register(123), + offset: 34, + base_type: read::UnitOffset(0), + }, + ), + ( + &|x| x.op_regval_type(Register(23), entry_id), + Operation::RegisterType(Register(23), entry_id), + read::Operation::RegisterOffset { + register: Register(23), + offset: 0, + base_type: entry_offset, + }, + ), + ( + &|x| x.op_fbreg(34), + Operation::FrameOffset(34), + read::Operation::FrameOffset { offset: 34 }, + ), + ( + &|x| x.op(constants::DW_OP_nop), + Operation::Simple(constants::DW_OP_nop), + read::Operation::Nop, + ), + ( + &|x| x.op(constants::DW_OP_push_object_address), + Operation::Simple(constants::DW_OP_push_object_address), + read::Operation::PushObjectAddress, + ), + ( + &|x| x.op_call(entry_id), + Operation::Call(entry_id), + read::Operation::Call { + offset: read::DieReference::UnitRef(entry_offset), + }, + ), + ( + &|x| x.op_call_ref(reference), + Operation::CallRef(reference), + read::Operation::Call { + offset: read::DieReference::DebugInfoRef(debug_info_offset), + }, + ), + ( + &|x| x.op(constants::DW_OP_form_tls_address), + Operation::Simple(constants::DW_OP_form_tls_address), + read::Operation::TLS, + ), + ( + &|x| x.op(constants::DW_OP_call_frame_cfa), + Operation::Simple(constants::DW_OP_call_frame_cfa), + read::Operation::CallFrameCFA, + ), + ( + &|x| x.op_piece(23), + Operation::Piece { size_in_bytes: 23 }, + read::Operation::Piece { + size_in_bits: 23 * 8, + bit_offset: None, + }, + ), + ( + &|x| x.op_bit_piece(23, 34), + Operation::BitPiece { + size_in_bits: 23, + bit_offset: 34, + }, + read::Operation::Piece { + size_in_bits: 23, + bit_offset: Some(34), + }, + ), + ( + &|x| x.op_implicit_value(vec![23].into()), + Operation::ImplicitValue(vec![23].into()), + read::Operation::ImplicitValue { + data: read::EndianSlice::new(&[23], LittleEndian), + }, + ), + ( + &|x| x.op(constants::DW_OP_stack_value), + Operation::Simple(constants::DW_OP_stack_value), + read::Operation::StackValue, + ), + ( + &|x| x.op_implicit_pointer(reference, 23), + Operation::ImplicitPointer { + entry: reference, + byte_offset: 23, + }, + read::Operation::ImplicitPointer { + value: debug_info_offset, + byte_offset: 23, + }, + ), + ( + &|x| x.op_entry_value(reg_expression.clone()), + Operation::EntryValue(reg_expression.clone()), + read::Operation::EntryValue { + expression: read::EndianSlice::new( + &[constants::DW_OP_reg23.0], + LittleEndian, + ), + }, + ), + ( + &|x| x.op_gnu_parameter_ref(entry_id), + Operation::ParameterRef(entry_id), + read::Operation::ParameterRef { + offset: entry_offset, + }, + ), + ( + &|x| x.op_addr(Address::Constant(23)), + Operation::Address(Address::Constant(23)), + read::Operation::Address { address: 23 }, + ), + ( + &|x| x.op_const_type(entry_id, vec![23].into()), + Operation::ConstantType(entry_id, vec![23].into()), + read::Operation::TypedLiteral { + base_type: entry_offset, + value: read::EndianSlice::new(&[23], LittleEndian), + }, + ), + ( + &|x| x.op_convert(None), + Operation::Convert(None), + read::Operation::Convert { + base_type: read::UnitOffset(0), + }, + ), + ( + &|x| x.op_convert(Some(entry_id)), + Operation::Convert(Some(entry_id)), + read::Operation::Convert { + base_type: entry_offset, + }, + ), + ( + &|x| x.op_reinterpret(None), + Operation::Reinterpret(None), + read::Operation::Reinterpret { + base_type: read::UnitOffset(0), + }, + ), + ( + &|x| x.op_reinterpret(Some(entry_id)), + Operation::Reinterpret(Some(entry_id)), + read::Operation::Reinterpret { + base_type: entry_offset, + }, + ), + ( + &|x| x.op_wasm_local(1000), + Operation::WasmLocal(1000), + read::Operation::WasmLocal { index: 1000 }, + ), + ( + &|x| x.op_wasm_global(1000), + Operation::WasmGlobal(1000), + read::Operation::WasmGlobal { index: 1000 }, + ), + ( + &|x| x.op_wasm_stack(1000), + Operation::WasmStack(1000), + read::Operation::WasmStack { index: 1000 }, + ), + ]; + + let mut expression = Expression::new(); + let start_index = expression.next_index(); + for (f, o, _) in operations { + f(&mut expression); + assert_eq!(expression.operations.last(), Some(o)); + } + + let bra_index = expression.op_bra(); + let skip_index = expression.op_skip(); + expression.op(constants::DW_OP_nop); + let end_index = expression.next_index(); + expression.set_target(bra_index, start_index); + expression.set_target(skip_index, end_index); + + let mut w = EndianVec::new(LittleEndian); + let mut refs = Vec::new(); + expression + .write(&mut w, Some(&mut refs), encoding, Some(&unit_offsets)) + .unwrap(); + for r in &refs { + assert_eq!(r.unit, unit_id); + assert_eq!(r.entry, entry_id); + w.write_offset_at( + r.offset, + debug_info_offset.0, + SectionId::DebugInfo, + r.size, + ) + .unwrap(); + } + + let read_expression = + read::Expression(read::EndianSlice::new(w.slice(), LittleEndian)); + let mut read_operations = read_expression.operations(encoding); + for (_, _, operation) in operations { + assert_eq!(read_operations.next(), Ok(Some(*operation))); + } + + // 4 = DW_OP_skip + i16 + DW_OP_nop + assert_eq!( + read_operations.next(), + Ok(Some(read::Operation::Bra { + target: -(w.len() as i16) + 4 + })) + ); + // 1 = DW_OP_nop + assert_eq!( + read_operations.next(), + Ok(Some(read::Operation::Skip { target: 1 })) + ); + assert_eq!(read_operations.next(), Ok(Some(read::Operation::Nop))); + assert_eq!(read_operations.next(), Ok(None)); + + // Fake the unit. + let unit = read::Unit { + header: read::UnitHeader::new( + encoding, + 0, + read::UnitType::Compilation, + DebugAbbrevOffset(0), + DebugInfoOffset(0).into(), + read::EndianSlice::new(&[], LittleEndian), + ), + abbreviations: Arc::new(read::Abbreviations::default()), + name: None, + comp_dir: None, + low_pc: 0, + str_offsets_base: DebugStrOffsetsBase(0), + addr_base: DebugAddrBase(0), + loclists_base: DebugLocListsBase(0), + rnglists_base: DebugRngListsBase(0), + line_program: None, + dwo_id: None, + }; + + let mut entry_ids = HashMap::new(); + entry_ids.insert(debug_info_offset.into(), (unit_id, entry_id)); + let convert_expression = Expression::from( + read_expression, + encoding, + None, /* dwarf */ + Some(&unit), + Some(&entry_ids), + &|address| Some(Address::Constant(address)), + ) + .unwrap(); + let mut convert_operations = convert_expression.operations.iter(); + for (_, operation, _) in operations { + assert_eq!(convert_operations.next(), Some(operation)); + } + assert_eq!( + convert_operations.next(), + Some(&Operation::Branch(start_index)) + ); + assert_eq!(convert_operations.next(), Some(&Operation::Skip(end_index))); + assert_eq!( + convert_operations.next(), + Some(&Operation::Simple(constants::DW_OP_nop)) + ); + } + } + } + } +} diff --git a/vendor/gimli/src/write/range.rs b/vendor/gimli/src/write/range.rs new file mode 100644 index 0000000..c707e1e --- /dev/null +++ b/vendor/gimli/src/write/range.rs @@ -0,0 +1,416 @@ +use alloc::vec::Vec; +use indexmap::IndexSet; +use std::ops::{Deref, DerefMut}; + +use crate::common::{Encoding, RangeListsOffset, SectionId}; +use crate::write::{Address, BaseId, Error, Result, Section, Sections, Writer}; + +define_section!( + DebugRanges, + RangeListsOffset, + "A writable `.debug_ranges` section." +); +define_section!( + DebugRngLists, + RangeListsOffset, + "A writable `.debug_rnglists` section." +); + +define_offsets!( + RangeListOffsets: RangeListId => RangeListsOffset, + "The section offsets of a series of range lists within the `.debug_ranges` or `.debug_rnglists` sections." +); + +define_id!( + RangeListId, + "An identifier for a range list in a `RangeListTable`." +); + +/// A table of range lists that will be stored in a `.debug_ranges` or `.debug_rnglists` section. +#[derive(Debug, Default)] +pub struct RangeListTable { + base_id: BaseId, + ranges: IndexSet<RangeList>, +} + +impl RangeListTable { + /// Add a range list to the table. + pub fn add(&mut self, range_list: RangeList) -> RangeListId { + let (index, _) = self.ranges.insert_full(range_list); + RangeListId::new(self.base_id, index) + } + + /// Write the range list table to the appropriate section for the given DWARF version. + pub(crate) fn write<W: Writer>( + &self, + sections: &mut Sections<W>, + encoding: Encoding, + ) -> Result<RangeListOffsets> { + if self.ranges.is_empty() { + return Ok(RangeListOffsets::none()); + } + + match encoding.version { + 2..=4 => self.write_ranges(&mut sections.debug_ranges, encoding.address_size), + 5 => self.write_rnglists(&mut sections.debug_rnglists, encoding), + _ => Err(Error::UnsupportedVersion(encoding.version)), + } + } + + /// Write the range list table to the `.debug_ranges` section. + fn write_ranges<W: Writer>( + &self, + w: &mut DebugRanges<W>, + address_size: u8, + ) -> Result<RangeListOffsets> { + let mut offsets = Vec::new(); + for range_list in self.ranges.iter() { + offsets.push(w.offset()); + for range in &range_list.0 { + // Note that we must ensure none of the ranges have both begin == 0 and end == 0. + // We do this by ensuring that begin != end, which is a bit more restrictive + // than required, but still seems reasonable. + match *range { + Range::BaseAddress { address } => { + let marker = !0 >> (64 - address_size * 8); + w.write_udata(marker, address_size)?; + w.write_address(address, address_size)?; + } + Range::OffsetPair { begin, end } => { + if begin == end { + return Err(Error::InvalidRange); + } + w.write_udata(begin, address_size)?; + w.write_udata(end, address_size)?; + } + Range::StartEnd { begin, end } => { + if begin == end { + return Err(Error::InvalidRange); + } + w.write_address(begin, address_size)?; + w.write_address(end, address_size)?; + } + Range::StartLength { begin, length } => { + let end = match begin { + Address::Constant(begin) => Address::Constant(begin + length), + Address::Symbol { symbol, addend } => Address::Symbol { + symbol, + addend: addend + length as i64, + }, + }; + if begin == end { + return Err(Error::InvalidRange); + } + w.write_address(begin, address_size)?; + w.write_address(end, address_size)?; + } + } + } + w.write_udata(0, address_size)?; + w.write_udata(0, address_size)?; + } + Ok(RangeListOffsets { + base_id: self.base_id, + offsets, + }) + } + + /// Write the range list table to the `.debug_rnglists` section. + fn write_rnglists<W: Writer>( + &self, + w: &mut DebugRngLists<W>, + encoding: Encoding, + ) -> Result<RangeListOffsets> { + let mut offsets = Vec::new(); + + if encoding.version != 5 { + return Err(Error::NeedVersion(5)); + } + + let length_offset = w.write_initial_length(encoding.format)?; + let length_base = w.len(); + + w.write_u16(encoding.version)?; + w.write_u8(encoding.address_size)?; + w.write_u8(0)?; // segment_selector_size + w.write_u32(0)?; // offset_entry_count (when set to zero DW_FORM_rnglistx can't be used, see section 7.28) + // FIXME implement DW_FORM_rnglistx writing and implement the offset entry list + + for range_list in self.ranges.iter() { + offsets.push(w.offset()); + for range in &range_list.0 { + match *range { + Range::BaseAddress { address } => { + w.write_u8(crate::constants::DW_RLE_base_address.0)?; + w.write_address(address, encoding.address_size)?; + } + Range::OffsetPair { begin, end } => { + w.write_u8(crate::constants::DW_RLE_offset_pair.0)?; + w.write_uleb128(begin)?; + w.write_uleb128(end)?; + } + Range::StartEnd { begin, end } => { + w.write_u8(crate::constants::DW_RLE_start_end.0)?; + w.write_address(begin, encoding.address_size)?; + w.write_address(end, encoding.address_size)?; + } + Range::StartLength { begin, length } => { + w.write_u8(crate::constants::DW_RLE_start_length.0)?; + w.write_address(begin, encoding.address_size)?; + w.write_uleb128(length)?; + } + } + } + + w.write_u8(crate::constants::DW_RLE_end_of_list.0)?; + } + + let length = (w.len() - length_base) as u64; + w.write_initial_length_at(length_offset, length, encoding.format)?; + + Ok(RangeListOffsets { + base_id: self.base_id, + offsets, + }) + } +} + +/// A range list that will be stored in a `.debug_ranges` or `.debug_rnglists` section. +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub struct RangeList(pub Vec<Range>); + +/// A single range. +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub enum Range { + /// DW_RLE_base_address + BaseAddress { + /// Base address. + address: Address, + }, + /// DW_RLE_offset_pair + OffsetPair { + /// Start of range relative to base address. + begin: u64, + /// End of range relative to base address. + end: u64, + }, + /// DW_RLE_start_end + StartEnd { + /// Start of range. + begin: Address, + /// End of range. + end: Address, + }, + /// DW_RLE_start_length + StartLength { + /// Start of range. + begin: Address, + /// Length of range. + length: u64, + }, +} + +#[cfg(feature = "read")] +mod convert { + use super::*; + + use crate::read::{self, Reader}; + use crate::write::{ConvertError, ConvertResult, ConvertUnitContext}; + + impl RangeList { + /// Create a range list by reading the data from the give range list iter. + pub(crate) fn from<R: Reader<Offset = usize>>( + mut from: read::RawRngListIter<R>, + context: &ConvertUnitContext<R>, + ) -> ConvertResult<Self> { + let mut have_base_address = context.base_address != Address::Constant(0); + let convert_address = + |x| (context.convert_address)(x).ok_or(ConvertError::InvalidAddress); + let mut ranges = Vec::new(); + while let Some(from_range) = from.next()? { + let range = match from_range { + read::RawRngListEntry::AddressOrOffsetPair { begin, end } => { + // These were parsed as addresses, even if they are offsets. + let begin = convert_address(begin)?; + let end = convert_address(end)?; + match (begin, end) { + (Address::Constant(begin_offset), Address::Constant(end_offset)) => { + if have_base_address { + Range::OffsetPair { + begin: begin_offset, + end: end_offset, + } + } else { + Range::StartEnd { begin, end } + } + } + _ => { + if have_base_address { + // At least one of begin/end is an address, but we also have + // a base address. Adding addresses is undefined. + return Err(ConvertError::InvalidRangeRelativeAddress); + } + Range::StartEnd { begin, end } + } + } + } + read::RawRngListEntry::BaseAddress { addr } => { + have_base_address = true; + let address = convert_address(addr)?; + Range::BaseAddress { address } + } + read::RawRngListEntry::BaseAddressx { addr } => { + have_base_address = true; + let address = convert_address(context.dwarf.address(context.unit, addr)?)?; + Range::BaseAddress { address } + } + read::RawRngListEntry::StartxEndx { begin, end } => { + let begin = convert_address(context.dwarf.address(context.unit, begin)?)?; + let end = convert_address(context.dwarf.address(context.unit, end)?)?; + Range::StartEnd { begin, end } + } + read::RawRngListEntry::StartxLength { begin, length } => { + let begin = convert_address(context.dwarf.address(context.unit, begin)?)?; + Range::StartLength { begin, length } + } + read::RawRngListEntry::OffsetPair { begin, end } => { + Range::OffsetPair { begin, end } + } + read::RawRngListEntry::StartEnd { begin, end } => { + let begin = convert_address(begin)?; + let end = convert_address(end)?; + Range::StartEnd { begin, end } + } + read::RawRngListEntry::StartLength { begin, length } => { + let begin = convert_address(begin)?; + Range::StartLength { begin, length } + } + }; + // Filtering empty ranges out. + match range { + Range::StartLength { length, .. } if length == 0 => continue, + Range::StartEnd { begin, end, .. } if begin == end => continue, + Range::OffsetPair { begin, end, .. } if begin == end => continue, + _ => (), + } + ranges.push(range); + } + Ok(RangeList(ranges)) + } + } +} + +#[cfg(test)] +#[cfg(feature = "read")] +mod tests { + use super::*; + use crate::common::{ + DebugAbbrevOffset, DebugAddrBase, DebugInfoOffset, DebugLocListsBase, DebugRngListsBase, + DebugStrOffsetsBase, Format, + }; + use crate::read; + use crate::write::{ + ConvertUnitContext, EndianVec, LineStringTable, LocationListTable, Range, RangeListTable, + StringTable, + }; + use crate::LittleEndian; + use std::collections::HashMap; + use std::sync::Arc; + + #[test] + fn test_range() { + let mut line_strings = LineStringTable::default(); + let mut strings = StringTable::default(); + + for &version in &[2, 3, 4, 5] { + for &address_size in &[4, 8] { + for &format in &[Format::Dwarf32, Format::Dwarf64] { + let encoding = Encoding { + format, + version, + address_size, + }; + + let mut range_list = RangeList(vec![ + Range::StartLength { + begin: Address::Constant(6666), + length: 7777, + }, + Range::StartEnd { + begin: Address::Constant(4444), + end: Address::Constant(5555), + }, + Range::BaseAddress { + address: Address::Constant(1111), + }, + Range::OffsetPair { + begin: 2222, + end: 3333, + }, + ]); + + let mut ranges = RangeListTable::default(); + let range_list_id = ranges.add(range_list.clone()); + + let mut sections = Sections::new(EndianVec::new(LittleEndian)); + let range_list_offsets = ranges.write(&mut sections, encoding).unwrap(); + + let read_debug_ranges = + read::DebugRanges::new(sections.debug_ranges.slice(), LittleEndian); + let read_debug_rnglists = + read::DebugRngLists::new(sections.debug_rnglists.slice(), LittleEndian); + let read_ranges = read::RangeLists::new(read_debug_ranges, read_debug_rnglists); + let offset = range_list_offsets.get(range_list_id); + let read_range_list = read_ranges.raw_ranges(offset, encoding).unwrap(); + + let dwarf = read::Dwarf { + ranges: read_ranges, + ..Default::default() + }; + let unit = read::Unit { + header: read::UnitHeader::new( + encoding, + 0, + read::UnitType::Compilation, + DebugAbbrevOffset(0), + DebugInfoOffset(0).into(), + read::EndianSlice::default(), + ), + abbreviations: Arc::new(read::Abbreviations::default()), + name: None, + comp_dir: None, + low_pc: 0, + str_offsets_base: DebugStrOffsetsBase(0), + addr_base: DebugAddrBase(0), + loclists_base: DebugLocListsBase(0), + rnglists_base: DebugRngListsBase(0), + line_program: None, + dwo_id: None, + }; + let context = ConvertUnitContext { + dwarf: &dwarf, + unit: &unit, + line_strings: &mut line_strings, + strings: &mut strings, + ranges: &mut ranges, + locations: &mut LocationListTable::default(), + convert_address: &|address| Some(Address::Constant(address)), + base_address: Address::Constant(0), + line_program_offset: None, + line_program_files: Vec::new(), + entry_ids: &HashMap::new(), + }; + let convert_range_list = RangeList::from(read_range_list, &context).unwrap(); + + if version <= 4 { + range_list.0[0] = Range::StartEnd { + begin: Address::Constant(6666), + end: Address::Constant(6666 + 7777), + }; + } + assert_eq!(range_list, convert_range_list); + } + } + } + } +} diff --git a/vendor/gimli/src/write/section.rs b/vendor/gimli/src/write/section.rs new file mode 100644 index 0000000..db5eb9a --- /dev/null +++ b/vendor/gimli/src/write/section.rs @@ -0,0 +1,172 @@ +use std::ops::DerefMut; +use std::result; +use std::vec::Vec; + +use crate::common::SectionId; +use crate::write::{ + DebugAbbrev, DebugFrame, DebugInfo, DebugInfoReference, DebugLine, DebugLineStr, DebugLoc, + DebugLocLists, DebugRanges, DebugRngLists, DebugStr, EhFrame, Writer, +}; + +macro_rules! define_section { + ($name:ident, $offset:ident, $docs:expr) => { + #[doc=$docs] + #[derive(Debug, Default)] + pub struct $name<W: Writer>(pub W); + + impl<W: Writer> $name<W> { + /// Return the offset of the next write. + pub fn offset(&self) -> $offset { + $offset(self.len()) + } + } + + impl<W: Writer> From<W> for $name<W> { + #[inline] + fn from(w: W) -> Self { + $name(w) + } + } + + impl<W: Writer> Deref for $name<W> { + type Target = W; + + #[inline] + fn deref(&self) -> &W { + &self.0 + } + } + + impl<W: Writer> DerefMut for $name<W> { + #[inline] + fn deref_mut(&mut self) -> &mut W { + &mut self.0 + } + } + + impl<W: Writer> Section<W> for $name<W> { + #[inline] + fn id(&self) -> SectionId { + SectionId::$name + } + } + }; +} + +/// Functionality common to all writable DWARF sections. +pub trait Section<W: Writer>: DerefMut<Target = W> { + /// Returns the DWARF section kind for this type. + fn id(&self) -> SectionId; + + /// Returns the ELF section name for this type. + fn name(&self) -> &'static str { + self.id().name() + } +} + +/// All of the writable DWARF sections. +#[derive(Debug, Default)] +pub struct Sections<W: Writer> { + /// The `.debug_abbrev` section. + pub debug_abbrev: DebugAbbrev<W>, + /// The `.debug_info` section. + pub debug_info: DebugInfo<W>, + /// The `.debug_line` section. + pub debug_line: DebugLine<W>, + /// The `.debug_line_str` section. + pub debug_line_str: DebugLineStr<W>, + /// The `.debug_ranges` section. + pub debug_ranges: DebugRanges<W>, + /// The `.debug_rnglists` section. + pub debug_rnglists: DebugRngLists<W>, + /// The `.debug_loc` section. + pub debug_loc: DebugLoc<W>, + /// The `.debug_loclists` section. + pub debug_loclists: DebugLocLists<W>, + /// The `.debug_str` section. + pub debug_str: DebugStr<W>, + /// The `.debug_frame` section. + pub debug_frame: DebugFrame<W>, + /// The `.eh_frame` section. + pub eh_frame: EhFrame<W>, + /// Unresolved references in the `.debug_info` section. + pub(crate) debug_info_refs: Vec<DebugInfoReference>, + /// Unresolved references in the `.debug_loc` section. + pub(crate) debug_loc_refs: Vec<DebugInfoReference>, + /// Unresolved references in the `.debug_loclists` section. + pub(crate) debug_loclists_refs: Vec<DebugInfoReference>, +} + +impl<W: Writer + Clone> Sections<W> { + /// Create a new `Sections` using clones of the given `section`. + pub fn new(section: W) -> Self { + Sections { + debug_abbrev: DebugAbbrev(section.clone()), + debug_info: DebugInfo(section.clone()), + debug_line: DebugLine(section.clone()), + debug_line_str: DebugLineStr(section.clone()), + debug_ranges: DebugRanges(section.clone()), + debug_rnglists: DebugRngLists(section.clone()), + debug_loc: DebugLoc(section.clone()), + debug_loclists: DebugLocLists(section.clone()), + debug_str: DebugStr(section.clone()), + debug_frame: DebugFrame(section.clone()), + eh_frame: EhFrame(section), + debug_info_refs: Vec::new(), + debug_loc_refs: Vec::new(), + debug_loclists_refs: Vec::new(), + } + } +} + +impl<W: Writer> Sections<W> { + /// For each section, call `f` once with a shared reference. + pub fn for_each<F, E>(&self, mut f: F) -> result::Result<(), E> + where + F: FnMut(SectionId, &W) -> result::Result<(), E>, + { + macro_rules! f { + ($s:expr) => { + f($s.id(), &$s) + }; + } + // Ordered so that earlier sections do not reference later sections. + f!(self.debug_abbrev)?; + f!(self.debug_str)?; + f!(self.debug_line_str)?; + f!(self.debug_line)?; + f!(self.debug_ranges)?; + f!(self.debug_rnglists)?; + f!(self.debug_loc)?; + f!(self.debug_loclists)?; + f!(self.debug_info)?; + f!(self.debug_frame)?; + f!(self.eh_frame)?; + Ok(()) + } + + /// For each section, call `f` once with a mutable reference. + pub fn for_each_mut<F, E>(&mut self, mut f: F) -> result::Result<(), E> + where + F: FnMut(SectionId, &mut W) -> result::Result<(), E>, + { + macro_rules! f { + ($s:expr) => { + f($s.id(), &mut $s) + }; + } + // Ordered so that earlier sections do not reference later sections. + f!(self.debug_abbrev)?; + f!(self.debug_str)?; + f!(self.debug_line_str)?; + f!(self.debug_line)?; + f!(self.debug_ranges)?; + f!(self.debug_rnglists)?; + f!(self.debug_loc)?; + f!(self.debug_loclists)?; + f!(self.debug_info)?; + f!(self.debug_frame)?; + f!(self.eh_frame)?; + Ok(()) + } +} diff --git a/vendor/gimli/src/write/str.rs b/vendor/gimli/src/write/str.rs new file mode 100644 index 0000000..83285c0 --- /dev/null +++ b/vendor/gimli/src/write/str.rs @@ -0,0 +1,172 @@ +use alloc::vec::Vec; +use indexmap::IndexSet; +use std::ops::{Deref, DerefMut}; + +use crate::common::{DebugLineStrOffset, DebugStrOffset, SectionId}; +use crate::write::{BaseId, Result, Section, Writer}; + +// Requirements: +// - values are `[u8]`, null bytes are not allowed +// - insertion returns a fixed id +// - inserting a duplicate returns the id of the existing value +// - able to convert an id to a section offset +// Optional? +// - able to get an existing value given an id +// +// Limitations of current implementation (using IndexSet): +// - inserting requires either an allocation for duplicates, +// or a double lookup for non-duplicates +// - doesn't preserve offsets when updating an existing `.debug_str` section +// +// Possible changes: +// - calculate offsets as we add values, and use that as the id. +// This would avoid the need for DebugStrOffsets but would make it +// hard to implement `get`. +macro_rules! define_string_table { + ($name:ident, $id:ident, $section:ident, $offsets:ident, $docs:expr) => { + #[doc=$docs] + #[derive(Debug, Default)] + pub struct $name { + base_id: BaseId, + strings: IndexSet<Vec<u8>>, + } + + impl $name { + /// Add a string to the string table and return its id. + /// + /// If the string already exists, then return the id of the existing string. + /// + /// # Panics + /// + /// Panics if `bytes` contains a null byte. + pub fn add<T>(&mut self, bytes: T) -> $id + where + T: Into<Vec<u8>>, + { + let bytes = bytes.into(); + assert!(!bytes.contains(&0)); + let (index, _) = self.strings.insert_full(bytes); + $id::new(self.base_id, index) + } + + /// Return the number of strings in the table. + #[inline] + pub fn count(&self) -> usize { + self.strings.len() + } + + /// Get a reference to a string in the table. + /// + /// # Panics + /// + /// Panics if `id` is invalid. + pub fn get(&self, id: $id) -> &[u8] { + debug_assert_eq!(self.base_id, id.base_id); + self.strings.get_index(id.index).map(Vec::as_slice).unwrap() + } + + /// Write the string table to the `.debug_str` section. + /// + /// Returns the offsets at which the strings are written. + pub fn write<W: Writer>(&self, w: &mut $section<W>) -> Result<$offsets> { + let mut offsets = Vec::new(); + for bytes in self.strings.iter() { + offsets.push(w.offset()); + w.write(bytes)?; + w.write_u8(0)?; + } + + Ok($offsets { + base_id: self.base_id, + offsets, + }) + } + } + }; +} + +define_id!(StringId, "An identifier for a string in a `StringTable`."); + +define_string_table!( + StringTable, + StringId, + DebugStr, + DebugStrOffsets, + "A table of strings that will be stored in a `.debug_str` section." +); + +define_section!(DebugStr, DebugStrOffset, "A writable `.debug_str` section."); + +define_offsets!( + DebugStrOffsets: StringId => DebugStrOffset, + "The section offsets of all strings within a `.debug_str` section." +); + +define_id!( + LineStringId, + "An identifier for a string in a `LineStringTable`." +); + +define_string_table!( + LineStringTable, + LineStringId, + DebugLineStr, + DebugLineStrOffsets, + "A table of strings that will be stored in a `.debug_line_str` section." +); + +define_section!( + DebugLineStr, + DebugLineStrOffset, + "A writable `.debug_line_str` section." +); + +define_offsets!( + DebugLineStrOffsets: LineStringId => DebugLineStrOffset, + "The section offsets of all strings within a `.debug_line_str` section." +); + +#[cfg(test)] +#[cfg(feature = "read")] +mod tests { + use super::*; + use crate::read; + use crate::write::EndianVec; + use crate::LittleEndian; + + #[test] + fn test_string_table() { + let mut strings = StringTable::default(); + assert_eq!(strings.count(), 0); + let id1 = strings.add(&b"one"[..]); + let id2 = strings.add(&b"two"[..]); + assert_eq!(strings.add(&b"one"[..]), id1); + assert_eq!(strings.add(&b"two"[..]), id2); + assert_eq!(strings.get(id1), &b"one"[..]); + assert_eq!(strings.get(id2), &b"two"[..]); + assert_eq!(strings.count(), 2); + + let mut debug_str = DebugStr::from(EndianVec::new(LittleEndian)); + let offsets = strings.write(&mut debug_str).unwrap(); + assert_eq!(debug_str.slice(), b"one\0two\0"); + assert_eq!(offsets.get(id1), DebugStrOffset(0)); + assert_eq!(offsets.get(id2), DebugStrOffset(4)); + assert_eq!(offsets.count(), 2); + } + + #[test] + fn test_string_table_read() { + let mut strings = StringTable::default(); + let id1 = strings.add(&b"one"[..]); + let id2 = strings.add(&b"two"[..]); + + let mut debug_str = DebugStr::from(EndianVec::new(LittleEndian)); + let offsets = strings.write(&mut debug_str).unwrap(); + + let read_debug_str = read::DebugStr::new(debug_str.slice(), LittleEndian); + let str1 = read_debug_str.get_str(offsets.get(id1)).unwrap(); + let str2 = read_debug_str.get_str(offsets.get(id2)).unwrap(); + assert_eq!(str1.slice(), &b"one"[..]); + assert_eq!(str2.slice(), &b"two"[..]); + } +} diff --git a/vendor/gimli/src/write/unit.rs b/vendor/gimli/src/write/unit.rs new file mode 100644 index 0000000..dd8ba67 --- /dev/null +++ b/vendor/gimli/src/write/unit.rs @@ -0,0 +1,3152 @@ +use alloc::vec::Vec; +use std::ops::{Deref, DerefMut}; +use std::{slice, usize}; + +use crate::common::{ + DebugAbbrevOffset, DebugInfoOffset, DebugLineOffset, DebugMacinfoOffset, DebugMacroOffset, + DebugStrOffset, DebugTypeSignature, Encoding, Format, SectionId, +}; +use crate::constants; +use crate::leb128::write::{sleb128_size, uleb128_size}; +use crate::write::{ + Abbreviation, AbbreviationTable, Address, AttributeSpecification, BaseId, DebugLineStrOffsets, + DebugStrOffsets, Error, Expression, FileId, LineProgram, LineStringId, LocationListId, + LocationListOffsets, LocationListTable, RangeListId, RangeListOffsets, RangeListTable, + Reference, Result, Section, Sections, StringId, Writer, +}; + +define_id!(UnitId, "An identifier for a unit in a `UnitTable`."); + +define_id!(UnitEntryId, "An identifier for an entry in a `Unit`."); + +/// A table of units that will be stored in the `.debug_info` section. +#[derive(Debug, Default)] +pub struct UnitTable { + base_id: BaseId, + units: Vec<Unit>, +} + +impl UnitTable { + /// Create a new unit and add it to the table. + /// + /// `address_size` must be in bytes. + /// + /// Returns the `UnitId` of the new unit. + #[inline] + pub fn add(&mut self, unit: Unit) -> UnitId { + let id = UnitId::new(self.base_id, self.units.len()); + self.units.push(unit); + id + } + + /// Return the number of units. + #[inline] + pub fn count(&self) -> usize { + self.units.len() + } + + /// Return the id of a unit. + /// + /// # Panics + /// + /// Panics if `index >= self.count()`. + #[inline] + pub fn id(&self, index: usize) -> UnitId { + assert!(index < self.count()); + UnitId::new(self.base_id, index) + } + + /// Get a reference to a unit. + /// + /// # Panics + /// + /// Panics if `id` is invalid. + #[inline] + pub fn get(&self, id: UnitId) -> &Unit { + debug_assert_eq!(self.base_id, id.base_id); + &self.units[id.index] + } + + /// Get a mutable reference to a unit. + /// + /// # Panics + /// + /// Panics if `id` is invalid. + #[inline] + pub fn get_mut(&mut self, id: UnitId) -> &mut Unit { + debug_assert_eq!(self.base_id, id.base_id); + &mut self.units[id.index] + } + + /// Write the units to the given sections. + /// + /// `strings` must contain the `.debug_str` offsets of the corresponding + /// `StringTable`. + pub fn write<W: Writer>( + &mut self, + sections: &mut Sections<W>, + line_strings: &DebugLineStrOffsets, + strings: &DebugStrOffsets, + ) -> Result<DebugInfoOffsets> { + let mut offsets = DebugInfoOffsets { + base_id: self.base_id, + units: Vec::new(), + }; + for unit in &mut self.units { + // TODO: maybe share abbreviation tables + let abbrev_offset = sections.debug_abbrev.offset(); + let mut abbrevs = AbbreviationTable::default(); + + offsets.units.push(unit.write( + sections, + abbrev_offset, + &mut abbrevs, + line_strings, + strings, + )?); + + abbrevs.write(&mut sections.debug_abbrev)?; + } + + write_section_refs( + &mut sections.debug_info_refs, + &mut sections.debug_info.0, + &offsets, + )?; + write_section_refs( + &mut sections.debug_loc_refs, + &mut sections.debug_loc.0, + &offsets, + )?; + write_section_refs( + &mut sections.debug_loclists_refs, + &mut sections.debug_loclists.0, + &offsets, + )?; + + Ok(offsets) + } +} + +fn write_section_refs<W: Writer>( + references: &mut Vec<DebugInfoReference>, + w: &mut W, + offsets: &DebugInfoOffsets, +) -> Result<()> { + for r in references.drain(..) { + let entry_offset = offsets.entry(r.unit, r.entry).0; + debug_assert_ne!(entry_offset, 0); + w.write_offset_at(r.offset, entry_offset, SectionId::DebugInfo, r.size)?; + } + Ok(()) +} + +/// A unit's debugging information. +#[derive(Debug)] +pub struct Unit { + base_id: BaseId, + /// The encoding parameters for this unit. + encoding: Encoding, + /// The line number program for this unit. + pub line_program: LineProgram, + /// A table of range lists used by this unit. + pub ranges: RangeListTable, + /// A table of location lists used by this unit. + pub locations: LocationListTable, + /// All entries in this unit. The order is unrelated to the tree order. + // Requirements: + // - entries form a tree + // - entries can be added in any order + // - entries have a fixed id + // - able to quickly lookup an entry from its id + // Limitations of current implementation: + // - mutable iteration of children is messy due to borrow checker + entries: Vec<DebuggingInformationEntry>, + /// The index of the root entry in entries. + root: UnitEntryId, +} + +impl Unit { + /// Create a new `Unit`. + pub fn new(encoding: Encoding, line_program: LineProgram) -> Self { + let base_id = BaseId::default(); + let ranges = RangeListTable::default(); + let locations = LocationListTable::default(); + let mut entries = Vec::new(); + let root = DebuggingInformationEntry::new( + base_id, + &mut entries, + None, + constants::DW_TAG_compile_unit, + ); + Unit { + base_id, + encoding, + line_program, + ranges, + locations, + entries, + root, + } + } + + /// Return the encoding parameters for this unit. + #[inline] + pub fn encoding(&self) -> Encoding { + self.encoding + } + + /// Return the DWARF version for this unit. + #[inline] + pub fn version(&self) -> u16 { + self.encoding.version + } + + /// Return the address size in bytes for this unit. + #[inline] + pub fn address_size(&self) -> u8 { + self.encoding.address_size + } + + /// Return the DWARF format for this unit. + #[inline] + pub fn format(&self) -> Format { + self.encoding.format + } + + /// Return the number of `DebuggingInformationEntry`s created for this unit. + /// + /// This includes entries that no longer have a parent. + #[inline] + pub fn count(&self) -> usize { + self.entries.len() + } + + /// Return the id of the root entry. + #[inline] + pub fn root(&self) -> UnitEntryId { + self.root + } + + /// Add a new `DebuggingInformationEntry` to this unit and return its id. + /// + /// The `parent` must be within the same unit. + /// + /// # Panics + /// + /// Panics if `parent` is invalid. + #[inline] + pub fn add(&mut self, parent: UnitEntryId, tag: constants::DwTag) -> UnitEntryId { + debug_assert_eq!(self.base_id, parent.base_id); + DebuggingInformationEntry::new(self.base_id, &mut self.entries, Some(parent), tag) + } + + /// Get a reference to an entry. + /// + /// # Panics + /// + /// Panics if `id` is invalid. + #[inline] + pub fn get(&self, id: UnitEntryId) -> &DebuggingInformationEntry { + debug_assert_eq!(self.base_id, id.base_id); + &self.entries[id.index] + } + + /// Get a mutable reference to an entry. + /// + /// # Panics + /// + /// Panics if `id` is invalid. + #[inline] + pub fn get_mut(&mut self, id: UnitEntryId) -> &mut DebuggingInformationEntry { + debug_assert_eq!(self.base_id, id.base_id); + &mut self.entries[id.index] + } + + /// Return true if `self.line_program` is used by a DIE. + fn line_program_in_use(&self) -> bool { + if self.line_program.is_none() { + return false; + } + if !self.line_program.is_empty() { + return true; + } + + for entry in &self.entries { + for attr in &entry.attrs { + if let AttributeValue::FileIndex(Some(_)) = attr.value { + return true; + } + } + } + + false + } + + /// Write the unit to the given sections. + pub(crate) fn write<W: Writer>( + &mut self, + sections: &mut Sections<W>, + abbrev_offset: DebugAbbrevOffset, + abbrevs: &mut AbbreviationTable, + line_strings: &DebugLineStrOffsets, + strings: &DebugStrOffsets, + ) -> Result<UnitOffsets> { + let line_program = if self.line_program_in_use() { + self.entries[self.root.index] + .set(constants::DW_AT_stmt_list, AttributeValue::LineProgramRef); + Some(self.line_program.write( + &mut sections.debug_line, + self.encoding, + line_strings, + strings, + )?) + } else { + self.entries[self.root.index].delete(constants::DW_AT_stmt_list); + None + }; + + // TODO: use .debug_types for type units in DWARF v4. + let w = &mut sections.debug_info; + + let mut offsets = UnitOffsets { + base_id: self.base_id, + unit: w.offset(), + // Entries can be written in any order, so create the complete vec now. + entries: vec![EntryOffset::none(); self.entries.len()], + }; + + let length_offset = w.write_initial_length(self.format())?; + let length_base = w.len(); + + w.write_u16(self.version())?; + if 2 <= self.version() && self.version() <= 4 { + w.write_offset( + abbrev_offset.0, + SectionId::DebugAbbrev, + self.format().word_size(), + )?; + w.write_u8(self.address_size())?; + } else if self.version() == 5 { + w.write_u8(constants::DW_UT_compile.0)?; + w.write_u8(self.address_size())?; + w.write_offset( + abbrev_offset.0, + SectionId::DebugAbbrev, + self.format().word_size(), + )?; + } else { + return Err(Error::UnsupportedVersion(self.version())); + } + + // Calculate all DIE offsets, so that we are able to output references to them. + // However, references to base types in expressions use ULEB128, so base types + // must be moved to the front before we can calculate offsets. + self.reorder_base_types(); + let mut offset = w.len(); + self.entries[self.root.index].calculate_offsets( + self, + &mut offset, + &mut offsets, + abbrevs, + )?; + + let range_lists = self.ranges.write(sections, self.encoding)?; + // Location lists can't be written until we have DIE offsets. + let loc_lists = self + .locations + .write(sections, self.encoding, Some(&offsets))?; + + let w = &mut sections.debug_info; + let mut unit_refs = Vec::new(); + self.entries[self.root.index].write( + w, + &mut sections.debug_info_refs, + &mut unit_refs, + self, + &mut offsets, + line_program, + line_strings, + strings, + &range_lists, + &loc_lists, + )?; + + let length = (w.len() - length_base) as u64; + w.write_initial_length_at(length_offset, length, self.format())?; + + for (offset, entry) in unit_refs { + // This does not need relocation. + w.write_udata_at( + offset.0, + offsets.unit_offset(entry), + self.format().word_size(), + )?; + } + + Ok(offsets) + } + + /// Reorder base types to come first so that typed stack operations + /// can get their offset. + fn reorder_base_types(&mut self) { + let root = &self.entries[self.root.index]; + let mut root_children = Vec::with_capacity(root.children.len()); + for entry in &root.children { + if self.entries[entry.index].tag == constants::DW_TAG_base_type { + root_children.push(*entry); + } + } + for entry in &root.children { + if self.entries[entry.index].tag != constants::DW_TAG_base_type { + root_children.push(*entry); + } + } + self.entries[self.root.index].children = root_children; + } +} + +/// A Debugging Information Entry (DIE). +/// +/// DIEs have a set of attributes and optionally have children DIEs as well. +/// +/// DIEs form a tree without any cycles. This is enforced by specifying the +/// parent when creating a DIE, and disallowing changes of parent. +#[derive(Debug)] +pub struct DebuggingInformationEntry { + id: UnitEntryId, + parent: Option<UnitEntryId>, + tag: constants::DwTag, + /// Whether to emit `DW_AT_sibling`. + sibling: bool, + attrs: Vec<Attribute>, + children: Vec<UnitEntryId>, +} + +impl DebuggingInformationEntry { + /// Create a new `DebuggingInformationEntry`. + /// + /// # Panics + /// + /// Panics if `parent` is invalid. + #[allow(clippy::new_ret_no_self)] + fn new( + base_id: BaseId, + entries: &mut Vec<DebuggingInformationEntry>, + parent: Option<UnitEntryId>, + tag: constants::DwTag, + ) -> UnitEntryId { + let id = UnitEntryId::new(base_id, entries.len()); + entries.push(DebuggingInformationEntry { + id, + parent, + tag, + sibling: false, + attrs: Vec::new(), + children: Vec::new(), + }); + if let Some(parent) = parent { + debug_assert_eq!(base_id, parent.base_id); + assert_ne!(parent, id); + entries[parent.index].children.push(id); + } + id + } + + /// Return the id of this entry. + #[inline] + pub fn id(&self) -> UnitEntryId { + self.id + } + + /// Return the parent of this entry. + #[inline] + pub fn parent(&self) -> Option<UnitEntryId> { + self.parent + } + + /// Return the tag of this entry. + #[inline] + pub fn tag(&self) -> constants::DwTag { + self.tag + } + + /// Return `true` if a `DW_AT_sibling` attribute will be emitted. + #[inline] + pub fn sibling(&self) -> bool { + self.sibling + } + + /// Set whether a `DW_AT_sibling` attribute will be emitted. + /// + /// The attribute will only be emitted if the DIE has children. + #[inline] + pub fn set_sibling(&mut self, sibling: bool) { + self.sibling = sibling; + } + + /// Iterate over the attributes of this entry. + #[inline] + pub fn attrs(&self) -> slice::Iter<Attribute> { + self.attrs.iter() + } + + /// Iterate over the attributes of this entry for modification. + #[inline] + pub fn attrs_mut(&mut self) -> slice::IterMut<Attribute> { + self.attrs.iter_mut() + } + + /// Get an attribute. + pub fn get(&self, name: constants::DwAt) -> Option<&AttributeValue> { + self.attrs + .iter() + .find(|attr| attr.name == name) + .map(|attr| &attr.value) + } + + /// Get an attribute for modification. + pub fn get_mut(&mut self, name: constants::DwAt) -> Option<&mut AttributeValue> { + self.attrs + .iter_mut() + .find(|attr| attr.name == name) + .map(|attr| &mut attr.value) + } + + /// Set an attribute. + /// + /// Replaces any existing attribute with the same name. + /// + /// # Panics + /// + /// Panics if `name` is `DW_AT_sibling`. Use `set_sibling` instead. + pub fn set(&mut self, name: constants::DwAt, value: AttributeValue) { + assert_ne!(name, constants::DW_AT_sibling); + if let Some(attr) = self.attrs.iter_mut().find(|attr| attr.name == name) { + attr.value = value; + return; + } + self.attrs.push(Attribute { name, value }); + } + + /// Delete an attribute. + /// + /// Replaces any existing attribute with the same name. + pub fn delete(&mut self, name: constants::DwAt) { + self.attrs.retain(|x| x.name != name); + } + + /// Iterate over the children of this entry. + /// + /// Note: use `Unit::add` to add a new child to this entry. + #[inline] + pub fn children(&self) -> slice::Iter<UnitEntryId> { + self.children.iter() + } + + /// Delete a child entry and all of its children. + pub fn delete_child(&mut self, id: UnitEntryId) { + self.children.retain(|&child| child != id); + } + + /// Return the type abbreviation for this DIE. + fn abbreviation(&self, encoding: Encoding) -> Result<Abbreviation> { + let mut attrs = Vec::new(); + + if self.sibling && !self.children.is_empty() { + let form = match encoding.format { + Format::Dwarf32 => constants::DW_FORM_ref4, + Format::Dwarf64 => constants::DW_FORM_ref8, + }; + attrs.push(AttributeSpecification::new(constants::DW_AT_sibling, form)); + } + + for attr in &self.attrs { + attrs.push(attr.specification(encoding)?); + } + + Ok(Abbreviation::new( + self.tag, + !self.children.is_empty(), + attrs, + )) + } + + fn calculate_offsets( + &self, + unit: &Unit, + offset: &mut usize, + offsets: &mut UnitOffsets, + abbrevs: &mut AbbreviationTable, + ) -> Result<()> { + offsets.entries[self.id.index].offset = DebugInfoOffset(*offset); + offsets.entries[self.id.index].abbrev = abbrevs.add(self.abbreviation(unit.encoding())?); + *offset += self.size(unit, offsets); + if !self.children.is_empty() { + for child in &self.children { + unit.entries[child.index].calculate_offsets(unit, offset, offsets, abbrevs)?; + } + // Null child + *offset += 1; + } + Ok(()) + } + + fn size(&self, unit: &Unit, offsets: &UnitOffsets) -> usize { + let mut size = uleb128_size(offsets.abbrev(self.id)); + if self.sibling && !self.children.is_empty() { + size += unit.format().word_size() as usize; + } + for attr in &self.attrs { + size += attr.value.size(unit, offsets); + } + size + } + + /// Write the entry to the given sections. + fn write<W: Writer>( + &self, + w: &mut DebugInfo<W>, + debug_info_refs: &mut Vec<DebugInfoReference>, + unit_refs: &mut Vec<(DebugInfoOffset, UnitEntryId)>, + unit: &Unit, + offsets: &mut UnitOffsets, + line_program: Option<DebugLineOffset>, + line_strings: &DebugLineStrOffsets, + strings: &DebugStrOffsets, + range_lists: &RangeListOffsets, + loc_lists: &LocationListOffsets, + ) -> Result<()> { + debug_assert_eq!(offsets.debug_info_offset(self.id), w.offset()); + w.write_uleb128(offsets.abbrev(self.id))?; + + let sibling_offset = if self.sibling && !self.children.is_empty() { + let offset = w.offset(); + w.write_udata(0, unit.format().word_size())?; + Some(offset) + } else { + None + }; + + for attr in &self.attrs { + attr.value.write( + w, + debug_info_refs, + unit_refs, + unit, + offsets, + line_program, + line_strings, + strings, + range_lists, + loc_lists, + )?; + } + + if !self.children.is_empty() { + for child in &self.children { + unit.entries[child.index].write( + w, + debug_info_refs, + unit_refs, + unit, + offsets, + line_program, + line_strings, + strings, + range_lists, + loc_lists, + )?; + } + // Null child + w.write_u8(0)?; + } + + if let Some(offset) = sibling_offset { + let next_offset = (w.offset().0 - offsets.unit.0) as u64; + // This does not need relocation. + w.write_udata_at(offset.0, next_offset, unit.format().word_size())?; + } + Ok(()) + } +} + +/// An attribute in a `DebuggingInformationEntry`, consisting of a name and +/// associated value. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Attribute { + name: constants::DwAt, + value: AttributeValue, +} + +impl Attribute { + /// Get the name of this attribute. + #[inline] + pub fn name(&self) -> constants::DwAt { + self.name + } + + /// Get the value of this attribute. + #[inline] + pub fn get(&self) -> &AttributeValue { + &self.value + } + + /// Set the value of this attribute. + #[inline] + pub fn set(&mut self, value: AttributeValue) { + self.value = value; + } + + /// Return the type specification for this attribute. + fn specification(&self, encoding: Encoding) -> Result<AttributeSpecification> { + Ok(AttributeSpecification::new( + self.name, + self.value.form(encoding)?, + )) + } +} + +/// The value of an attribute in a `DebuggingInformationEntry`. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum AttributeValue { + /// "Refers to some location in the address space of the described program." + Address(Address), + + /// A slice of an arbitrary number of bytes. + Block(Vec<u8>), + + /// A one byte constant data value. How to interpret the byte depends on context. + /// + /// From section 7 of the standard: "Depending on context, it may be a + /// signed integer, an unsigned integer, a floating-point constant, or + /// anything else." + Data1(u8), + + /// A two byte constant data value. How to interpret the bytes depends on context. + /// + /// This value will be converted to the target endian before writing. + /// + /// From section 7 of the standard: "Depending on context, it may be a + /// signed integer, an unsigned integer, a floating-point constant, or + /// anything else." + Data2(u16), + + /// A four byte constant data value. How to interpret the bytes depends on context. + /// + /// This value will be converted to the target endian before writing. + /// + /// From section 7 of the standard: "Depending on context, it may be a + /// signed integer, an unsigned integer, a floating-point constant, or + /// anything else." + Data4(u32), + + /// An eight byte constant data value. How to interpret the bytes depends on context. + /// + /// This value will be converted to the target endian before writing. + /// + /// From section 7 of the standard: "Depending on context, it may be a + /// signed integer, an unsigned integer, a floating-point constant, or + /// anything else." + Data8(u64), + + /// A signed integer constant. + Sdata(i64), + + /// An unsigned integer constant. + Udata(u64), + + /// "The information bytes contain a DWARF expression (see Section 2.5) or + /// location description (see Section 2.6)." + Exprloc(Expression), + + /// A boolean that indicates presence or absence of the attribute. + Flag(bool), + + /// An attribute that is always present. + FlagPresent, + + /// A reference to a `DebuggingInformationEntry` in this unit. + UnitRef(UnitEntryId), + + /// A reference to a `DebuggingInformationEntry` in a potentially different unit. + DebugInfoRef(Reference), + + /// An offset into the `.debug_info` section of the supplementary object file. + /// + /// The API does not currently assist with generating this offset. + /// This variant will be removed from the API once support for writing + /// supplementary object files is implemented. + DebugInfoRefSup(DebugInfoOffset), + + /// A reference to a line number program. + LineProgramRef, + + /// A reference to a location list. + LocationListRef(LocationListId), + + /// An offset into the `.debug_macinfo` section. + /// + /// The API does not currently assist with generating this offset. + /// This variant will be removed from the API once support for writing + /// `.debug_macinfo` sections is implemented. + DebugMacinfoRef(DebugMacinfoOffset), + + /// An offset into the `.debug_macro` section. + /// + /// The API does not currently assist with generating this offset. + /// This variant will be removed from the API once support for writing + /// `.debug_macro` sections is implemented. + DebugMacroRef(DebugMacroOffset), + + /// A reference to a range list. + RangeListRef(RangeListId), + + /// A type signature. + /// + /// The API does not currently assist with generating this signature. + /// This variant will be removed from the API once support for writing + /// `.debug_types` sections is implemented. + DebugTypesRef(DebugTypeSignature), + + /// A reference to a string in the `.debug_str` section. + StringRef(StringId), + + /// An offset into the `.debug_str` section of the supplementary object file. + /// + /// The API does not currently assist with generating this offset. + /// This variant will be removed from the API once support for writing + /// supplementary object files is implemented. + DebugStrRefSup(DebugStrOffset), + + /// A reference to a string in the `.debug_line_str` section. + LineStringRef(LineStringId), + + /// A slice of bytes representing a string. Must not include null bytes. + /// Not guaranteed to be UTF-8 or anything like that. + String(Vec<u8>), + + /// The value of a `DW_AT_encoding` attribute. + Encoding(constants::DwAte), + + /// The value of a `DW_AT_decimal_sign` attribute. + DecimalSign(constants::DwDs), + + /// The value of a `DW_AT_endianity` attribute. + Endianity(constants::DwEnd), + + /// The value of a `DW_AT_accessibility` attribute. + Accessibility(constants::DwAccess), + + /// The value of a `DW_AT_visibility` attribute. + Visibility(constants::DwVis), + + /// The value of a `DW_AT_virtuality` attribute. + Virtuality(constants::DwVirtuality), + + /// The value of a `DW_AT_language` attribute. + Language(constants::DwLang), + + /// The value of a `DW_AT_address_class` attribute. + AddressClass(constants::DwAddr), + + /// The value of a `DW_AT_identifier_case` attribute. + IdentifierCase(constants::DwId), + + /// The value of a `DW_AT_calling_convention` attribute. + CallingConvention(constants::DwCc), + + /// The value of a `DW_AT_inline` attribute. + Inline(constants::DwInl), + + /// The value of a `DW_AT_ordering` attribute. + Ordering(constants::DwOrd), + + /// An index into the filename entries from the line number information + /// table for the unit containing this value. + FileIndex(Option<FileId>), +} + +impl AttributeValue { + /// Return the form that will be used to encode this value. + pub fn form(&self, encoding: Encoding) -> Result<constants::DwForm> { + // TODO: missing forms: + // - DW_FORM_indirect + // - DW_FORM_implicit_const + // - FW_FORM_block1/block2/block4 + // - DW_FORM_str/strx1/strx2/strx3/strx4 + // - DW_FORM_addrx/addrx1/addrx2/addrx3/addrx4 + // - DW_FORM_data16 + // - DW_FORM_line_strp + // - DW_FORM_loclistx + // - DW_FORM_rnglistx + let form = match *self { + AttributeValue::Address(_) => constants::DW_FORM_addr, + AttributeValue::Block(_) => constants::DW_FORM_block, + AttributeValue::Data1(_) => constants::DW_FORM_data1, + AttributeValue::Data2(_) => constants::DW_FORM_data2, + AttributeValue::Data4(_) => constants::DW_FORM_data4, + AttributeValue::Data8(_) => constants::DW_FORM_data8, + AttributeValue::Exprloc(_) => constants::DW_FORM_exprloc, + AttributeValue::Flag(_) => constants::DW_FORM_flag, + AttributeValue::FlagPresent => constants::DW_FORM_flag_present, + AttributeValue::UnitRef(_) => { + // Using a fixed size format lets us write a placeholder before we know + // the value. + match encoding.format { + Format::Dwarf32 => constants::DW_FORM_ref4, + Format::Dwarf64 => constants::DW_FORM_ref8, + } + } + AttributeValue::DebugInfoRef(_) => constants::DW_FORM_ref_addr, + AttributeValue::DebugInfoRefSup(_) => { + // TODO: should this depend on the size of supplementary section? + match encoding.format { + Format::Dwarf32 => constants::DW_FORM_ref_sup4, + Format::Dwarf64 => constants::DW_FORM_ref_sup8, + } + } + AttributeValue::LineProgramRef + | AttributeValue::LocationListRef(_) + | AttributeValue::DebugMacinfoRef(_) + | AttributeValue::DebugMacroRef(_) + | AttributeValue::RangeListRef(_) => { + if encoding.version == 2 || encoding.version == 3 { + match encoding.format { + Format::Dwarf32 => constants::DW_FORM_data4, + Format::Dwarf64 => constants::DW_FORM_data8, + } + } else { + constants::DW_FORM_sec_offset + } + } + AttributeValue::DebugTypesRef(_) => constants::DW_FORM_ref_sig8, + AttributeValue::StringRef(_) => constants::DW_FORM_strp, + AttributeValue::DebugStrRefSup(_) => constants::DW_FORM_strp_sup, + AttributeValue::LineStringRef(_) => constants::DW_FORM_line_strp, + AttributeValue::String(_) => constants::DW_FORM_string, + AttributeValue::Encoding(_) + | AttributeValue::DecimalSign(_) + | AttributeValue::Endianity(_) + | AttributeValue::Accessibility(_) + | AttributeValue::Visibility(_) + | AttributeValue::Virtuality(_) + | AttributeValue::Language(_) + | AttributeValue::AddressClass(_) + | AttributeValue::IdentifierCase(_) + | AttributeValue::CallingConvention(_) + | AttributeValue::Inline(_) + | AttributeValue::Ordering(_) + | AttributeValue::FileIndex(_) + | AttributeValue::Udata(_) => constants::DW_FORM_udata, + AttributeValue::Sdata(_) => constants::DW_FORM_sdata, + }; + Ok(form) + } + + fn size(&self, unit: &Unit, offsets: &UnitOffsets) -> usize { + macro_rules! debug_assert_form { + ($form:expr) => { + debug_assert_eq!(self.form(unit.encoding()).unwrap(), $form) + }; + } + match *self { + AttributeValue::Address(_) => { + debug_assert_form!(constants::DW_FORM_addr); + unit.address_size() as usize + } + AttributeValue::Block(ref val) => { + debug_assert_form!(constants::DW_FORM_block); + uleb128_size(val.len() as u64) + val.len() + } + AttributeValue::Data1(_) => { + debug_assert_form!(constants::DW_FORM_data1); + 1 + } + AttributeValue::Data2(_) => { + debug_assert_form!(constants::DW_FORM_data2); + 2 + } + AttributeValue::Data4(_) => { + debug_assert_form!(constants::DW_FORM_data4); + 4 + } + AttributeValue::Data8(_) => { + debug_assert_form!(constants::DW_FORM_data8); + 8 + } + AttributeValue::Sdata(val) => { + debug_assert_form!(constants::DW_FORM_sdata); + sleb128_size(val) + } + AttributeValue::Udata(val) => { + debug_assert_form!(constants::DW_FORM_udata); + uleb128_size(val) + } + AttributeValue::Exprloc(ref val) => { + debug_assert_form!(constants::DW_FORM_exprloc); + let size = val.size(unit.encoding(), Some(offsets)); + uleb128_size(size as u64) + size + } + AttributeValue::Flag(_) => { + debug_assert_form!(constants::DW_FORM_flag); + 1 + } + AttributeValue::FlagPresent => { + debug_assert_form!(constants::DW_FORM_flag_present); + 0 + } + AttributeValue::UnitRef(_) => { + match unit.format() { + Format::Dwarf32 => debug_assert_form!(constants::DW_FORM_ref4), + Format::Dwarf64 => debug_assert_form!(constants::DW_FORM_ref8), + } + unit.format().word_size() as usize + } + AttributeValue::DebugInfoRef(_) => { + debug_assert_form!(constants::DW_FORM_ref_addr); + if unit.version() == 2 { + unit.address_size() as usize + } else { + unit.format().word_size() as usize + } + } + AttributeValue::DebugInfoRefSup(_) => { + match unit.format() { + Format::Dwarf32 => debug_assert_form!(constants::DW_FORM_ref_sup4), + Format::Dwarf64 => debug_assert_form!(constants::DW_FORM_ref_sup8), + } + unit.format().word_size() as usize + } + AttributeValue::LineProgramRef => { + if unit.version() >= 4 { + debug_assert_form!(constants::DW_FORM_sec_offset); + } + unit.format().word_size() as usize + } + AttributeValue::LocationListRef(_) => { + if unit.version() >= 4 { + debug_assert_form!(constants::DW_FORM_sec_offset); + } + unit.format().word_size() as usize + } + AttributeValue::DebugMacinfoRef(_) => { + if unit.version() >= 4 { + debug_assert_form!(constants::DW_FORM_sec_offset); + } + unit.format().word_size() as usize + } + AttributeValue::DebugMacroRef(_) => { + if unit.version() >= 4 { + debug_assert_form!(constants::DW_FORM_sec_offset); + } + unit.format().word_size() as usize + } + AttributeValue::RangeListRef(_) => { + if unit.version() >= 4 { + debug_assert_form!(constants::DW_FORM_sec_offset); + } + unit.format().word_size() as usize + } + AttributeValue::DebugTypesRef(_) => { + debug_assert_form!(constants::DW_FORM_ref_sig8); + 8 + } + AttributeValue::StringRef(_) => { + debug_assert_form!(constants::DW_FORM_strp); + unit.format().word_size() as usize + } + AttributeValue::DebugStrRefSup(_) => { + debug_assert_form!(constants::DW_FORM_strp_sup); + unit.format().word_size() as usize + } + AttributeValue::LineStringRef(_) => { + debug_assert_form!(constants::DW_FORM_line_strp); + unit.format().word_size() as usize + } + AttributeValue::String(ref val) => { + debug_assert_form!(constants::DW_FORM_string); + val.len() + 1 + } + AttributeValue::Encoding(val) => { + debug_assert_form!(constants::DW_FORM_udata); + uleb128_size(val.0 as u64) + } + AttributeValue::DecimalSign(val) => { + debug_assert_form!(constants::DW_FORM_udata); + uleb128_size(val.0 as u64) + } + AttributeValue::Endianity(val) => { + debug_assert_form!(constants::DW_FORM_udata); + uleb128_size(val.0 as u64) + } + AttributeValue::Accessibility(val) => { + debug_assert_form!(constants::DW_FORM_udata); + uleb128_size(val.0 as u64) + } + AttributeValue::Visibility(val) => { + debug_assert_form!(constants::DW_FORM_udata); + uleb128_size(val.0 as u64) + } + AttributeValue::Virtuality(val) => { + debug_assert_form!(constants::DW_FORM_udata); + uleb128_size(val.0 as u64) + } + AttributeValue::Language(val) => { + debug_assert_form!(constants::DW_FORM_udata); + uleb128_size(val.0 as u64) + } + AttributeValue::AddressClass(val) => { + debug_assert_form!(constants::DW_FORM_udata); + uleb128_size(val.0 as u64) + } + AttributeValue::IdentifierCase(val) => { + debug_assert_form!(constants::DW_FORM_udata); + uleb128_size(val.0 as u64) + } + AttributeValue::CallingConvention(val) => { + debug_assert_form!(constants::DW_FORM_udata); + uleb128_size(val.0 as u64) + } + AttributeValue::Inline(val) => { + debug_assert_form!(constants::DW_FORM_udata); + uleb128_size(val.0 as u64) + } + AttributeValue::Ordering(val) => { + debug_assert_form!(constants::DW_FORM_udata); + uleb128_size(val.0 as u64) + } + AttributeValue::FileIndex(val) => { + debug_assert_form!(constants::DW_FORM_udata); + uleb128_size(val.map(FileId::raw).unwrap_or(0)) + } + } + } + + /// Write the attribute value to the given sections. + fn write<W: Writer>( + &self, + w: &mut DebugInfo<W>, + debug_info_refs: &mut Vec<DebugInfoReference>, + unit_refs: &mut Vec<(DebugInfoOffset, UnitEntryId)>, + unit: &Unit, + offsets: &UnitOffsets, + line_program: Option<DebugLineOffset>, + line_strings: &DebugLineStrOffsets, + strings: &DebugStrOffsets, + range_lists: &RangeListOffsets, + loc_lists: &LocationListOffsets, + ) -> Result<()> { + macro_rules! debug_assert_form { + ($form:expr) => { + debug_assert_eq!(self.form(unit.encoding()).unwrap(), $form) + }; + } + match *self { + AttributeValue::Address(val) => { + debug_assert_form!(constants::DW_FORM_addr); + w.write_address(val, unit.address_size())?; + } + AttributeValue::Block(ref val) => { + debug_assert_form!(constants::DW_FORM_block); + w.write_uleb128(val.len() as u64)?; + w.write(val)?; + } + AttributeValue::Data1(val) => { + debug_assert_form!(constants::DW_FORM_data1); + w.write_u8(val)?; + } + AttributeValue::Data2(val) => { + debug_assert_form!(constants::DW_FORM_data2); + w.write_u16(val)?; + } + AttributeValue::Data4(val) => { + debug_assert_form!(constants::DW_FORM_data4); + w.write_u32(val)?; + } + AttributeValue::Data8(val) => { + debug_assert_form!(constants::DW_FORM_data8); + w.write_u64(val)?; + } + AttributeValue::Sdata(val) => { + debug_assert_form!(constants::DW_FORM_sdata); + w.write_sleb128(val)?; + } + AttributeValue::Udata(val) => { + debug_assert_form!(constants::DW_FORM_udata); + w.write_uleb128(val)?; + } + AttributeValue::Exprloc(ref val) => { + debug_assert_form!(constants::DW_FORM_exprloc); + w.write_uleb128(val.size(unit.encoding(), Some(offsets)) as u64)?; + val.write( + &mut w.0, + Some(debug_info_refs), + unit.encoding(), + Some(offsets), + )?; + } + AttributeValue::Flag(val) => { + debug_assert_form!(constants::DW_FORM_flag); + w.write_u8(val as u8)?; + } + AttributeValue::FlagPresent => { + debug_assert_form!(constants::DW_FORM_flag_present); + } + AttributeValue::UnitRef(id) => { + match unit.format() { + Format::Dwarf32 => debug_assert_form!(constants::DW_FORM_ref4), + Format::Dwarf64 => debug_assert_form!(constants::DW_FORM_ref8), + } + unit_refs.push((w.offset(), id)); + w.write_udata(0, unit.format().word_size())?; + } + AttributeValue::DebugInfoRef(reference) => { + debug_assert_form!(constants::DW_FORM_ref_addr); + let size = if unit.version() == 2 { + unit.address_size() + } else { + unit.format().word_size() + }; + match reference { + Reference::Symbol(symbol) => w.write_reference(symbol, size)?, + Reference::Entry(unit, entry) => { + debug_info_refs.push(DebugInfoReference { + offset: w.len(), + unit, + entry, + size, + }); + w.write_udata(0, size)?; + } + } + } + AttributeValue::DebugInfoRefSup(val) => { + match unit.format() { + Format::Dwarf32 => debug_assert_form!(constants::DW_FORM_ref_sup4), + Format::Dwarf64 => debug_assert_form!(constants::DW_FORM_ref_sup8), + } + w.write_udata(val.0 as u64, unit.format().word_size())?; + } + AttributeValue::LineProgramRef => { + if unit.version() >= 4 { + debug_assert_form!(constants::DW_FORM_sec_offset); + } + match line_program { + Some(line_program) => { + w.write_offset( + line_program.0, + SectionId::DebugLine, + unit.format().word_size(), + )?; + } + None => return Err(Error::InvalidAttributeValue), + } + } + AttributeValue::LocationListRef(val) => { + if unit.version() >= 4 { + debug_assert_form!(constants::DW_FORM_sec_offset); + } + let section = if unit.version() <= 4 { + SectionId::DebugLoc + } else { + SectionId::DebugLocLists + }; + w.write_offset(loc_lists.get(val).0, section, unit.format().word_size())?; + } + AttributeValue::DebugMacinfoRef(val) => { + if unit.version() >= 4 { + debug_assert_form!(constants::DW_FORM_sec_offset); + } + w.write_offset(val.0, SectionId::DebugMacinfo, unit.format().word_size())?; + } + AttributeValue::DebugMacroRef(val) => { + if unit.version() >= 4 { + debug_assert_form!(constants::DW_FORM_sec_offset); + } + w.write_offset(val.0, SectionId::DebugMacro, unit.format().word_size())?; + } + AttributeValue::RangeListRef(val) => { + if unit.version() >= 4 { + debug_assert_form!(constants::DW_FORM_sec_offset); + } + let section = if unit.version() <= 4 { + SectionId::DebugRanges + } else { + SectionId::DebugRngLists + }; + w.write_offset(range_lists.get(val).0, section, unit.format().word_size())?; + } + AttributeValue::DebugTypesRef(val) => { + debug_assert_form!(constants::DW_FORM_ref_sig8); + w.write_u64(val.0)?; + } + AttributeValue::StringRef(val) => { + debug_assert_form!(constants::DW_FORM_strp); + w.write_offset( + strings.get(val).0, + SectionId::DebugStr, + unit.format().word_size(), + )?; + } + AttributeValue::DebugStrRefSup(val) => { + debug_assert_form!(constants::DW_FORM_strp_sup); + w.write_udata(val.0 as u64, unit.format().word_size())?; + } + AttributeValue::LineStringRef(val) => { + debug_assert_form!(constants::DW_FORM_line_strp); + w.write_offset( + line_strings.get(val).0, + SectionId::DebugLineStr, + unit.format().word_size(), + )?; + } + AttributeValue::String(ref val) => { + debug_assert_form!(constants::DW_FORM_string); + w.write(val)?; + w.write_u8(0)?; + } + AttributeValue::Encoding(val) => { + debug_assert_form!(constants::DW_FORM_udata); + w.write_uleb128(u64::from(val.0))?; + } + AttributeValue::DecimalSign(val) => { + debug_assert_form!(constants::DW_FORM_udata); + w.write_uleb128(u64::from(val.0))?; + } + AttributeValue::Endianity(val) => { + debug_assert_form!(constants::DW_FORM_udata); + w.write_uleb128(u64::from(val.0))?; + } + AttributeValue::Accessibility(val) => { + debug_assert_form!(constants::DW_FORM_udata); + w.write_uleb128(u64::from(val.0))?; + } + AttributeValue::Visibility(val) => { + debug_assert_form!(constants::DW_FORM_udata); + w.write_uleb128(u64::from(val.0))?; + } + AttributeValue::Virtuality(val) => { + debug_assert_form!(constants::DW_FORM_udata); + w.write_uleb128(u64::from(val.0))?; + } + AttributeValue::Language(val) => { + debug_assert_form!(constants::DW_FORM_udata); + w.write_uleb128(u64::from(val.0))?; + } + AttributeValue::AddressClass(val) => { + debug_assert_form!(constants::DW_FORM_udata); + w.write_uleb128(val.0)?; + } + AttributeValue::IdentifierCase(val) => { + debug_assert_form!(constants::DW_FORM_udata); + w.write_uleb128(u64::from(val.0))?; + } + AttributeValue::CallingConvention(val) => { + debug_assert_form!(constants::DW_FORM_udata); + w.write_uleb128(u64::from(val.0))?; + } + AttributeValue::Inline(val) => { + debug_assert_form!(constants::DW_FORM_udata); + w.write_uleb128(u64::from(val.0))?; + } + AttributeValue::Ordering(val) => { + debug_assert_form!(constants::DW_FORM_udata); + w.write_uleb128(u64::from(val.0))?; + } + AttributeValue::FileIndex(val) => { + debug_assert_form!(constants::DW_FORM_udata); + w.write_uleb128(val.map(FileId::raw).unwrap_or(0))?; + } + } + Ok(()) + } +} + +define_section!( + DebugInfo, + DebugInfoOffset, + "A writable `.debug_info` section." +); + +/// The section offsets of all elements within a `.debug_info` section. +#[derive(Debug, Default)] +pub struct DebugInfoOffsets { + base_id: BaseId, + units: Vec<UnitOffsets>, +} + +impl DebugInfoOffsets { + #[cfg(test)] + #[cfg(feature = "read")] + pub(crate) fn unit_offsets(&self, unit: UnitId) -> &UnitOffsets { + debug_assert_eq!(self.base_id, unit.base_id); + &self.units[unit.index] + } + + /// Get the `.debug_info` section offset for the given unit. + #[inline] + pub fn unit(&self, unit: UnitId) -> DebugInfoOffset { + debug_assert_eq!(self.base_id, unit.base_id); + self.units[unit.index].unit + } + + /// Get the `.debug_info` section offset for the given entry. + #[inline] + pub fn entry(&self, unit: UnitId, entry: UnitEntryId) -> DebugInfoOffset { + debug_assert_eq!(self.base_id, unit.base_id); + self.units[unit.index].debug_info_offset(entry) + } +} + +/// The section offsets of all elements of a unit within a `.debug_info` section. +#[derive(Debug)] +pub(crate) struct UnitOffsets { + base_id: BaseId, + unit: DebugInfoOffset, + entries: Vec<EntryOffset>, +} + +impl UnitOffsets { + #[cfg(test)] + #[cfg(feature = "read")] + fn none() -> Self { + UnitOffsets { + base_id: BaseId::default(), + unit: DebugInfoOffset(0), + entries: Vec::new(), + } + } + + /// Get the .debug_info offset for the given entry. + #[inline] + pub(crate) fn debug_info_offset(&self, entry: UnitEntryId) -> DebugInfoOffset { + debug_assert_eq!(self.base_id, entry.base_id); + let offset = self.entries[entry.index].offset; + debug_assert_ne!(offset.0, 0); + offset + } + + /// Get the unit offset for the given entry. + #[inline] + pub(crate) fn unit_offset(&self, entry: UnitEntryId) -> u64 { + let offset = self.debug_info_offset(entry); + (offset.0 - self.unit.0) as u64 + } + + /// Get the abbreviation code for the given entry. + #[inline] + pub(crate) fn abbrev(&self, entry: UnitEntryId) -> u64 { + debug_assert_eq!(self.base_id, entry.base_id); + self.entries[entry.index].abbrev + } +} + +#[derive(Debug, Clone, Copy)] +pub(crate) struct EntryOffset { + offset: DebugInfoOffset, + abbrev: u64, +} + +impl EntryOffset { + fn none() -> Self { + EntryOffset { + offset: DebugInfoOffset(0), + abbrev: 0, + } + } +} + +/// A reference to a `.debug_info` entry that has yet to be resolved. +#[derive(Debug, Clone, Copy)] +pub(crate) struct DebugInfoReference { + /// The offset within the section of the reference. + pub offset: usize, + /// The size of the reference. + pub size: u8, + /// The unit containing the entry. + pub unit: UnitId, + /// The entry being referenced. + pub entry: UnitEntryId, +} + +#[cfg(feature = "read")] +pub(crate) mod convert { + use super::*; + use crate::common::{DwoId, UnitSectionOffset}; + use crate::read::{self, Reader}; + use crate::write::{self, ConvertError, ConvertResult, LocationList, RangeList}; + use std::collections::HashMap; + + pub(crate) struct ConvertUnit<R: Reader<Offset = usize>> { + from_unit: read::Unit<R>, + base_id: BaseId, + encoding: Encoding, + entries: Vec<DebuggingInformationEntry>, + entry_offsets: Vec<read::UnitOffset>, + root: UnitEntryId, + } + + pub(crate) struct ConvertUnitContext<'a, R: Reader<Offset = usize>> { + pub dwarf: &'a read::Dwarf<R>, + pub unit: &'a read::Unit<R>, + pub line_strings: &'a mut write::LineStringTable, + pub strings: &'a mut write::StringTable, + pub ranges: &'a mut write::RangeListTable, + pub locations: &'a mut write::LocationListTable, + pub convert_address: &'a dyn Fn(u64) -> Option<Address>, + pub base_address: Address, + pub line_program_offset: Option<DebugLineOffset>, + pub line_program_files: Vec<FileId>, + pub entry_ids: &'a HashMap<UnitSectionOffset, (UnitId, UnitEntryId)>, + } + + impl UnitTable { + /// Create a unit table by reading the data in the given sections. + /// + /// This also updates the given tables with the values that are referenced from + /// attributes in this section. + /// + /// `convert_address` is a function to convert read addresses into the `Address` + /// type. For non-relocatable addresses, this function may simply return + /// `Address::Constant(address)`. For relocatable addresses, it is the caller's + /// responsibility to determine the symbol and addend corresponding to the address + /// and return `Address::Symbol { symbol, addend }`. + pub fn from<R: Reader<Offset = usize>>( + dwarf: &read::Dwarf<R>, + line_strings: &mut write::LineStringTable, + strings: &mut write::StringTable, + convert_address: &dyn Fn(u64) -> Option<Address>, + ) -> ConvertResult<UnitTable> { + let base_id = BaseId::default(); + let mut unit_entries = Vec::new(); + let mut entry_ids = HashMap::new(); + + let mut from_units = dwarf.units(); + while let Some(from_unit) = from_units.next()? { + let unit_id = UnitId::new(base_id, unit_entries.len()); + unit_entries.push(Unit::convert_entries( + from_unit, + unit_id, + &mut entry_ids, + dwarf, + )?); + } + + // Attributes must be converted in a separate pass so that we can handle + // references to other compilation units. + let mut units = Vec::new(); + for unit_entries in unit_entries.drain(..) { + units.push(Unit::convert_attributes( + unit_entries, + &entry_ids, + dwarf, + line_strings, + strings, + convert_address, + )?); + } + + Ok(UnitTable { base_id, units }) + } + } + + impl Unit { + /// Create a unit by reading the data in the input sections. + /// + /// Does not add entry attributes. + pub(crate) fn convert_entries<R: Reader<Offset = usize>>( + from_header: read::UnitHeader<R>, + unit_id: UnitId, + entry_ids: &mut HashMap<UnitSectionOffset, (UnitId, UnitEntryId)>, + dwarf: &read::Dwarf<R>, + ) -> ConvertResult<ConvertUnit<R>> { + match from_header.type_() { + read::UnitType::Compilation => (), + _ => return Err(ConvertError::UnsupportedUnitType), + } + let base_id = BaseId::default(); + + let from_unit = dwarf.unit(from_header)?; + let encoding = from_unit.encoding(); + + let mut entries = Vec::new(); + let mut entry_offsets = Vec::new(); + + let mut from_tree = from_unit.entries_tree(None)?; + let from_root = from_tree.root()?; + let root = DebuggingInformationEntry::convert_entry( + from_root, + &from_unit, + base_id, + &mut entries, + &mut entry_offsets, + entry_ids, + None, + unit_id, + )?; + + Ok(ConvertUnit { + from_unit, + base_id, + encoding, + entries, + entry_offsets, + root, + }) + } + + /// Create entry attributes by reading the data in the input sections. + fn convert_attributes<R: Reader<Offset = usize>>( + unit: ConvertUnit<R>, + entry_ids: &HashMap<UnitSectionOffset, (UnitId, UnitEntryId)>, + dwarf: &read::Dwarf<R>, + line_strings: &mut write::LineStringTable, + strings: &mut write::StringTable, + convert_address: &dyn Fn(u64) -> Option<Address>, + ) -> ConvertResult<Unit> { + let from_unit = unit.from_unit; + let base_address = + convert_address(from_unit.low_pc).ok_or(ConvertError::InvalidAddress)?; + + let (line_program_offset, line_program, line_program_files) = + match from_unit.line_program { + Some(ref from_program) => { + let from_program = from_program.clone(); + let line_program_offset = from_program.header().offset(); + let (line_program, line_program_files) = LineProgram::from( + from_program, + dwarf, + line_strings, + strings, + convert_address, + )?; + (Some(line_program_offset), line_program, line_program_files) + } + None => (None, LineProgram::none(), Vec::new()), + }; + + let mut ranges = RangeListTable::default(); + let mut locations = LocationListTable::default(); + + let mut context = ConvertUnitContext { + entry_ids, + dwarf, + unit: &from_unit, + line_strings, + strings, + ranges: &mut ranges, + locations: &mut locations, + convert_address, + base_address, + line_program_offset, + line_program_files, + }; + + let mut entries = unit.entries; + for entry in &mut entries { + entry.convert_attributes(&mut context, &unit.entry_offsets)?; + } + + Ok(Unit { + base_id: unit.base_id, + encoding: unit.encoding, + line_program, + ranges, + locations, + entries, + root: unit.root, + }) + } + } + + impl DebuggingInformationEntry { + /// Create an entry by reading the data in the input sections. + /// + /// Does not add the entry attributes. + fn convert_entry<R: Reader<Offset = usize>>( + from: read::EntriesTreeNode<R>, + from_unit: &read::Unit<R>, + base_id: BaseId, + entries: &mut Vec<DebuggingInformationEntry>, + entry_offsets: &mut Vec<read::UnitOffset>, + entry_ids: &mut HashMap<UnitSectionOffset, (UnitId, UnitEntryId)>, + parent: Option<UnitEntryId>, + unit_id: UnitId, + ) -> ConvertResult<UnitEntryId> { + let from_entry = from.entry(); + let id = DebuggingInformationEntry::new(base_id, entries, parent, from_entry.tag()); + let offset = from_entry.offset(); + entry_offsets.push(offset); + entry_ids.insert(offset.to_unit_section_offset(from_unit), (unit_id, id)); + + let mut from_children = from.children(); + while let Some(from_child) = from_children.next()? { + DebuggingInformationEntry::convert_entry( + from_child, + from_unit, + base_id, + entries, + entry_offsets, + entry_ids, + Some(id), + unit_id, + )?; + } + Ok(id) + } + + /// Create an entry's attributes by reading the data in the input sections. + fn convert_attributes<R: Reader<Offset = usize>>( + &mut self, + context: &mut ConvertUnitContext<R>, + entry_offsets: &[read::UnitOffset], + ) -> ConvertResult<()> { + let offset = entry_offsets[self.id.index]; + let from = context.unit.entry(offset)?; + let mut from_attrs = from.attrs(); + while let Some(from_attr) = from_attrs.next()? { + if from_attr.name() == constants::DW_AT_sibling { + // This may point to a null entry, so we have to treat it differently. + self.set_sibling(true); + } else if let Some(attr) = Attribute::from(context, &from_attr)? { + self.set(attr.name, attr.value); + } + } + Ok(()) + } + } + + impl Attribute { + /// Create an attribute by reading the data in the given sections. + pub(crate) fn from<R: Reader<Offset = usize>>( + context: &mut ConvertUnitContext<R>, + from: &read::Attribute<R>, + ) -> ConvertResult<Option<Attribute>> { + let value = AttributeValue::from(context, from.value())?; + Ok(value.map(|value| Attribute { + name: from.name(), + value, + })) + } + } + + impl AttributeValue { + /// Create an attribute value by reading the data in the given sections. + pub(crate) fn from<R: Reader<Offset = usize>>( + context: &mut ConvertUnitContext<R>, + from: read::AttributeValue<R>, + ) -> ConvertResult<Option<AttributeValue>> { + let to = match from { + read::AttributeValue::Addr(val) => match (context.convert_address)(val) { + Some(val) => AttributeValue::Address(val), + None => return Err(ConvertError::InvalidAddress), + }, + read::AttributeValue::Block(r) => AttributeValue::Block(r.to_slice()?.into()), + read::AttributeValue::Data1(val) => AttributeValue::Data1(val), + read::AttributeValue::Data2(val) => AttributeValue::Data2(val), + read::AttributeValue::Data4(val) => AttributeValue::Data4(val), + read::AttributeValue::Data8(val) => AttributeValue::Data8(val), + read::AttributeValue::Sdata(val) => AttributeValue::Sdata(val), + read::AttributeValue::Udata(val) => AttributeValue::Udata(val), + read::AttributeValue::Exprloc(expression) => { + let expression = Expression::from( + expression, + context.unit.encoding(), + Some(context.dwarf), + Some(context.unit), + Some(context.entry_ids), + context.convert_address, + )?; + AttributeValue::Exprloc(expression) + } + // TODO: it would be nice to preserve the flag form. + read::AttributeValue::Flag(val) => AttributeValue::Flag(val), + read::AttributeValue::DebugAddrBase(_base) => { + // We convert all address indices to addresses, + // so this is unneeded. + return Ok(None); + } + read::AttributeValue::DebugAddrIndex(index) => { + let val = context.dwarf.address(context.unit, index)?; + match (context.convert_address)(val) { + Some(val) => AttributeValue::Address(val), + None => return Err(ConvertError::InvalidAddress), + } + } + read::AttributeValue::UnitRef(val) => { + if !context.unit.header.is_valid_offset(val) { + return Err(ConvertError::InvalidUnitRef); + } + let id = context + .entry_ids + .get(&val.to_unit_section_offset(context.unit)) + .ok_or(ConvertError::InvalidUnitRef)?; + AttributeValue::UnitRef(id.1) + } + read::AttributeValue::DebugInfoRef(val) => { + // TODO: support relocation of this value + let id = context + .entry_ids + .get(&UnitSectionOffset::DebugInfoOffset(val)) + .ok_or(ConvertError::InvalidDebugInfoRef)?; + AttributeValue::DebugInfoRef(Reference::Entry(id.0, id.1)) + } + read::AttributeValue::DebugInfoRefSup(val) => AttributeValue::DebugInfoRefSup(val), + read::AttributeValue::DebugLineRef(val) => { + // There should only be the line program in the CU DIE which we've already + // converted, so check if it matches that. + if Some(val) == context.line_program_offset { + AttributeValue::LineProgramRef + } else { + return Err(ConvertError::InvalidLineRef); + } + } + read::AttributeValue::DebugMacinfoRef(val) => AttributeValue::DebugMacinfoRef(val), + read::AttributeValue::DebugMacroRef(val) => AttributeValue::DebugMacroRef(val), + read::AttributeValue::LocationListsRef(val) => { + let iter = context + .dwarf + .locations + .raw_locations(val, context.unit.encoding())?; + let loc_list = LocationList::from(iter, context)?; + let loc_id = context.locations.add(loc_list); + AttributeValue::LocationListRef(loc_id) + } + read::AttributeValue::DebugLocListsBase(_base) => { + // We convert all location list indices to offsets, + // so this is unneeded. + return Ok(None); + } + read::AttributeValue::DebugLocListsIndex(index) => { + let offset = context.dwarf.locations_offset(context.unit, index)?; + let iter = context + .dwarf + .locations + .raw_locations(offset, context.unit.encoding())?; + let loc_list = LocationList::from(iter, context)?; + let loc_id = context.locations.add(loc_list); + AttributeValue::LocationListRef(loc_id) + } + read::AttributeValue::RangeListsRef(offset) => { + let offset = context.dwarf.ranges_offset_from_raw(context.unit, offset); + let iter = context.dwarf.raw_ranges(context.unit, offset)?; + let range_list = RangeList::from(iter, context)?; + let range_id = context.ranges.add(range_list); + AttributeValue::RangeListRef(range_id) + } + read::AttributeValue::DebugRngListsBase(_base) => { + // We convert all range list indices to offsets, + // so this is unneeded. + return Ok(None); + } + read::AttributeValue::DebugRngListsIndex(index) => { + let offset = context.dwarf.ranges_offset(context.unit, index)?; + let iter = context + .dwarf + .ranges + .raw_ranges(offset, context.unit.encoding())?; + let range_list = RangeList::from(iter, context)?; + let range_id = context.ranges.add(range_list); + AttributeValue::RangeListRef(range_id) + } + read::AttributeValue::DebugTypesRef(val) => AttributeValue::DebugTypesRef(val), + read::AttributeValue::DebugStrRef(offset) => { + let r = context.dwarf.string(offset)?; + let id = context.strings.add(r.to_slice()?); + AttributeValue::StringRef(id) + } + read::AttributeValue::DebugStrRefSup(val) => AttributeValue::DebugStrRefSup(val), + read::AttributeValue::DebugStrOffsetsBase(_base) => { + // We convert all string offsets to `.debug_str` references, + // so this is unneeded. + return Ok(None); + } + read::AttributeValue::DebugStrOffsetsIndex(index) => { + let offset = context.dwarf.string_offset(context.unit, index)?; + let r = context.dwarf.string(offset)?; + let id = context.strings.add(r.to_slice()?); + AttributeValue::StringRef(id) + } + read::AttributeValue::DebugLineStrRef(offset) => { + let r = context.dwarf.line_string(offset)?; + let id = context.line_strings.add(r.to_slice()?); + AttributeValue::LineStringRef(id) + } + read::AttributeValue::String(r) => AttributeValue::String(r.to_slice()?.into()), + read::AttributeValue::Encoding(val) => AttributeValue::Encoding(val), + read::AttributeValue::DecimalSign(val) => AttributeValue::DecimalSign(val), + read::AttributeValue::Endianity(val) => AttributeValue::Endianity(val), + read::AttributeValue::Accessibility(val) => AttributeValue::Accessibility(val), + read::AttributeValue::Visibility(val) => AttributeValue::Visibility(val), + read::AttributeValue::Virtuality(val) => AttributeValue::Virtuality(val), + read::AttributeValue::Language(val) => AttributeValue::Language(val), + read::AttributeValue::AddressClass(val) => AttributeValue::AddressClass(val), + read::AttributeValue::IdentifierCase(val) => AttributeValue::IdentifierCase(val), + read::AttributeValue::CallingConvention(val) => { + AttributeValue::CallingConvention(val) + } + read::AttributeValue::Inline(val) => AttributeValue::Inline(val), + read::AttributeValue::Ordering(val) => AttributeValue::Ordering(val), + read::AttributeValue::FileIndex(val) => { + if val == 0 { + // 0 means not specified, even for version 5. + AttributeValue::FileIndex(None) + } else { + match context.line_program_files.get(val as usize) { + Some(id) => AttributeValue::FileIndex(Some(*id)), + None => return Err(ConvertError::InvalidFileIndex), + } + } + } + // Should always be a more specific section reference. + read::AttributeValue::SecOffset(_) => { + return Err(ConvertError::InvalidAttributeValue); + } + read::AttributeValue::DwoId(DwoId(val)) => AttributeValue::Udata(val), + }; + Ok(Some(to)) + } + } +} + +#[cfg(test)] +#[cfg(feature = "read")] +mod tests { + use super::*; + use crate::common::{ + DebugAddrBase, DebugLocListsBase, DebugRngListsBase, DebugStrOffsetsBase, LineEncoding, + }; + use crate::constants; + use crate::read; + use crate::write::{ + DebugLine, DebugLineStr, DebugStr, DwarfUnit, EndianVec, LineString, LineStringTable, + Location, LocationList, LocationListTable, Range, RangeList, RangeListOffsets, + RangeListTable, StringTable, + }; + use crate::LittleEndian; + use std::collections::HashMap; + use std::mem; + use std::sync::Arc; + + #[test] + fn test_unit_table() { + let mut strings = StringTable::default(); + + let mut units = UnitTable::default(); + let unit_id1 = units.add(Unit::new( + Encoding { + version: 4, + address_size: 8, + format: Format::Dwarf32, + }, + LineProgram::none(), + )); + let unit2 = units.add(Unit::new( + Encoding { + version: 2, + address_size: 4, + format: Format::Dwarf64, + }, + LineProgram::none(), + )); + let unit3 = units.add(Unit::new( + Encoding { + version: 5, + address_size: 4, + format: Format::Dwarf32, + }, + LineProgram::none(), + )); + assert_eq!(units.count(), 3); + { + let unit1 = units.get_mut(unit_id1); + assert_eq!(unit1.version(), 4); + assert_eq!(unit1.address_size(), 8); + assert_eq!(unit1.format(), Format::Dwarf32); + assert_eq!(unit1.count(), 1); + + let root_id = unit1.root(); + assert_eq!(root_id, UnitEntryId::new(unit1.base_id, 0)); + { + let root = unit1.get_mut(root_id); + assert_eq!(root.id(), root_id); + assert!(root.parent().is_none()); + assert_eq!(root.tag(), constants::DW_TAG_compile_unit); + + // Test get/get_mut + assert!(root.get(constants::DW_AT_producer).is_none()); + assert!(root.get_mut(constants::DW_AT_producer).is_none()); + let mut producer = AttributeValue::String(b"root"[..].into()); + root.set(constants::DW_AT_producer, producer.clone()); + assert_eq!(root.get(constants::DW_AT_producer), Some(&producer)); + assert_eq!(root.get_mut(constants::DW_AT_producer), Some(&mut producer)); + + // Test attrs + let mut attrs = root.attrs(); + let attr = attrs.next().unwrap(); + assert_eq!(attr.name(), constants::DW_AT_producer); + assert_eq!(attr.get(), &producer); + assert!(attrs.next().is_none()); + } + + let child1 = unit1.add(root_id, constants::DW_TAG_subprogram); + assert_eq!(child1, UnitEntryId::new(unit1.base_id, 1)); + { + let child1 = unit1.get_mut(child1); + assert_eq!(child1.parent(), Some(root_id)); + + let tmp = AttributeValue::String(b"tmp"[..].into()); + child1.set(constants::DW_AT_name, tmp.clone()); + assert_eq!(child1.get(constants::DW_AT_name), Some(&tmp)); + + // Test attrs_mut + let name = AttributeValue::StringRef(strings.add(&b"child1"[..])); + { + let attr = child1.attrs_mut().next().unwrap(); + assert_eq!(attr.name(), constants::DW_AT_name); + attr.set(name.clone()); + } + assert_eq!(child1.get(constants::DW_AT_name), Some(&name)); + } + + let child2 = unit1.add(root_id, constants::DW_TAG_subprogram); + assert_eq!(child2, UnitEntryId::new(unit1.base_id, 2)); + { + let child2 = unit1.get_mut(child2); + assert_eq!(child2.parent(), Some(root_id)); + + let tmp = AttributeValue::String(b"tmp"[..].into()); + child2.set(constants::DW_AT_name, tmp.clone()); + assert_eq!(child2.get(constants::DW_AT_name), Some(&tmp)); + + // Test replace + let name = AttributeValue::StringRef(strings.add(&b"child2"[..])); + child2.set(constants::DW_AT_name, name.clone()); + assert_eq!(child2.get(constants::DW_AT_name), Some(&name)); + } + + { + let root = unit1.get(root_id); + assert_eq!( + root.children().cloned().collect::<Vec<_>>(), + vec![child1, child2] + ); + } + } + { + let unit2 = units.get(unit2); + assert_eq!(unit2.version(), 2); + assert_eq!(unit2.address_size(), 4); + assert_eq!(unit2.format(), Format::Dwarf64); + assert_eq!(unit2.count(), 1); + + let root = unit2.root(); + assert_eq!(root, UnitEntryId::new(unit2.base_id, 0)); + let root = unit2.get(root); + assert_eq!(root.id(), UnitEntryId::new(unit2.base_id, 0)); + assert!(root.parent().is_none()); + assert_eq!(root.tag(), constants::DW_TAG_compile_unit); + } + + let mut sections = Sections::new(EndianVec::new(LittleEndian)); + let debug_line_str_offsets = DebugLineStrOffsets::none(); + let debug_str_offsets = strings.write(&mut sections.debug_str).unwrap(); + units + .write(&mut sections, &debug_line_str_offsets, &debug_str_offsets) + .unwrap(); + + println!("{:?}", sections.debug_str); + println!("{:?}", sections.debug_info); + println!("{:?}", sections.debug_abbrev); + + let dwarf = read::Dwarf { + debug_abbrev: read::DebugAbbrev::new(sections.debug_abbrev.slice(), LittleEndian), + debug_info: read::DebugInfo::new(sections.debug_info.slice(), LittleEndian), + debug_str: read::DebugStr::new(sections.debug_str.slice(), LittleEndian), + ..Default::default() + }; + let mut read_units = dwarf.units(); + + { + let read_unit1 = read_units.next().unwrap().unwrap(); + let unit1 = units.get(unit_id1); + assert_eq!(unit1.version(), read_unit1.version()); + assert_eq!(unit1.address_size(), read_unit1.address_size()); + assert_eq!(unit1.format(), read_unit1.format()); + + let read_unit1 = dwarf.unit(read_unit1).unwrap(); + let mut read_entries = read_unit1.entries(); + + let root = unit1.get(unit1.root()); + { + let (depth, read_root) = read_entries.next_dfs().unwrap().unwrap(); + assert_eq!(depth, 0); + assert_eq!(root.tag(), read_root.tag()); + assert!(read_root.has_children()); + + let producer = match root.get(constants::DW_AT_producer).unwrap() { + AttributeValue::String(ref producer) => &**producer, + otherwise => panic!("unexpected {:?}", otherwise), + }; + assert_eq!(producer, b"root"); + let read_producer = read_root + .attr_value(constants::DW_AT_producer) + .unwrap() + .unwrap(); + assert_eq!( + dwarf + .attr_string(&read_unit1, read_producer) + .unwrap() + .slice(), + producer + ); + } + + let mut children = root.children().cloned(); + + { + let child = children.next().unwrap(); + assert_eq!(child, UnitEntryId::new(unit1.base_id, 1)); + let child = unit1.get(child); + let (depth, read_child) = read_entries.next_dfs().unwrap().unwrap(); + assert_eq!(depth, 1); + assert_eq!(child.tag(), read_child.tag()); + assert!(!read_child.has_children()); + + let name = match child.get(constants::DW_AT_name).unwrap() { + AttributeValue::StringRef(name) => *name, + otherwise => panic!("unexpected {:?}", otherwise), + }; + let name = strings.get(name); + assert_eq!(name, b"child1"); + let read_name = read_child + .attr_value(constants::DW_AT_name) + .unwrap() + .unwrap(); + assert_eq!( + dwarf.attr_string(&read_unit1, read_name).unwrap().slice(), + name + ); + } + + { + let child = children.next().unwrap(); + assert_eq!(child, UnitEntryId::new(unit1.base_id, 2)); + let child = unit1.get(child); + let (depth, read_child) = read_entries.next_dfs().unwrap().unwrap(); + assert_eq!(depth, 0); + assert_eq!(child.tag(), read_child.tag()); + assert!(!read_child.has_children()); + + let name = match child.get(constants::DW_AT_name).unwrap() { + AttributeValue::StringRef(name) => *name, + otherwise => panic!("unexpected {:?}", otherwise), + }; + let name = strings.get(name); + assert_eq!(name, b"child2"); + let read_name = read_child + .attr_value(constants::DW_AT_name) + .unwrap() + .unwrap(); + assert_eq!( + dwarf.attr_string(&read_unit1, read_name).unwrap().slice(), + name + ); + } + + assert!(read_entries.next_dfs().unwrap().is_none()); + } + + { + let read_unit2 = read_units.next().unwrap().unwrap(); + let unit2 = units.get(unit2); + assert_eq!(unit2.version(), read_unit2.version()); + assert_eq!(unit2.address_size(), read_unit2.address_size()); + assert_eq!(unit2.format(), read_unit2.format()); + + let abbrevs = dwarf.abbreviations(&read_unit2).unwrap(); + let mut read_entries = read_unit2.entries(&abbrevs); + + { + let root = unit2.get(unit2.root()); + let (depth, read_root) = read_entries.next_dfs().unwrap().unwrap(); + assert_eq!(depth, 0); + assert_eq!(root.tag(), read_root.tag()); + assert!(!read_root.has_children()); + } + + assert!(read_entries.next_dfs().unwrap().is_none()); + } + + { + let read_unit3 = read_units.next().unwrap().unwrap(); + let unit3 = units.get(unit3); + assert_eq!(unit3.version(), read_unit3.version()); + assert_eq!(unit3.address_size(), read_unit3.address_size()); + assert_eq!(unit3.format(), read_unit3.format()); + + let abbrevs = dwarf.abbreviations(&read_unit3).unwrap(); + let mut read_entries = read_unit3.entries(&abbrevs); + + { + let root = unit3.get(unit3.root()); + let (depth, read_root) = read_entries.next_dfs().unwrap().unwrap(); + assert_eq!(depth, 0); + assert_eq!(root.tag(), read_root.tag()); + assert!(!read_root.has_children()); + } + + assert!(read_entries.next_dfs().unwrap().is_none()); + } + + assert!(read_units.next().unwrap().is_none()); + + let mut convert_line_strings = LineStringTable::default(); + let mut convert_strings = StringTable::default(); + let convert_units = UnitTable::from( + &dwarf, + &mut convert_line_strings, + &mut convert_strings, + &|address| Some(Address::Constant(address)), + ) + .unwrap(); + assert_eq!(convert_units.count(), units.count()); + + for i in 0..convert_units.count() { + let unit_id = units.id(i); + let unit = units.get(unit_id); + let convert_unit_id = convert_units.id(i); + let convert_unit = convert_units.get(convert_unit_id); + assert_eq!(convert_unit.version(), unit.version()); + assert_eq!(convert_unit.address_size(), unit.address_size()); + assert_eq!(convert_unit.format(), unit.format()); + assert_eq!(convert_unit.count(), unit.count()); + + let root = unit.get(unit.root()); + let convert_root = convert_unit.get(convert_unit.root()); + assert_eq!(convert_root.tag(), root.tag()); + for (convert_attr, attr) in convert_root.attrs().zip(root.attrs()) { + assert_eq!(convert_attr, attr); + } + } + } + + #[test] + fn test_attribute_value() { + // Create a string table and a string with a non-zero id/offset. + let mut strings = StringTable::default(); + strings.add("string one"); + let string_id = strings.add("string two"); + let mut debug_str = DebugStr::from(EndianVec::new(LittleEndian)); + let debug_str_offsets = strings.write(&mut debug_str).unwrap(); + let read_debug_str = read::DebugStr::new(debug_str.slice(), LittleEndian); + + let mut line_strings = LineStringTable::default(); + line_strings.add("line string one"); + let line_string_id = line_strings.add("line string two"); + let mut debug_line_str = DebugLineStr::from(EndianVec::new(LittleEndian)); + let debug_line_str_offsets = line_strings.write(&mut debug_line_str).unwrap(); + let read_debug_line_str = + read::DebugLineStr::from(read::EndianSlice::new(debug_line_str.slice(), LittleEndian)); + + let data = vec![1, 2, 3, 4]; + let read_data = read::EndianSlice::new(&[1, 2, 3, 4], LittleEndian); + + let mut expression = Expression::new(); + expression.op_constu(57); + let read_expression = read::Expression(read::EndianSlice::new( + &[constants::DW_OP_constu.0, 57], + LittleEndian, + )); + + let mut ranges = RangeListTable::default(); + let range_id = ranges.add(RangeList(vec![Range::StartEnd { + begin: Address::Constant(0x1234), + end: Address::Constant(0x2345), + }])); + + let mut locations = LocationListTable::default(); + let loc_id = locations.add(LocationList(vec![Location::StartEnd { + begin: Address::Constant(0x1234), + end: Address::Constant(0x2345), + data: expression.clone(), + }])); + + for &version in &[2, 3, 4, 5] { + for &address_size in &[4, 8] { + for &format in &[Format::Dwarf32, Format::Dwarf64] { + let encoding = Encoding { + format, + version, + address_size, + }; + + let mut sections = Sections::new(EndianVec::new(LittleEndian)); + let range_list_offsets = ranges.write(&mut sections, encoding).unwrap(); + let loc_list_offsets = locations.write(&mut sections, encoding, None).unwrap(); + + let read_debug_ranges = + read::DebugRanges::new(sections.debug_ranges.slice(), LittleEndian); + let read_debug_rnglists = + read::DebugRngLists::new(sections.debug_rnglists.slice(), LittleEndian); + + let read_debug_loc = + read::DebugLoc::new(sections.debug_loc.slice(), LittleEndian); + let read_debug_loclists = + read::DebugLocLists::new(sections.debug_loclists.slice(), LittleEndian); + + let mut units = UnitTable::default(); + let unit = units.add(Unit::new(encoding, LineProgram::none())); + let unit = units.get(unit); + let encoding = Encoding { + format, + version, + address_size, + }; + let from_unit = read::UnitHeader::new( + encoding, + 0, + read::UnitType::Compilation, + DebugAbbrevOffset(0), + DebugInfoOffset(0).into(), + read::EndianSlice::new(&[], LittleEndian), + ); + + for &(ref name, ref value, ref expect_value) in &[ + ( + constants::DW_AT_name, + AttributeValue::Address(Address::Constant(0x1234)), + read::AttributeValue::Addr(0x1234), + ), + ( + constants::DW_AT_name, + AttributeValue::Block(data.clone()), + read::AttributeValue::Block(read_data), + ), + ( + constants::DW_AT_name, + AttributeValue::Data1(0x12), + read::AttributeValue::Data1(0x12), + ), + ( + constants::DW_AT_name, + AttributeValue::Data2(0x1234), + read::AttributeValue::Data2(0x1234), + ), + ( + constants::DW_AT_name, + AttributeValue::Data4(0x1234), + read::AttributeValue::Data4(0x1234), + ), + ( + constants::DW_AT_name, + AttributeValue::Data8(0x1234), + read::AttributeValue::Data8(0x1234), + ), + ( + constants::DW_AT_name, + AttributeValue::Sdata(0x1234), + read::AttributeValue::Sdata(0x1234), + ), + ( + constants::DW_AT_name, + AttributeValue::Udata(0x1234), + read::AttributeValue::Udata(0x1234), + ), + ( + constants::DW_AT_name, + AttributeValue::Exprloc(expression.clone()), + read::AttributeValue::Exprloc(read_expression), + ), + ( + constants::DW_AT_name, + AttributeValue::Flag(false), + read::AttributeValue::Flag(false), + ), + /* + ( + constants::DW_AT_name, + AttributeValue::FlagPresent, + read::AttributeValue::Flag(true), + ), + */ + ( + constants::DW_AT_name, + AttributeValue::DebugInfoRefSup(DebugInfoOffset(0x1234)), + read::AttributeValue::DebugInfoRefSup(DebugInfoOffset(0x1234)), + ), + ( + constants::DW_AT_location, + AttributeValue::LocationListRef(loc_id), + read::AttributeValue::SecOffset(loc_list_offsets.get(loc_id).0), + ), + ( + constants::DW_AT_macro_info, + AttributeValue::DebugMacinfoRef(DebugMacinfoOffset(0x1234)), + read::AttributeValue::SecOffset(0x1234), + ), + ( + constants::DW_AT_macros, + AttributeValue::DebugMacroRef(DebugMacroOffset(0x1234)), + read::AttributeValue::SecOffset(0x1234), + ), + ( + constants::DW_AT_ranges, + AttributeValue::RangeListRef(range_id), + read::AttributeValue::SecOffset(range_list_offsets.get(range_id).0), + ), + ( + constants::DW_AT_name, + AttributeValue::DebugTypesRef(DebugTypeSignature(0x1234)), + read::AttributeValue::DebugTypesRef(DebugTypeSignature(0x1234)), + ), + ( + constants::DW_AT_name, + AttributeValue::StringRef(string_id), + read::AttributeValue::DebugStrRef(debug_str_offsets.get(string_id)), + ), + ( + constants::DW_AT_name, + AttributeValue::DebugStrRefSup(DebugStrOffset(0x1234)), + read::AttributeValue::DebugStrRefSup(DebugStrOffset(0x1234)), + ), + ( + constants::DW_AT_name, + AttributeValue::LineStringRef(line_string_id), + read::AttributeValue::DebugLineStrRef( + debug_line_str_offsets.get(line_string_id), + ), + ), + ( + constants::DW_AT_name, + AttributeValue::String(data.clone()), + read::AttributeValue::String(read_data), + ), + ( + constants::DW_AT_encoding, + AttributeValue::Encoding(constants::DwAte(0x12)), + read::AttributeValue::Udata(0x12), + ), + ( + constants::DW_AT_decimal_sign, + AttributeValue::DecimalSign(constants::DwDs(0x12)), + read::AttributeValue::Udata(0x12), + ), + ( + constants::DW_AT_endianity, + AttributeValue::Endianity(constants::DwEnd(0x12)), + read::AttributeValue::Udata(0x12), + ), + ( + constants::DW_AT_accessibility, + AttributeValue::Accessibility(constants::DwAccess(0x12)), + read::AttributeValue::Udata(0x12), + ), + ( + constants::DW_AT_visibility, + AttributeValue::Visibility(constants::DwVis(0x12)), + read::AttributeValue::Udata(0x12), + ), + ( + constants::DW_AT_virtuality, + AttributeValue::Virtuality(constants::DwVirtuality(0x12)), + read::AttributeValue::Udata(0x12), + ), + ( + constants::DW_AT_language, + AttributeValue::Language(constants::DwLang(0x12)), + read::AttributeValue::Udata(0x12), + ), + ( + constants::DW_AT_address_class, + AttributeValue::AddressClass(constants::DwAddr(0x12)), + read::AttributeValue::Udata(0x12), + ), + ( + constants::DW_AT_identifier_case, + AttributeValue::IdentifierCase(constants::DwId(0x12)), + read::AttributeValue::Udata(0x12), + ), + ( + constants::DW_AT_calling_convention, + AttributeValue::CallingConvention(constants::DwCc(0x12)), + read::AttributeValue::Udata(0x12), + ), + ( + constants::DW_AT_ordering, + AttributeValue::Ordering(constants::DwOrd(0x12)), + read::AttributeValue::Udata(0x12), + ), + ( + constants::DW_AT_inline, + AttributeValue::Inline(constants::DwInl(0x12)), + read::AttributeValue::Udata(0x12), + ), + ][..] + { + let form = value.form(encoding).unwrap(); + let attr = Attribute { + name: *name, + value: value.clone(), + }; + + let offsets = UnitOffsets::none(); + let line_program_offset = None; + let mut debug_info_refs = Vec::new(); + let mut unit_refs = Vec::new(); + let mut debug_info = DebugInfo::from(EndianVec::new(LittleEndian)); + attr.value + .write( + &mut debug_info, + &mut debug_info_refs, + &mut unit_refs, + &unit, + &offsets, + line_program_offset, + &debug_line_str_offsets, + &debug_str_offsets, + &range_list_offsets, + &loc_list_offsets, + ) + .unwrap(); + + let spec = read::AttributeSpecification::new(*name, form, None); + let mut r = read::EndianSlice::new(debug_info.slice(), LittleEndian); + let read_attr = read::parse_attribute(&mut r, encoding, spec).unwrap(); + let read_value = &read_attr.raw_value(); + // read::AttributeValue is invariant in the lifetime of R. + // The lifetimes here are all okay, so transmute it. + let read_value = unsafe { + mem::transmute::< + &read::AttributeValue<read::EndianSlice<LittleEndian>>, + &read::AttributeValue<read::EndianSlice<LittleEndian>>, + >(read_value) + }; + assert_eq!(read_value, expect_value); + + let dwarf = read::Dwarf { + debug_str: read_debug_str.clone(), + debug_line_str: read_debug_line_str.clone(), + ranges: read::RangeLists::new(read_debug_ranges, read_debug_rnglists), + locations: read::LocationLists::new( + read_debug_loc, + read_debug_loclists, + ), + ..Default::default() + }; + + let unit = read::Unit { + header: from_unit, + abbreviations: Arc::new(read::Abbreviations::default()), + name: None, + comp_dir: None, + low_pc: 0, + str_offsets_base: DebugStrOffsetsBase(0), + addr_base: DebugAddrBase(0), + loclists_base: DebugLocListsBase(0), + rnglists_base: DebugRngListsBase(0), + line_program: None, + dwo_id: None, + }; + + let mut context = convert::ConvertUnitContext { + dwarf: &dwarf, + unit: &unit, + line_strings: &mut line_strings, + strings: &mut strings, + ranges: &mut ranges, + locations: &mut locations, + convert_address: &|address| Some(Address::Constant(address)), + base_address: Address::Constant(0), + line_program_offset: None, + line_program_files: Vec::new(), + entry_ids: &HashMap::new(), + }; + + let convert_attr = + Attribute::from(&mut context, &read_attr).unwrap().unwrap(); + assert_eq!(convert_attr, attr); + } + } + } + } + } + + #[test] + fn test_unit_ref() { + let mut units = UnitTable::default(); + let unit_id1 = units.add(Unit::new( + Encoding { + version: 4, + address_size: 8, + format: Format::Dwarf32, + }, + LineProgram::none(), + )); + assert_eq!(unit_id1, units.id(0)); + let unit_id2 = units.add(Unit::new( + Encoding { + version: 2, + address_size: 4, + format: Format::Dwarf64, + }, + LineProgram::none(), + )); + assert_eq!(unit_id2, units.id(1)); + let unit1_child1 = UnitEntryId::new(units.get(unit_id1).base_id, 1); + let unit1_child2 = UnitEntryId::new(units.get(unit_id1).base_id, 2); + let unit2_child1 = UnitEntryId::new(units.get(unit_id2).base_id, 1); + let unit2_child2 = UnitEntryId::new(units.get(unit_id2).base_id, 2); + { + let unit1 = units.get_mut(unit_id1); + let root = unit1.root(); + let child_id1 = unit1.add(root, constants::DW_TAG_subprogram); + assert_eq!(child_id1, unit1_child1); + let child_id2 = unit1.add(root, constants::DW_TAG_subprogram); + assert_eq!(child_id2, unit1_child2); + { + let child1 = unit1.get_mut(child_id1); + child1.set(constants::DW_AT_type, AttributeValue::UnitRef(child_id2)); + } + { + let child2 = unit1.get_mut(child_id2); + child2.set( + constants::DW_AT_type, + AttributeValue::DebugInfoRef(Reference::Entry(unit_id2, unit2_child1)), + ); + } + } + { + let unit2 = units.get_mut(unit_id2); + let root = unit2.root(); + let child_id1 = unit2.add(root, constants::DW_TAG_subprogram); + assert_eq!(child_id1, unit2_child1); + let child_id2 = unit2.add(root, constants::DW_TAG_subprogram); + assert_eq!(child_id2, unit2_child2); + { + let child1 = unit2.get_mut(child_id1); + child1.set(constants::DW_AT_type, AttributeValue::UnitRef(child_id2)); + } + { + let child2 = unit2.get_mut(child_id2); + child2.set( + constants::DW_AT_type, + AttributeValue::DebugInfoRef(Reference::Entry(unit_id1, unit1_child1)), + ); + } + } + + let debug_line_str_offsets = DebugLineStrOffsets::none(); + let debug_str_offsets = DebugStrOffsets::none(); + let mut sections = Sections::new(EndianVec::new(LittleEndian)); + let debug_info_offsets = units + .write(&mut sections, &debug_line_str_offsets, &debug_str_offsets) + .unwrap(); + + println!("{:?}", sections.debug_info); + println!("{:?}", sections.debug_abbrev); + + let dwarf = read::Dwarf { + debug_abbrev: read::DebugAbbrev::new(sections.debug_abbrev.slice(), LittleEndian), + debug_info: read::DebugInfo::new(sections.debug_info.slice(), LittleEndian), + ..Default::default() + }; + + let mut read_units = dwarf.units(); + { + let read_unit1 = read_units.next().unwrap().unwrap(); + assert_eq!( + read_unit1.offset(), + debug_info_offsets.unit(unit_id1).into() + ); + + let abbrevs = dwarf.abbreviations(&read_unit1).unwrap(); + let mut read_entries = read_unit1.entries(&abbrevs); + { + let (_, _read_root) = read_entries.next_dfs().unwrap().unwrap(); + } + { + let (_, read_child1) = read_entries.next_dfs().unwrap().unwrap(); + let offset = debug_info_offsets + .entry(unit_id1, unit1_child2) + .to_unit_offset(&read_unit1) + .unwrap(); + assert_eq!( + read_child1.attr_value(constants::DW_AT_type).unwrap(), + Some(read::AttributeValue::UnitRef(offset)) + ); + } + { + let (_, read_child2) = read_entries.next_dfs().unwrap().unwrap(); + let offset = debug_info_offsets.entry(unit_id2, unit2_child1); + assert_eq!( + read_child2.attr_value(constants::DW_AT_type).unwrap(), + Some(read::AttributeValue::DebugInfoRef(offset)) + ); + } + } + { + let read_unit2 = read_units.next().unwrap().unwrap(); + assert_eq!( + read_unit2.offset(), + debug_info_offsets.unit(unit_id2).into() + ); + + let abbrevs = dwarf.abbreviations(&read_unit2).unwrap(); + let mut read_entries = read_unit2.entries(&abbrevs); + { + let (_, _read_root) = read_entries.next_dfs().unwrap().unwrap(); + } + { + let (_, read_child1) = read_entries.next_dfs().unwrap().unwrap(); + let offset = debug_info_offsets + .entry(unit_id2, unit2_child2) + .to_unit_offset(&read_unit2) + .unwrap(); + assert_eq!( + read_child1.attr_value(constants::DW_AT_type).unwrap(), + Some(read::AttributeValue::UnitRef(offset)) + ); + } + { + let (_, read_child2) = read_entries.next_dfs().unwrap().unwrap(); + let offset = debug_info_offsets.entry(unit_id1, unit1_child1); + assert_eq!( + read_child2.attr_value(constants::DW_AT_type).unwrap(), + Some(read::AttributeValue::DebugInfoRef(offset)) + ); + } + } + + let mut convert_line_strings = LineStringTable::default(); + let mut convert_strings = StringTable::default(); + let convert_units = UnitTable::from( + &dwarf, + &mut convert_line_strings, + &mut convert_strings, + &|address| Some(Address::Constant(address)), + ) + .unwrap(); + assert_eq!(convert_units.count(), units.count()); + + for i in 0..convert_units.count() { + let unit = units.get(units.id(i)); + let convert_unit = convert_units.get(convert_units.id(i)); + assert_eq!(convert_unit.version(), unit.version()); + assert_eq!(convert_unit.address_size(), unit.address_size()); + assert_eq!(convert_unit.format(), unit.format()); + assert_eq!(convert_unit.count(), unit.count()); + + let root = unit.get(unit.root()); + let convert_root = convert_unit.get(convert_unit.root()); + assert_eq!(convert_root.tag(), root.tag()); + for (convert_attr, attr) in convert_root.attrs().zip(root.attrs()) { + assert_eq!(convert_attr, attr); + } + + let child1 = unit.get(UnitEntryId::new(unit.base_id, 1)); + let convert_child1 = convert_unit.get(UnitEntryId::new(convert_unit.base_id, 1)); + assert_eq!(convert_child1.tag(), child1.tag()); + for (convert_attr, attr) in convert_child1.attrs().zip(child1.attrs()) { + assert_eq!(convert_attr.name, attr.name); + match (convert_attr.value.clone(), attr.value.clone()) { + ( + AttributeValue::DebugInfoRef(Reference::Entry(convert_unit, convert_entry)), + AttributeValue::DebugInfoRef(Reference::Entry(unit, entry)), + ) => { + assert_eq!(convert_unit.index, unit.index); + assert_eq!(convert_entry.index, entry.index); + } + (AttributeValue::UnitRef(convert_id), AttributeValue::UnitRef(id)) => { + assert_eq!(convert_id.index, id.index); + } + (convert_value, value) => assert_eq!(convert_value, value), + } + } + + let child2 = unit.get(UnitEntryId::new(unit.base_id, 2)); + let convert_child2 = convert_unit.get(UnitEntryId::new(convert_unit.base_id, 2)); + assert_eq!(convert_child2.tag(), child2.tag()); + for (convert_attr, attr) in convert_child2.attrs().zip(child2.attrs()) { + assert_eq!(convert_attr.name, attr.name); + match (convert_attr.value.clone(), attr.value.clone()) { + ( + AttributeValue::DebugInfoRef(Reference::Entry(convert_unit, convert_entry)), + AttributeValue::DebugInfoRef(Reference::Entry(unit, entry)), + ) => { + assert_eq!(convert_unit.index, unit.index); + assert_eq!(convert_entry.index, entry.index); + } + (AttributeValue::UnitRef(convert_id), AttributeValue::UnitRef(id)) => { + assert_eq!(convert_id.index, id.index); + } + (convert_value, value) => assert_eq!(convert_value, value), + } + } + } + } + + #[test] + fn test_sibling() { + fn add_child( + unit: &mut Unit, + parent: UnitEntryId, + tag: constants::DwTag, + name: &str, + ) -> UnitEntryId { + let id = unit.add(parent, tag); + let child = unit.get_mut(id); + child.set(constants::DW_AT_name, AttributeValue::String(name.into())); + child.set_sibling(true); + id + } + + fn add_children(units: &mut UnitTable, unit_id: UnitId) { + let unit = units.get_mut(unit_id); + let root = unit.root(); + let child1 = add_child(unit, root, constants::DW_TAG_subprogram, "child1"); + add_child(unit, child1, constants::DW_TAG_variable, "grandchild1"); + add_child(unit, root, constants::DW_TAG_subprogram, "child2"); + add_child(unit, root, constants::DW_TAG_subprogram, "child3"); + } + + fn next_child<R: read::Reader<Offset = usize>>( + entries: &mut read::EntriesCursor<R>, + ) -> (read::UnitOffset, Option<read::UnitOffset>) { + let (_, entry) = entries.next_dfs().unwrap().unwrap(); + let offset = entry.offset(); + let sibling = + entry + .attr_value(constants::DW_AT_sibling) + .unwrap() + .map(|attr| match attr { + read::AttributeValue::UnitRef(offset) => offset, + _ => panic!("bad sibling value"), + }); + (offset, sibling) + } + + fn check_sibling<R: read::Reader<Offset = usize>>( + unit: &read::UnitHeader<R>, + debug_abbrev: &read::DebugAbbrev<R>, + ) { + let abbrevs = unit.abbreviations(debug_abbrev).unwrap(); + let mut entries = unit.entries(&abbrevs); + // root + entries.next_dfs().unwrap().unwrap(); + // child1 + let (_, sibling1) = next_child(&mut entries); + // grandchild1 + entries.next_dfs().unwrap().unwrap(); + // child2 + let (offset2, sibling2) = next_child(&mut entries); + // child3 + let (_, _) = next_child(&mut entries); + assert_eq!(sibling1, Some(offset2)); + assert_eq!(sibling2, None); + } + + let encoding = Encoding { + format: Format::Dwarf32, + version: 4, + address_size: 8, + }; + let mut units = UnitTable::default(); + let unit_id1 = units.add(Unit::new(encoding, LineProgram::none())); + add_children(&mut units, unit_id1); + let unit_id2 = units.add(Unit::new(encoding, LineProgram::none())); + add_children(&mut units, unit_id2); + + let debug_line_str_offsets = DebugLineStrOffsets::none(); + let debug_str_offsets = DebugStrOffsets::none(); + let mut sections = Sections::new(EndianVec::new(LittleEndian)); + units + .write(&mut sections, &debug_line_str_offsets, &debug_str_offsets) + .unwrap(); + + println!("{:?}", sections.debug_info); + println!("{:?}", sections.debug_abbrev); + + let read_debug_info = read::DebugInfo::new(sections.debug_info.slice(), LittleEndian); + let read_debug_abbrev = read::DebugAbbrev::new(sections.debug_abbrev.slice(), LittleEndian); + let mut read_units = read_debug_info.units(); + check_sibling(&read_units.next().unwrap().unwrap(), &read_debug_abbrev); + check_sibling(&read_units.next().unwrap().unwrap(), &read_debug_abbrev); + } + + #[test] + fn test_line_ref() { + for &version in &[2, 3, 4, 5] { + for &address_size in &[4, 8] { + for &format in &[Format::Dwarf32, Format::Dwarf64] { + let encoding = Encoding { + format, + version, + address_size, + }; + + // The line program we'll be referencing. + let mut line_program = LineProgram::new( + encoding, + LineEncoding::default(), + LineString::String(b"comp_dir".to_vec()), + LineString::String(b"comp_name".to_vec()), + None, + ); + let dir = line_program.default_directory(); + let file1 = + line_program.add_file(LineString::String(b"file1".to_vec()), dir, None); + let file2 = + line_program.add_file(LineString::String(b"file2".to_vec()), dir, None); + + // Write, read, and convert the line program, so that we have the info + // required to convert the attributes. + let line_strings = DebugLineStrOffsets::none(); + let strings = DebugStrOffsets::none(); + let mut debug_line = DebugLine::from(EndianVec::new(LittleEndian)); + let line_program_offset = line_program + .write(&mut debug_line, encoding, &line_strings, &strings) + .unwrap(); + let read_debug_line = read::DebugLine::new(debug_line.slice(), LittleEndian); + let read_line_program = read_debug_line + .program( + line_program_offset, + address_size, + Some(read::EndianSlice::new(b"comp_dir", LittleEndian)), + Some(read::EndianSlice::new(b"comp_name", LittleEndian)), + ) + .unwrap(); + let dwarf = read::Dwarf::default(); + let mut convert_line_strings = LineStringTable::default(); + let mut convert_strings = StringTable::default(); + let (_, line_program_files) = LineProgram::from( + read_line_program, + &dwarf, + &mut convert_line_strings, + &mut convert_strings, + &|address| Some(Address::Constant(address)), + ) + .unwrap(); + + // Fake the unit. + let mut units = UnitTable::default(); + let unit = units.add(Unit::new(encoding, LineProgram::none())); + let unit = units.get(unit); + let from_unit = read::UnitHeader::new( + encoding, + 0, + read::UnitType::Compilation, + DebugAbbrevOffset(0), + DebugInfoOffset(0).into(), + read::EndianSlice::new(&[], LittleEndian), + ); + + for &(ref name, ref value, ref expect_value) in &[ + ( + constants::DW_AT_stmt_list, + AttributeValue::LineProgramRef, + read::AttributeValue::SecOffset(line_program_offset.0), + ), + ( + constants::DW_AT_decl_file, + AttributeValue::FileIndex(Some(file1)), + read::AttributeValue::Udata(file1.raw()), + ), + ( + constants::DW_AT_decl_file, + AttributeValue::FileIndex(Some(file2)), + read::AttributeValue::Udata(file2.raw()), + ), + ][..] + { + let mut ranges = RangeListTable::default(); + let mut locations = LocationListTable::default(); + let mut strings = StringTable::default(); + let mut line_strings = LineStringTable::default(); + + let form = value.form(encoding).unwrap(); + let attr = Attribute { + name: *name, + value: value.clone(), + }; + + let mut debug_info_refs = Vec::new(); + let mut unit_refs = Vec::new(); + let mut debug_info = DebugInfo::from(EndianVec::new(LittleEndian)); + let offsets = UnitOffsets::none(); + let debug_line_str_offsets = DebugLineStrOffsets::none(); + let debug_str_offsets = DebugStrOffsets::none(); + let range_list_offsets = RangeListOffsets::none(); + let loc_list_offsets = LocationListOffsets::none(); + attr.value + .write( + &mut debug_info, + &mut debug_info_refs, + &mut unit_refs, + &unit, + &offsets, + Some(line_program_offset), + &debug_line_str_offsets, + &debug_str_offsets, + &range_list_offsets, + &loc_list_offsets, + ) + .unwrap(); + + let spec = read::AttributeSpecification::new(*name, form, None); + let mut r = read::EndianSlice::new(debug_info.slice(), LittleEndian); + let read_attr = read::parse_attribute(&mut r, encoding, spec).unwrap(); + let read_value = &read_attr.raw_value(); + // read::AttributeValue is invariant in the lifetime of R. + // The lifetimes here are all okay, so transmute it. + let read_value = unsafe { + mem::transmute::< + &read::AttributeValue<read::EndianSlice<LittleEndian>>, + &read::AttributeValue<read::EndianSlice<LittleEndian>>, + >(read_value) + }; + assert_eq!(read_value, expect_value); + + let unit = read::Unit { + header: from_unit, + abbreviations: Arc::new(read::Abbreviations::default()), + name: None, + comp_dir: None, + low_pc: 0, + str_offsets_base: DebugStrOffsetsBase(0), + addr_base: DebugAddrBase(0), + loclists_base: DebugLocListsBase(0), + rnglists_base: DebugRngListsBase(0), + line_program: None, + dwo_id: None, + }; + + let mut context = convert::ConvertUnitContext { + dwarf: &dwarf, + unit: &unit, + line_strings: &mut line_strings, + strings: &mut strings, + ranges: &mut ranges, + locations: &mut locations, + convert_address: &|address| Some(Address::Constant(address)), + base_address: Address::Constant(0), + line_program_offset: Some(line_program_offset), + line_program_files: line_program_files.clone(), + entry_ids: &HashMap::new(), + }; + + let convert_attr = + Attribute::from(&mut context, &read_attr).unwrap().unwrap(); + assert_eq!(convert_attr, attr); + } + } + } + } + } + + #[test] + fn test_line_program_used() { + for used in vec![false, true] { + let encoding = Encoding { + format: Format::Dwarf32, + version: 5, + address_size: 8, + }; + + let line_program = LineProgram::new( + encoding, + LineEncoding::default(), + LineString::String(b"comp_dir".to_vec()), + LineString::String(b"comp_name".to_vec()), + None, + ); + + let mut unit = Unit::new(encoding, line_program); + let file_id = if used { Some(FileId::new(0)) } else { None }; + let root = unit.root(); + unit.get_mut(root).set( + constants::DW_AT_decl_file, + AttributeValue::FileIndex(file_id), + ); + + let mut units = UnitTable::default(); + units.add(unit); + + let debug_line_str_offsets = DebugLineStrOffsets::none(); + let debug_str_offsets = DebugStrOffsets::none(); + let mut sections = Sections::new(EndianVec::new(LittleEndian)); + units + .write(&mut sections, &debug_line_str_offsets, &debug_str_offsets) + .unwrap(); + assert_eq!(!used, sections.debug_line.slice().is_empty()); + } + } + + #[test] + fn test_delete_child() { + fn set_name(unit: &mut Unit, id: UnitEntryId, name: &str) { + let entry = unit.get_mut(id); + entry.set(constants::DW_AT_name, AttributeValue::String(name.into())); + } + fn check_name<R: read::Reader>( + entry: &read::DebuggingInformationEntry<R>, + debug_str: &read::DebugStr<R>, + name: &str, + ) { + let name_attr = entry.attr(constants::DW_AT_name).unwrap().unwrap(); + let entry_name = name_attr.string_value(debug_str).unwrap(); + let entry_name_str = entry_name.to_string().unwrap(); + assert_eq!(entry_name_str, name); + } + let encoding = Encoding { + format: Format::Dwarf32, + version: 4, + address_size: 8, + }; + let mut dwarf = DwarfUnit::new(encoding); + let root = dwarf.unit.root(); + + // Add and delete entries in the root unit + let child1 = dwarf.unit.add(root, constants::DW_TAG_subprogram); + set_name(&mut dwarf.unit, child1, "child1"); + let grandchild1 = dwarf.unit.add(child1, constants::DW_TAG_variable); + set_name(&mut dwarf.unit, grandchild1, "grandchild1"); + let child2 = dwarf.unit.add(root, constants::DW_TAG_subprogram); + set_name(&mut dwarf.unit, child2, "child2"); + // This deletes both `child1` and its child `grandchild1` + dwarf.unit.get_mut(root).delete_child(child1); + let child3 = dwarf.unit.add(root, constants::DW_TAG_subprogram); + set_name(&mut dwarf.unit, child3, "child3"); + let child4 = dwarf.unit.add(root, constants::DW_TAG_subprogram); + set_name(&mut dwarf.unit, child4, "child4"); + let grandchild4 = dwarf.unit.add(child4, constants::DW_TAG_variable); + set_name(&mut dwarf.unit, grandchild4, "grandchild4"); + dwarf.unit.get_mut(child4).delete_child(grandchild4); + + let mut sections = Sections::new(EndianVec::new(LittleEndian)); + + // Write DWARF data which should only include `child2`, `child3` and `child4` + dwarf.write(&mut sections).unwrap(); + + let read_debug_info = read::DebugInfo::new(sections.debug_info.slice(), LittleEndian); + let read_debug_abbrev = read::DebugAbbrev::new(sections.debug_abbrev.slice(), LittleEndian); + let read_debug_str = read::DebugStr::new(sections.debug_str.slice(), LittleEndian); + let read_unit = read_debug_info.units().next().unwrap().unwrap(); + let abbrevs = read_unit.abbreviations(&read_debug_abbrev).unwrap(); + let mut entries = read_unit.entries(&abbrevs); + // root + entries.next_dfs().unwrap().unwrap(); + // child2 + let (_, read_child2) = entries.next_dfs().unwrap().unwrap(); + check_name(read_child2, &read_debug_str, "child2"); + // child3 + let (_, read_child3) = entries.next_dfs().unwrap().unwrap(); + check_name(read_child3, &read_debug_str, "child3"); + // child4 + let (_, read_child4) = entries.next_dfs().unwrap().unwrap(); + check_name(read_child4, &read_debug_str, "child4"); + // There should be no more entries + assert!(entries.next_dfs().unwrap().is_none()); + } +} diff --git a/vendor/gimli/src/write/writer.rs b/vendor/gimli/src/write/writer.rs new file mode 100644 index 0000000..1ce3641 --- /dev/null +++ b/vendor/gimli/src/write/writer.rs @@ -0,0 +1,494 @@ +use crate::common::{Format, SectionId}; +use crate::constants; +use crate::endianity::Endianity; +use crate::leb128; +use crate::write::{Address, Error, Result}; + +/// A trait for writing the data to a DWARF section. +/// +/// All write operations append to the section unless otherwise specified. +#[allow(clippy::len_without_is_empty)] +pub trait Writer { + /// The endianity of bytes that are written. + type Endian: Endianity; + + /// Return the endianity of bytes that are written. + fn endian(&self) -> Self::Endian; + + /// Return the current section length. + /// + /// This may be used as an offset for future `write_at` calls. + fn len(&self) -> usize; + + /// Write a slice. + fn write(&mut self, bytes: &[u8]) -> Result<()>; + + /// Write a slice at a given offset. + /// + /// The write must not extend past the current section length. + fn write_at(&mut self, offset: usize, bytes: &[u8]) -> Result<()>; + + /// Write an address. + /// + /// If the writer supports relocations, then it must provide its own implementation + /// of this method. + // TODO: use write_reference instead? + fn write_address(&mut self, address: Address, size: u8) -> Result<()> { + match address { + Address::Constant(val) => self.write_udata(val, size), + Address::Symbol { .. } => Err(Error::InvalidAddress), + } + } + + /// Write an address with a `.eh_frame` pointer encoding. + /// + /// The given size is only used for `DW_EH_PE_absptr` formats. + /// + /// If the writer supports relocations, then it must provide its own implementation + /// of this method. + fn write_eh_pointer( + &mut self, + address: Address, + eh_pe: constants::DwEhPe, + size: u8, + ) -> Result<()> { + match address { + Address::Constant(val) => { + // Indirect doesn't matter here. + let val = match eh_pe.application() { + constants::DW_EH_PE_absptr => val, + constants::DW_EH_PE_pcrel => { + // TODO: better handling of sign + let offset = self.len() as u64; + val.wrapping_sub(offset) + } + _ => { + return Err(Error::UnsupportedPointerEncoding(eh_pe)); + } + }; + self.write_eh_pointer_data(val, eh_pe.format(), size) + } + Address::Symbol { .. } => Err(Error::InvalidAddress), + } + } + + /// Write a value with a `.eh_frame` pointer format. + /// + /// The given size is only used for `DW_EH_PE_absptr` formats. + /// + /// This must not be used directly for values that may require relocation. + fn write_eh_pointer_data( + &mut self, + val: u64, + format: constants::DwEhPe, + size: u8, + ) -> Result<()> { + match format { + constants::DW_EH_PE_absptr => self.write_udata(val, size), + constants::DW_EH_PE_uleb128 => self.write_uleb128(val), + constants::DW_EH_PE_udata2 => self.write_udata(val, 2), + constants::DW_EH_PE_udata4 => self.write_udata(val, 4), + constants::DW_EH_PE_udata8 => self.write_udata(val, 8), + constants::DW_EH_PE_sleb128 => self.write_sleb128(val as i64), + constants::DW_EH_PE_sdata2 => self.write_sdata(val as i64, 2), + constants::DW_EH_PE_sdata4 => self.write_sdata(val as i64, 4), + constants::DW_EH_PE_sdata8 => self.write_sdata(val as i64, 8), + _ => Err(Error::UnsupportedPointerEncoding(format)), + } + } + + /// Write an offset that is relative to the start of the given section. + /// + /// If the writer supports relocations, then it must provide its own implementation + /// of this method. + fn write_offset(&mut self, val: usize, _section: SectionId, size: u8) -> Result<()> { + self.write_udata(val as u64, size) + } + + /// Write an offset that is relative to the start of the given section. + /// + /// If the writer supports relocations, then it must provide its own implementation + /// of this method. + fn write_offset_at( + &mut self, + offset: usize, + val: usize, + _section: SectionId, + size: u8, + ) -> Result<()> { + self.write_udata_at(offset, val as u64, size) + } + + /// Write a reference to a symbol. + /// + /// If the writer supports symbols, then it must provide its own implementation + /// of this method. + fn write_reference(&mut self, _symbol: usize, _size: u8) -> Result<()> { + Err(Error::InvalidReference) + } + + /// Write a u8. + fn write_u8(&mut self, val: u8) -> Result<()> { + let bytes = [val]; + self.write(&bytes) + } + + /// Write a u16. + fn write_u16(&mut self, val: u16) -> Result<()> { + let mut bytes = [0; 2]; + self.endian().write_u16(&mut bytes, val); + self.write(&bytes) + } + + /// Write a u32. + fn write_u32(&mut self, val: u32) -> Result<()> { + let mut bytes = [0; 4]; + self.endian().write_u32(&mut bytes, val); + self.write(&bytes) + } + + /// Write a u64. + fn write_u64(&mut self, val: u64) -> Result<()> { + let mut bytes = [0; 8]; + self.endian().write_u64(&mut bytes, val); + self.write(&bytes) + } + + /// Write a u8 at the given offset. + fn write_u8_at(&mut self, offset: usize, val: u8) -> Result<()> { + let bytes = [val]; + self.write_at(offset, &bytes) + } + + /// Write a u16 at the given offset. + fn write_u16_at(&mut self, offset: usize, val: u16) -> Result<()> { + let mut bytes = [0; 2]; + self.endian().write_u16(&mut bytes, val); + self.write_at(offset, &bytes) + } + + /// Write a u32 at the given offset. + fn write_u32_at(&mut self, offset: usize, val: u32) -> Result<()> { + let mut bytes = [0; 4]; + self.endian().write_u32(&mut bytes, val); + self.write_at(offset, &bytes) + } + + /// Write a u64 at the given offset. + fn write_u64_at(&mut self, offset: usize, val: u64) -> Result<()> { + let mut bytes = [0; 8]; + self.endian().write_u64(&mut bytes, val); + self.write_at(offset, &bytes) + } + + /// Write unsigned data of the given size. + /// + /// Returns an error if the value is too large for the size. + /// This must not be used directly for values that may require relocation. + fn write_udata(&mut self, val: u64, size: u8) -> Result<()> { + match size { + 1 => { + let write_val = val as u8; + if val != u64::from(write_val) { + return Err(Error::ValueTooLarge); + } + self.write_u8(write_val) + } + 2 => { + let write_val = val as u16; + if val != u64::from(write_val) { + return Err(Error::ValueTooLarge); + } + self.write_u16(write_val) + } + 4 => { + let write_val = val as u32; + if val != u64::from(write_val) { + return Err(Error::ValueTooLarge); + } + self.write_u32(write_val) + } + 8 => self.write_u64(val), + otherwise => Err(Error::UnsupportedWordSize(otherwise)), + } + } + + /// Write signed data of the given size. + /// + /// Returns an error if the value is too large for the size. + /// This must not be used directly for values that may require relocation. + fn write_sdata(&mut self, val: i64, size: u8) -> Result<()> { + match size { + 1 => { + let write_val = val as i8; + if val != i64::from(write_val) { + return Err(Error::ValueTooLarge); + } + self.write_u8(write_val as u8) + } + 2 => { + let write_val = val as i16; + if val != i64::from(write_val) { + return Err(Error::ValueTooLarge); + } + self.write_u16(write_val as u16) + } + 4 => { + let write_val = val as i32; + if val != i64::from(write_val) { + return Err(Error::ValueTooLarge); + } + self.write_u32(write_val as u32) + } + 8 => self.write_u64(val as u64), + otherwise => Err(Error::UnsupportedWordSize(otherwise)), + } + } + + /// Write a word of the given size at the given offset. + /// + /// Returns an error if the value is too large for the size. + /// This must not be used directly for values that may require relocation. + fn write_udata_at(&mut self, offset: usize, val: u64, size: u8) -> Result<()> { + match size { + 1 => { + let write_val = val as u8; + if val != u64::from(write_val) { + return Err(Error::ValueTooLarge); + } + self.write_u8_at(offset, write_val) + } + 2 => { + let write_val = val as u16; + if val != u64::from(write_val) { + return Err(Error::ValueTooLarge); + } + self.write_u16_at(offset, write_val) + } + 4 => { + let write_val = val as u32; + if val != u64::from(write_val) { + return Err(Error::ValueTooLarge); + } + self.write_u32_at(offset, write_val) + } + 8 => self.write_u64_at(offset, val), + otherwise => Err(Error::UnsupportedWordSize(otherwise)), + } + } + + /// Write an unsigned LEB128 encoded integer. + fn write_uleb128(&mut self, val: u64) -> Result<()> { + let mut bytes = [0u8; 10]; + // bytes is long enough so this will never fail. + let len = leb128::write::unsigned(&mut { &mut bytes[..] }, val).unwrap(); + self.write(&bytes[..len]) + } + + /// Read an unsigned LEB128 encoded integer. + fn write_sleb128(&mut self, val: i64) -> Result<()> { + let mut bytes = [0u8; 10]; + // bytes is long enough so this will never fail. + let len = leb128::write::signed(&mut { &mut bytes[..] }, val).unwrap(); + self.write(&bytes[..len]) + } + + /// Write an initial length according to the given DWARF format. + /// + /// This will only write a length of zero, since the length isn't + /// known yet, and a subsequent call to `write_initial_length_at` + /// will write the actual length. + fn write_initial_length(&mut self, format: Format) -> Result<InitialLengthOffset> { + if format == Format::Dwarf64 { + self.write_u32(0xffff_ffff)?; + } + let offset = InitialLengthOffset(self.len()); + self.write_udata(0, format.word_size())?; + Ok(offset) + } + + /// Write an initial length at the given offset according to the given DWARF format. + /// + /// `write_initial_length` must have previously returned the offset. + fn write_initial_length_at( + &mut self, + offset: InitialLengthOffset, + length: u64, + format: Format, + ) -> Result<()> { + self.write_udata_at(offset.0, length, format.word_size()) + } +} + +/// The offset at which an initial length should be written. +#[derive(Debug, Clone, Copy)] +pub struct InitialLengthOffset(usize); + +#[cfg(test)] +mod tests { + use super::*; + use crate::write; + use crate::{BigEndian, LittleEndian}; + use std::{i64, u64}; + + #[test] + fn test_writer() { + let mut w = write::EndianVec::new(LittleEndian); + w.write_address(Address::Constant(0x1122_3344), 4).unwrap(); + assert_eq!(w.slice(), &[0x44, 0x33, 0x22, 0x11]); + assert_eq!( + w.write_address( + Address::Symbol { + symbol: 0, + addend: 0 + }, + 4 + ), + Err(Error::InvalidAddress) + ); + + let mut w = write::EndianVec::new(LittleEndian); + w.write_offset(0x1122_3344, SectionId::DebugInfo, 4) + .unwrap(); + assert_eq!(w.slice(), &[0x44, 0x33, 0x22, 0x11]); + w.write_offset_at(1, 0x5566, SectionId::DebugInfo, 2) + .unwrap(); + assert_eq!(w.slice(), &[0x44, 0x66, 0x55, 0x11]); + + let mut w = write::EndianVec::new(LittleEndian); + w.write_u8(0x11).unwrap(); + w.write_u16(0x2233).unwrap(); + w.write_u32(0x4455_6677).unwrap(); + w.write_u64(0x8081_8283_8485_8687).unwrap(); + #[rustfmt::skip] + assert_eq!(w.slice(), &[ + 0x11, + 0x33, 0x22, + 0x77, 0x66, 0x55, 0x44, + 0x87, 0x86, 0x85, 0x84, 0x83, 0x82, 0x81, 0x80, + ]); + w.write_u8_at(14, 0x11).unwrap(); + w.write_u16_at(12, 0x2233).unwrap(); + w.write_u32_at(8, 0x4455_6677).unwrap(); + w.write_u64_at(0, 0x8081_8283_8485_8687).unwrap(); + #[rustfmt::skip] + assert_eq!(w.slice(), &[ + 0x87, 0x86, 0x85, 0x84, 0x83, 0x82, 0x81, 0x80, + 0x77, 0x66, 0x55, 0x44, + 0x33, 0x22, + 0x11, + ]); + + let mut w = write::EndianVec::new(BigEndian); + w.write_u8(0x11).unwrap(); + w.write_u16(0x2233).unwrap(); + w.write_u32(0x4455_6677).unwrap(); + w.write_u64(0x8081_8283_8485_8687).unwrap(); + #[rustfmt::skip] + assert_eq!(w.slice(), &[ + 0x11, + 0x22, 0x33, + 0x44, 0x55, 0x66, 0x77, + 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + ]); + w.write_u8_at(14, 0x11).unwrap(); + w.write_u16_at(12, 0x2233).unwrap(); + w.write_u32_at(8, 0x4455_6677).unwrap(); + w.write_u64_at(0, 0x8081_8283_8485_8687).unwrap(); + #[rustfmt::skip] + assert_eq!(w.slice(), &[ + 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + 0x44, 0x55, 0x66, 0x77, + 0x22, 0x33, + 0x11, + ]); + + let mut w = write::EndianVec::new(LittleEndian); + w.write_udata(0x11, 1).unwrap(); + w.write_udata(0x2233, 2).unwrap(); + w.write_udata(0x4455_6677, 4).unwrap(); + w.write_udata(0x8081_8283_8485_8687, 8).unwrap(); + #[rustfmt::skip] + assert_eq!(w.slice(), &[ + 0x11, + 0x33, 0x22, + 0x77, 0x66, 0x55, 0x44, + 0x87, 0x86, 0x85, 0x84, 0x83, 0x82, 0x81, 0x80, + ]); + assert_eq!(w.write_udata(0x100, 1), Err(Error::ValueTooLarge)); + assert_eq!(w.write_udata(0x1_0000, 2), Err(Error::ValueTooLarge)); + assert_eq!(w.write_udata(0x1_0000_0000, 4), Err(Error::ValueTooLarge)); + assert_eq!(w.write_udata(0x00, 3), Err(Error::UnsupportedWordSize(3))); + w.write_udata_at(14, 0x11, 1).unwrap(); + w.write_udata_at(12, 0x2233, 2).unwrap(); + w.write_udata_at(8, 0x4455_6677, 4).unwrap(); + w.write_udata_at(0, 0x8081_8283_8485_8687, 8).unwrap(); + #[rustfmt::skip] + assert_eq!(w.slice(), &[ + 0x87, 0x86, 0x85, 0x84, 0x83, 0x82, 0x81, 0x80, + 0x77, 0x66, 0x55, 0x44, + 0x33, 0x22, + 0x11, + ]); + assert_eq!(w.write_udata_at(0, 0x100, 1), Err(Error::ValueTooLarge)); + assert_eq!(w.write_udata_at(0, 0x1_0000, 2), Err(Error::ValueTooLarge)); + assert_eq!( + w.write_udata_at(0, 0x1_0000_0000, 4), + Err(Error::ValueTooLarge) + ); + assert_eq!( + w.write_udata_at(0, 0x00, 3), + Err(Error::UnsupportedWordSize(3)) + ); + + let mut w = write::EndianVec::new(LittleEndian); + w.write_uleb128(0).unwrap(); + assert_eq!(w.slice(), &[0]); + + let mut w = write::EndianVec::new(LittleEndian); + w.write_uleb128(u64::MAX).unwrap(); + assert_eq!( + w.slice(), + &[0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 1] + ); + + let mut w = write::EndianVec::new(LittleEndian); + w.write_sleb128(0).unwrap(); + assert_eq!(w.slice(), &[0]); + + let mut w = write::EndianVec::new(LittleEndian); + w.write_sleb128(i64::MAX).unwrap(); + assert_eq!( + w.slice(), + &[0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0] + ); + + let mut w = write::EndianVec::new(LittleEndian); + w.write_sleb128(i64::MIN).unwrap(); + assert_eq!( + w.slice(), + &[0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7f] + ); + + let mut w = write::EndianVec::new(LittleEndian); + let offset = w.write_initial_length(Format::Dwarf32).unwrap(); + assert_eq!(w.slice(), &[0, 0, 0, 0]); + w.write_initial_length_at(offset, 0x1122_3344, Format::Dwarf32) + .unwrap(); + assert_eq!(w.slice(), &[0x44, 0x33, 0x22, 0x11]); + assert_eq!( + w.write_initial_length_at(offset, 0x1_0000_0000, Format::Dwarf32), + Err(Error::ValueTooLarge) + ); + + let mut w = write::EndianVec::new(LittleEndian); + let offset = w.write_initial_length(Format::Dwarf64).unwrap(); + assert_eq!(w.slice(), &[0xff, 0xff, 0xff, 0xff, 0, 0, 0, 0, 0, 0, 0, 0]); + w.write_initial_length_at(offset, 0x1122_3344_5566_7788, Format::Dwarf64) + .unwrap(); + assert_eq!( + w.slice(), + &[0xff, 0xff, 0xff, 0xff, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11] + ); + } +} |