]> git.proxmox.com Git - rustc.git/blob - library/backtrace/src/symbolize/gimli/elf.rs
New upstream version 1.50.0+dfsg1
[rustc.git] / library / backtrace / src / symbolize / gimli / elf.rs
1 use super::{Context, Mapping, Path, Stash, Vec};
2 use core::convert::TryFrom;
3 use object::elf::{ELFCOMPRESS_ZLIB, SHF_COMPRESSED};
4 use object::read::elf::{CompressionHeader, FileHeader, SectionHeader, SectionTable, Sym};
5 use object::read::StringTable;
6 use object::{BigEndian, Bytes, NativeEndian};
7
8 #[cfg(target_pointer_width = "32")]
9 type Elf = object::elf::FileHeader32<NativeEndian>;
10 #[cfg(target_pointer_width = "64")]
11 type Elf = object::elf::FileHeader64<NativeEndian>;
12
13 impl Mapping {
14 pub fn new(path: &Path) -> Option<Mapping> {
15 let map = super::mmap(path)?;
16 Mapping::mk(map, |data, stash| Context::new(stash, Object::parse(data)?))
17 }
18 }
19
20 struct ParsedSym {
21 address: u64,
22 size: u64,
23 name: u32,
24 }
25
26 pub struct Object<'a> {
27 /// Zero-sized type representing the native endianness.
28 ///
29 /// We could use a literal instead, but this helps ensure correctness.
30 endian: NativeEndian,
31 /// The entire file data.
32 data: Bytes<'a>,
33 sections: SectionTable<'a, Elf>,
34 strings: StringTable<'a>,
35 /// List of pre-parsed and sorted symbols by base address.
36 syms: Vec<ParsedSym>,
37 }
38
39 impl<'a> Object<'a> {
40 fn parse(data: &'a [u8]) -> Option<Object<'a>> {
41 let data = object::Bytes(data);
42 let elf = Elf::parse(data).ok()?;
43 let endian = elf.endian().ok()?;
44 let sections = elf.sections(endian, data).ok()?;
45 let mut syms = sections
46 .symbols(endian, data, object::elf::SHT_SYMTAB)
47 .ok()?;
48 if syms.is_empty() {
49 syms = sections
50 .symbols(endian, data, object::elf::SHT_DYNSYM)
51 .ok()?;
52 }
53 let strings = syms.strings();
54
55 let mut syms = syms
56 .iter()
57 // Only look at function/object symbols. This mirrors what
58 // libbacktrace does and in general we're only symbolicating
59 // function addresses in theory. Object symbols correspond
60 // to data, and maybe someone's crazy enough to have a
61 // function go into static data?
62 .filter(|sym| {
63 let st_type = sym.st_type();
64 st_type == object::elf::STT_FUNC || st_type == object::elf::STT_OBJECT
65 })
66 // skip anything that's in an undefined section header,
67 // since it means it's an imported function and we're only
68 // symbolicating with locally defined functions.
69 .filter(|sym| sym.st_shndx(endian) != object::elf::SHN_UNDEF)
70 .map(|sym| {
71 let address = sym.st_value(endian).into();
72 let size = sym.st_size(endian).into();
73 let name = sym.st_name(endian);
74 ParsedSym {
75 address,
76 size,
77 name,
78 }
79 })
80 .collect::<Vec<_>>();
81 syms.sort_unstable_by_key(|s| s.address);
82 Some(Object {
83 endian,
84 data,
85 sections,
86 strings,
87 syms,
88 })
89 }
90
91 pub fn section(&self, stash: &'a Stash, name: &str) -> Option<&'a [u8]> {
92 if let Some(section) = self.section_header(name) {
93 let mut data = section.data(self.endian, self.data).ok()?;
94
95 // Check for DWARF-standard (gABI) compression, i.e., as generated
96 // by ld's `--compress-debug-sections=zlib-gabi` flag.
97 let flags: u64 = section.sh_flags(self.endian).into();
98 if (flags & u64::from(SHF_COMPRESSED)) == 0 {
99 // Not compressed.
100 return Some(data.0);
101 }
102
103 let header = data.read::<<Elf as FileHeader>::CompressionHeader>().ok()?;
104 if header.ch_type(self.endian) != ELFCOMPRESS_ZLIB {
105 // Zlib compression is the only known type.
106 return None;
107 }
108 let size = usize::try_from(header.ch_size(self.endian)).ok()?;
109 let buf = stash.allocate(size);
110 decompress_zlib(data.0, buf)?;
111 return Some(buf);
112 }
113
114 // Check for the nonstandard GNU compression format, i.e., as generated
115 // by ld's `--compress-debug-sections=zlib-gnu` flag. This means that if
116 // we're actually asking for `.debug_info` then we need to look up a
117 // section named `.zdebug_info`.
118 if !name.starts_with(".debug_") {
119 return None;
120 }
121 let debug_name = name[7..].as_bytes();
122 let compressed_section = self
123 .sections
124 .iter()
125 .filter_map(|header| {
126 let name = self.sections.section_name(self.endian, header).ok()?;
127 if name.starts_with(b".zdebug_") && &name[8..] == debug_name {
128 Some(header)
129 } else {
130 None
131 }
132 })
133 .next()?;
134 let mut data = compressed_section.data(self.endian, self.data).ok()?;
135 if data.read_bytes(8).ok()?.0 != b"ZLIB\0\0\0\0" {
136 return None;
137 }
138 let size = usize::try_from(data.read::<object::U32Bytes<_>>().ok()?.get(BigEndian)).ok()?;
139 let buf = stash.allocate(size);
140 decompress_zlib(data.0, buf)?;
141 Some(buf)
142 }
143
144 fn section_header(&self, name: &str) -> Option<&<Elf as FileHeader>::SectionHeader> {
145 self.sections
146 .section_by_name(self.endian, name.as_bytes())
147 .map(|(_index, section)| section)
148 }
149
150 pub fn search_symtab<'b>(&'b self, addr: u64) -> Option<&'b [u8]> {
151 // Same sort of binary search as Windows above
152 let i = match self.syms.binary_search_by_key(&addr, |sym| sym.address) {
153 Ok(i) => i,
154 Err(i) => i.checked_sub(1)?,
155 };
156 let sym = self.syms.get(i)?;
157 if sym.address <= addr && addr <= sym.address + sym.size {
158 self.strings.get(sym.name).ok()
159 } else {
160 None
161 }
162 }
163
164 pub(super) fn search_object_map(&self, _addr: u64) -> Option<(&Context<'_>, u64)> {
165 None
166 }
167 }
168
169 fn decompress_zlib(input: &[u8], output: &mut [u8]) -> Option<()> {
170 use miniz_oxide::inflate::core::inflate_flags::{
171 TINFL_FLAG_PARSE_ZLIB_HEADER, TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF,
172 };
173 use miniz_oxide::inflate::core::{decompress, DecompressorOxide};
174 use miniz_oxide::inflate::TINFLStatus;
175
176 let (status, in_read, out_read) = decompress(
177 &mut DecompressorOxide::new(),
178 input,
179 output,
180 0,
181 TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF | TINFL_FLAG_PARSE_ZLIB_HEADER,
182 );
183 if status == TINFLStatus::Done && in_read == input.len() && out_read == output.len() {
184 Some(())
185 } else {
186 None
187 }
188 }