diff --git a/src/read/aranges.rs b/src/read/aranges.rs index c2aac914..2829f261 100644 --- a/src/read/aranges.rs +++ b/src/read/aranges.rs @@ -290,6 +290,8 @@ impl ArangeEntryIter { #[doc(hidden)] pub fn convert_raw(&self, mut entry: ArangeEntry) -> Result> { // Skip tombstone entries. + // DWARF specifies a tombstone value of -1, but many linkers use 0. + // However, 0 may be a valid address, so the caller must handle that case. let address_size = self.encoding.address_size; let tombstone_address = !0 >> (64 - self.encoding.address_size * 8); if entry.range.begin == tombstone_address { diff --git a/src/read/line.rs b/src/read/line.rs index ceaf3b3f..1e91b020 100644 --- a/src/read/line.rs +++ b/src/read/line.rs @@ -828,12 +828,16 @@ impl LineRow { } LineInstruction::SetAddress(address) => { + // If the address is a tombstone, then skip instructions until the next address. + // DWARF specifies a tombstone value of -1, but many linkers use 0. + // However, 0 may be a valid address, so we only skip that if we have previously + // seen a higher address. Additionally, gold may keep the relocation addend, + // so we treat all lower addresses as tombstones instead of just 0. + // This works because DWARF specifies that addresses are monotonically increasing + // within a sequence; the alternative is to return an error. let tombstone_address = !0 >> (64 - program.header().encoding.address_size * 8); - self.tombstone = address == tombstone_address; + self.tombstone = address < self.address || address == tombstone_address; if !self.tombstone { - if address < self.address { - return Err(Error::InvalidAddressRange); - } self.address = address; self.op_index.0 = 0; } @@ -2877,13 +2881,14 @@ mod tests { #[test] fn test_exec_set_address_backwards() { let header = make_test_header(EndianSlice::new(&[], LittleEndian)); - let mut registers = LineRow::new(&header); - registers.address = 1; + let mut initial_registers = LineRow::new(&header); + initial_registers.address = 1; let opcode = LineInstruction::SetAddress(0); - let mut program = IncompleteLineProgram { header }; - let result = registers.execute(opcode, &mut program); - assert_eq!(result, Err(Error::InvalidAddressRange)); + let mut expected_registers = initial_registers; + expected_registers.tombstone = true; + + assert_exec_opcode(header, initial_registers, opcode, expected_registers, false); } #[test] diff --git a/src/read/loclists.rs b/src/read/loclists.rs index a2874687..99cf0b43 100644 --- a/src/read/loclists.rs +++ b/src/read/loclists.rs @@ -595,6 +595,9 @@ impl LocListIter { ) -> Result>> { let address_size = self.raw.encoding.address_size; let mask = u64::ones_sized(address_size); + // Skip tombstone entries. + // DWARF specifies a tombstone value of -1 or -2, but many linkers use 0 or 1. + // However, 0/1 may be a valid address, so the caller must handle that case. let tombstone = if self.raw.encoding.version <= 4 { mask - 1 } else { diff --git a/src/read/rnglists.rs b/src/read/rnglists.rs index 47d2a44b..3b12d124 100644 --- a/src/read/rnglists.rs +++ b/src/read/rnglists.rs @@ -529,6 +529,9 @@ impl RngListIter { pub fn convert_raw(&mut self, raw_range: RawRngListEntry) -> Result> { let address_size = self.raw.encoding.address_size; let mask = u64::ones_sized(address_size); + // Skip tombstone entries. + // DWARF specifies a tombstone value of -1 or -2, but many linkers use 0 or 1. + // However, 0/1 may be a valid address, so the caller must handle that case. let tombstone = if self.raw.encoding.version <= 4 { mask - 1 } else {