1use crate::align::align_up;
2use core::mem;
3use core::mem::MaybeUninit;
4use core::{convert::TryInto, fmt};
5use log::{debug, error, warn};
6
7#[cfg(target_pointer_width = "64")]
16mod elf {
17 pub type ElfPhdr = libc::Elf64_Phdr;
18}
19#[cfg(target_pointer_width = "32")]
20mod elf {
21 pub type ElfPhdr = libc::Elf32_Phdr;
22}
23use elf::*;
24
25const NT_GNU_BUILD_ID: u32 = 3;
26
27#[derive(Debug)]
29struct Note {
30 data: [u8],
31}
32
33const MIN_NOTE_SIZE: usize = mem::size_of::<usize>() * 3;
34
35#[derive(Debug)]
36enum NoteError {
37 MissingHeader { size: usize },
38 Truncated { have: usize, need: usize },
39}
40
41impl fmt::Display for NoteError {
42 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
43 match self {
44 Self::MissingHeader { size } => write!(
45 f,
46 "have {} bytes, but need at least {}",
47 size, MIN_NOTE_SIZE
48 ),
49 Self::Truncated { have, need } => {
50 write!(f, "have {} bytes, but need at least {}", have, need)
51 }
52 }
53 }
54}
55
56impl Note {
57 const ALIGN: usize = 4;
60
61 fn from_bytes_raw(data: &[u8]) -> &Self {
63 unsafe { &*(data as *const [u8] as *const Note) }
64 }
65
66 fn from_bytes(data: &[u8]) -> Result<(&Self, &[u8]), NoteError> {
67 let u = core::mem::size_of::<u32>();
68 if data.len() < u * 3 {
69 return Err(NoteError::MissingHeader { size: data.len() });
70 }
71
72 Self::from_bytes_raw(data).split_trailing()
73 }
74
75 fn name_len(&self) -> usize {
76 u32::from_ne_bytes(self.data[..core::mem::size_of::<u32>()].try_into().unwrap()) as usize
77 }
78
79 fn desc_len(&self) -> usize {
80 let u = core::mem::size_of::<u32>();
81 u32::from_ne_bytes(self.data[u..(u + u)].try_into().unwrap()) as usize
82 }
83
84 fn type_(&self) -> u32 {
85 let u = core::mem::size_of::<u32>();
86 u32::from_ne_bytes(self.data[(u + u)..(u + u + u)].try_into().unwrap())
87 }
88
89 fn name(&self) -> &[u8] {
90 let u = core::mem::size_of::<u32>();
91 let b = u * 3;
92 &self.data[b..(b + self.name_len())]
93 }
94
95 fn desc(&self) -> &[u8] {
96 let u = core::mem::size_of::<u32>();
97 let b = u * 3 + align_up(self.name_len(), Self::ALIGN);
98 &self.data[b..(b + self.desc_len())]
99 }
100
101 fn split_trailing(&self) -> Result<(&Self, &[u8]), NoteError> {
102 let u = core::mem::size_of::<u32>();
103 let end =
104 u * 3 + align_up(self.name_len(), Self::ALIGN) + align_up(self.desc_len(), Self::ALIGN);
105 if end > self.data.len() {
106 Err(NoteError::Truncated {
107 need: end,
108 have: self.data.len(),
109 })
110 } else {
111 Ok((Self::from_bytes_raw(&self.data[0..end]), &self.data[end..]))
112 }
113 }
114}
115
116unsafe extern "C" fn phdr_cb(
119 info: *mut libc::dl_phdr_info,
120 size: libc::size_t,
121 data: *mut libc::c_void,
122) -> libc::c_int {
123 let closure: &mut &mut dyn FnMut(&libc::dl_phdr_info, usize) -> libc::c_int = &mut *(data
124 as *mut &mut dyn for<'r> core::ops::FnMut(&'r libc::dl_phdr_info, usize) -> i32);
125 let info = &*info;
126
127 closure(info, size)
128}
129
130fn object_map<F: FnMut(&'static libc::dl_phdr_info, usize) -> libc::c_int>(
131 mut cb: F,
132) -> libc::c_int {
133 let mut cb: &mut dyn FnMut(&'static libc::dl_phdr_info, usize) -> libc::c_int = &mut cb;
134 let cb = &mut cb;
135 unsafe { libc::dl_iterate_phdr(Some(phdr_cb), cb as *mut _ as *mut _) }
136}
137
138struct PhdrIter<'a> {
140 info: &'a libc::dl_phdr_info,
141 i: u16,
142}
143
144impl<'a> Iterator for PhdrIter<'a> {
145 type Item = &'a libc::Elf64_Phdr;
146
147 fn next(&mut self) -> Option<Self::Item> {
148 if self.i >= self.info.dlpi_phnum {
149 return None;
150 }
151
152 let phdr = unsafe { &*self.info.dlpi_phdr.add(self.i as usize) };
153 self.i += 1;
154 Some(phdr)
155 }
156}
157
158impl<'a> From<&'a libc::dl_phdr_info> for PhdrIter<'a> {
159 fn from(info: &'a libc::dl_phdr_info) -> Self {
160 PhdrIter { info, i: 0 }
161 }
162}
163
164#[derive(Debug)]
166struct NoteIter<'a> {
167 segment: &'a [u8],
168}
169
170impl<'a> NoteIter<'a> {
171 fn new(info: &'a libc::dl_phdr_info, phdr: &'a ElfPhdr) -> Option<Self> {
172 if phdr.p_type != libc::PT_NOTE {
178 None
179 } else {
180 let segment_base = (info.dlpi_addr + phdr.p_vaddr) as *const u8;
181 let segment = unsafe {
182 core::slice::from_raw_parts(segment_base, phdr.p_filesz as usize)
185 };
186 Some(NoteIter { segment })
187 }
188 }
189}
190
191impl<'a> Iterator for NoteIter<'a> {
192 type Item = Result<&'a Note, NoteError>;
193 fn next(&mut self) -> Option<Self::Item> {
194 if self.segment.is_empty() {
195 return None;
196 }
197
198 let (n, r) = match Note::from_bytes(self.segment) {
199 Err(e) => return Some(Err(e)),
200 Ok(v) => v,
201 };
202
203 self.segment = r;
204
205 Some(Ok(n))
206 }
207}
208
209pub fn build_id() -> Option<&'static [u8]> {
210 let data = {
212 let mut data = MaybeUninit::uninit();
213 let addr = build_id as *const libc::c_void;
214 if unsafe { libc::dladdr(addr, data.as_mut_ptr()) } == 0 {
215 error!("dladdr failed to find our own symbol");
217 return None;
218 }
219
220 unsafe { data.assume_init() }
221 };
222
223 let mut res = None;
224 object_map(|info, _size| {
228 let mut map_start = None;
229
230 for phdr in PhdrIter::from(info) {
231 if phdr.p_type == libc::PT_LOAD {
233 map_start = Some(info.dlpi_addr + phdr.p_vaddr);
234 break;
235 }
236 }
237
238 let map_start = match map_start {
239 Some(v) => v,
240 None => {
241 debug!(
242 "no PT_LOAD segment found in object {:?}, skipping",
243 info.dlpi_name
244 );
245 return 0;
246 }
247 };
248
249 if map_start != data.dli_fbase as u64 {
251 debug!(
252 "map_start ({:?}) != data.dli_fbase ({:?}), skipping",
253 map_start, data.dli_fbase
254 );
255 return 0;
256 }
257
258 'phdr: for phdr in PhdrIter::from(info) {
259 let ni = match NoteIter::new(info, phdr) {
260 Some(v) => v,
261 None => continue,
262 };
263
264 for note in ni {
266 let note = match note {
267 Err(e) => {
268 warn!("note program segment had invalid note {}", e);
269 continue 'phdr;
270 }
271 Ok(v) => v,
272 };
273 if note.type_() == NT_GNU_BUILD_ID
274 && !note.desc().is_empty()
275 && note.name() == b"GNU\0"
276 {
277 res = Some(note.desc());
278 break 'phdr;
279 }
280 }
281 }
282
283 0
284 });
285
286 res
287}