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}
;
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
>;
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
)?
))
26 pub struct Object
<'a
> {
27 /// Zero-sized type representing the native endianness.
29 /// We could use a literal instead, but this helps ensure correctness.
31 /// The entire file data.
33 sections
: SectionTable
<'a
, Elf
>,
34 strings
: StringTable
<'a
>,
35 /// List of pre-parsed and sorted symbols by base address.
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
)
50 .symbols(endian
, data
, object
::elf
::SHT_DYNSYM
)
53 let strings
= syms
.strings();
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?
63 let st_type
= sym
.st_type();
64 st_type
== object
::elf
::STT_FUNC
|| st_type
== object
::elf
::STT_OBJECT
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
)
71 let address
= sym
.st_value(endian
).into();
72 let size
= sym
.st_size(endian
).into();
73 let name
= sym
.st_name(endian
);
81 syms
.sort_unstable_by_key(|s
| s
.address
);
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()?
;
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 {
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.
108 let size
= usize::try_from(header
.ch_size(self.endian
)).ok()?
;
109 let buf
= stash
.allocate(size
);
110 decompress_zlib(data
.0, buf
)?
;
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_") {
121 let debug_name
= name
[7..].as_bytes();
122 let compressed_section
= self
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
{
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" {
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
)?
;
144 fn section_header(&self, name
: &str) -> Option
<&<Elf
as FileHeader
>::SectionHeader
> {
146 .section_by_name(self.endian
, name
.as_bytes())
147 .map(|(_index
, section
)| section
)
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
) {
154 Err(i
) => i
.checked_sub(1)?
,
156 let sym
= self.syms
.get(i
)?
;
157 if sym
.address
<= addr
&& addr
<= sym
.address
+ sym
.size
{
158 self.strings
.get(sym
.name
).ok()
164 pub(super) fn search_object_map(&self, _addr
: u64) -> Option
<(&Context
<'_
>, u64)> {
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
,
173 use miniz_oxide
::inflate
::core
::{decompress, DecompressorOxide}
;
174 use miniz_oxide
::inflate
::TINFLStatus
;
176 let (status
, in_read
, out_read
) = decompress(
177 &mut DecompressorOxide
::new(),
181 TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF
| TINFL_FLAG_PARSE_ZLIB_HEADER
,
183 if status
== TINFLStatus
::Done
&& in_read
== input
.len() && out_read
== output
.len() {