Skip to main content

c/
dl_extra.rs

1// dladdr / dl_iterate_phdr / dlinfo.
2
3#[repr(C)]
4pub struct DlInfo {
5    pub dli_fname: *const c_char,
6    pub dli_fbase: *mut c_void,
7    pub dli_sname: *const c_char,
8    pub dli_saddr: *mut c_void,
9}
10
11#[repr(C)]
12pub struct DlPhdrInfo {
13    pub dlpi_addr: u64,
14    pub dlpi_name: *const c_char,
15    pub dlpi_phdr: *const u8,
16    pub dlpi_phnum: u16,
17    __pad0: u16,
18    __pad1: u32,
19    pub dlpi_adds: u64,
20    pub dlpi_subs: u64,
21    pub dlpi_tls_modid: usize,
22    pub dlpi_tls_data: *mut c_void,
23}
24
25type DlIterateCb = unsafe extern "C" fn(*mut DlPhdrInfo, usize, *mut c_void) -> c_int;
26type LdsoIterateFn = unsafe extern "C" fn(Option<DlIterateCb>, *mut c_void) -> c_int;
27type LdsoDlinfoFn = unsafe extern "C" fn(*mut c_void, c_int, *mut c_void) -> c_int;
28
29static mut LDSO_ITERATE: Option<LdsoIterateFn> = None;
30static mut LDSO_DLINFO: Option<LdsoDlinfoFn> = None;
31
32#[no_mangle]
33pub unsafe extern "C" fn __ldso_register_dl_iterate(f: LdsoIterateFn) {
34    LDSO_ITERATE = Some(f);
35}
36
37#[no_mangle]
38pub unsafe extern "C" fn __ldso_register_dlinfo(f: LdsoDlinfoFn) {
39    LDSO_DLINFO = Some(f);
40}
41
42// ------------------------------------------------------------------
43// /proc/self/maps
44// ------------------------------------------------------------------
45
46static mut MAPS_PATH: [c_char; 512] = [0; 512];
47
48unsafe fn maps_parse_hex(p: *const u8) -> (u64, *const u8) {
49    let mut v: u64 = 0;
50    let mut q = p;
51    loop {
52        let c = *q;
53        let d = if c >= b'0' && c <= b'9' {
54            (c - b'0') as u64
55        } else if c >= b'a' && c <= b'f' {
56            (c - b'a' + 10) as u64
57        } else if c >= b'A' && c <= b'F' {
58            (c - b'A' + 10) as u64
59        } else {
60            break;
61        };
62        v = (v << 4) | d;
63        q = q.add(1);
64    }
65    (v, q)
66}
67
68/// Find the mapping containing `addr`: (base, path). The path is copied into
69/// MAPS_PATH and must be consumed before the next call.
70unsafe fn maps_lookup(addr: usize) -> (u64, u64, *const c_char, bool) {
71    MAPS_PATH[0] = 0;
72    let f = crate::fopen(b"/proc/self/maps\0".as_ptr() as *const c_char, b"r\0".as_ptr() as *const c_char);
73    if f.is_null() { return (0, 0, MAPS_PATH.as_ptr(), false); }
74    let mut line = [0 as c_char; 1024];
75    let mut start_out = 0u64;
76    let mut off_out = 0u64;
77    let mut found = false;
78    while !crate::fgets(line.as_mut_ptr(), line.len() as c_int, f).is_null() {
79        let p = line.as_ptr() as *const u8;
80        let (start, after) = maps_parse_hex(p);
81        if *after != b'-' { continue; }
82        let (end, after2) = maps_parse_hex(after.add(1));
83        if (addr as u64) < start || (addr as u64) >= end { continue; }
84        // Field order after the range: perms offset dev inode path.
85        let mut q = after2;
86        while *q == b' ' { q = q.add(1); }
87        while *q != 0 && *q != b' ' { q = q.add(1); }
88        while *q == b' ' { q = q.add(1); }
89        let (off, after_off) = maps_parse_hex(q);
90        start_out = start;
91        off_out = off;
92        let mut q2 = after_off;
93        for _ in 0..2 {
94            while *q2 == b' ' { q2 = q2.add(1); }
95            while *q2 != 0 && *q2 != b' ' { q2 = q2.add(1); }
96        }
97        while *q2 == b' ' { q2 = q2.add(1); }
98        if *q2 != 0 {
99            let mut i = 0usize;
100            while *q2 != 0 && *q2 != b'\n' && i + 1 < MAPS_PATH.len() {
101                MAPS_PATH[i] = *q2 as c_char;
102                i += 1;
103                q2 = q2.add(1);
104            }
105            MAPS_PATH[i] = 0;
106        }
107        found = true;
108        break;
109    }
110    crate::fclose(f);
111    (start_out, off_out, MAPS_PATH.as_ptr(), found)
112}
113
114/// Compute the load bias for a mapping given the mapping start and file offset.
115/// For a PT_LOAD segment, start = bias + p_vaddr - adj and off = p_offset - adj,
116/// so bias = (start - off) - (p_vaddr - p_offset).
117unsafe fn elf_bias_for(path: *const c_char, start: u64, off: u64) -> u64 {
118    let fd = crate::open(path, 0, 0);
119    if fd < 0 { return start.wrapping_sub(off); }
120    let mut eh = [0u8; 64];
121    if crate::pread(fd, eh.as_mut_ptr() as *mut c_void, 64, 0) != 64 || eh[0] != 0x7f {
122        crate::close(fd);
123        return start.wrapping_sub(off);
124    }
125    let e_phoff = rd64(eh.as_ptr().add(0x20));
126    let e_phentsize = rd16(eh.as_ptr().add(0x36)) as usize;
127    let e_phnum = rd16(eh.as_ptr().add(0x38)) as usize;
128    if e_phentsize != 56 || e_phnum == 0 || e_phnum > 512 {
129        crate::close(fd);
130        return start.wrapping_sub(off);
131    }
132    let total = e_phnum * e_phentsize;
133    let ph = crate::malloc(total) as *mut u8;
134    if ph.is_null() {
135        crate::close(fd);
136        return start.wrapping_sub(off);
137    }
138    let got = crate::pread(fd, ph as *mut c_void, total, e_phoff as i64);
139    crate::close(fd);
140    if got != total as isize {
141        crate::free(ph as *mut c_void);
142        return start.wrapping_sub(off);
143    }
144    let mut bias = start.wrapping_sub(off);
145    for i in 0..e_phnum {
146        let p = ph.add(i * e_phentsize);
147        let p_type = rd32(p);
148        if p_type != 1 { continue; }
149        let p_offset = rd64(p.add(8));
150        let p_vaddr = rd64(p.add(0x10));
151        // The loader maps each segment at file offset p_offset - (p_vaddr&0xfff).
152        let map_off = p_offset.wrapping_sub(p_vaddr & 0xfff);
153        if off == map_off {
154            bias = start.wrapping_sub(off).wrapping_sub(p_vaddr.wrapping_sub(p_offset));
155            break;
156        }
157    }
158    crate::free(ph as *mut c_void);
159    bias
160}
161
162// ------------------------------------------------------------------
163// ELF symbol lookup (for dladdr)
164// ------------------------------------------------------------------
165
166static mut DLADDR_NAME: [c_char; 512] = [0; 512];
167
168unsafe fn rd16(p: *const u8) -> u16 { core::ptr::read_unaligned(p as *const u16) }
169unsafe fn rd32(p: *const u8) -> u32 { core::ptr::read_unaligned(p as *const u32) }
170unsafe fn rd64(p: *const u8) -> u64 { core::ptr::read_unaligned(p as *const u64) }
171
172unsafe fn elf_symbol_lookup(path: *const c_char, addr: u64) -> (bool, u64) {
173    let fd = crate::open(path, 0, 0);
174    if fd < 0 { return (false, 0); }
175    let mut eh = [0u8; 64];
176    if crate::pread(fd, eh.as_mut_ptr() as *mut c_void, 64, 0) != 64 {
177        crate::close(fd);
178        return (false, 0);
179    }
180    if eh[0] != 0x7f || eh[1] != b'E' || eh[2] != b'L' || eh[3] != b'F' {
181        crate::close(fd);
182        return (false, 0);
183    }
184    let e_shoff = rd64(eh.as_ptr().add(0x28));
185    let e_shentsize = rd16(eh.as_ptr().add(0x3a)) as usize;
186    let e_shnum = rd16(eh.as_ptr().add(0x3c)) as usize;
187    if e_shoff == 0 || e_shentsize < 64 || e_shnum == 0 || e_shnum > 4096 {
188        crate::close(fd);
189        return (false, 0);
190    }
191    let shtotal = e_shnum * e_shentsize;
192    let sh = crate::malloc(shtotal) as *mut u8;
193    if sh.is_null() { crate::close(fd); return (false, 0); }
194    if crate::pread(fd, sh as *mut c_void, shtotal, e_shoff as i64) != shtotal as isize {
195        crate::free(sh as *mut c_void);
196        crate::close(fd);
197        return (false, 0);
198    }
199    let mut best: u64 = 0;
200    let mut have = false;
201    for i in 0..e_shnum {
202        let sec = sh.add(i * e_shentsize);
203        let sh_type = rd32(sec.add(4));
204        if sh_type != 2 && sh_type != 11 { continue; }
205        let sh_offset = rd64(sec.add(0x18));
206        let sh_size = rd64(sec.add(0x20)) as usize;
207        let sh_link = rd32(sec.add(0x28)) as usize;
208        let sh_entsize = rd64(sec.add(0x38)) as usize;
209        if sh_size == 0 || sh_entsize < 24 || sh_link >= e_shnum { continue; }
210        let syms = crate::malloc(sh_size) as *mut u8;
211        if syms.is_null() { continue; }
212        let got = crate::pread(fd, syms as *mut c_void, sh_size, sh_offset as i64);
213        if got != sh_size as isize {
214            crate::free(syms as *mut c_void);
215            continue;
216        }
217        let strsec = sh.add(sh_link * e_shentsize);
218        let str_off = rd64(strsec.add(0x18));
219        let str_size = rd64(strsec.add(0x20)) as usize;
220        if str_size == 0 || str_size > (1 << 24) {
221            crate::free(syms as *mut c_void);
222            continue;
223        }
224        let strs = crate::malloc(str_size) as *mut u8;
225        if strs.is_null() {
226            crate::free(syms as *mut c_void);
227            continue;
228        }
229        if crate::pread(fd, strs as *mut c_void, str_size, str_off as i64) != str_size as isize {
230            crate::free(strs as *mut c_void);
231            crate::free(syms as *mut c_void);
232            continue;
233        }
234        let count = sh_size / sh_entsize;
235        for j in 0..count {
236            let sym = syms.add(j * sh_entsize);
237            let st_name = rd32(sym) as usize;
238            let st_value = rd64(sym.add(8));
239            let st_size = rd64(sym.add(16));
240            let st_shndx = rd16(sym.add(6));
241            if st_name == 0 || st_name >= str_size || st_shndx == 0 { continue; }
242            if st_value > addr { continue; }
243            if st_size != 0 && addr >= st_value + st_size { continue; }
244            if !have || st_value > best {
245                best = st_value;
246                have = true;
247                let s = strs.add(st_name);
248                let mut k = 0usize;
249                while *s.add(k) != 0 && k + 1 < DLADDR_NAME.len() {
250                    DLADDR_NAME[k] = *s.add(k) as c_char;
251                    k += 1;
252                }
253                DLADDR_NAME[k] = 0;
254            }
255        }
256        crate::free(strs as *mut c_void);
257        crate::free(syms as *mut c_void);
258    }
259    crate::free(sh as *mut c_void);
260    crate::close(fd);
261    (have, best)
262}
263
264#[no_mangle]
265pub unsafe extern "C" fn dladdr(addr: *const c_void, info: *mut DlInfo) -> c_int {
266    if info.is_null() { ERRNO = EINVAL; return 0; }
267    (*info).dli_fname = core::ptr::null();
268    (*info).dli_fbase = core::ptr::null_mut();
269    (*info).dli_sname = core::ptr::null();
270    (*info).dli_saddr = core::ptr::null_mut();
271    if addr.is_null() { return 0; }
272    let (start, off, path, found) = maps_lookup(addr as usize);
273    if !found { return 0; }
274    let base = if MAPS_PATH[0] != 0 && MAPS_PATH[0] != b'[' as c_char {
275        elf_bias_for(path, start, off)
276    } else {
277        start.wrapping_sub(off)
278    };
279    (*info).dli_fname = path;
280    (*info).dli_fbase = base as *mut c_void;
281    if MAPS_PATH[0] == 0 || MAPS_PATH[0] == b'[' as c_char { return 1; }
282    let off = addr as u64 - base;
283    let (have, value) = elf_symbol_lookup(path, off);
284    if have {
285        (*info).dli_sname = DLADDR_NAME.as_ptr();
286        (*info).dli_saddr = (base + value) as *mut c_void;
287    }
288    1
289}
290
291// ------------------------------------------------------------------
292// dl_iterate_phdr
293// ------------------------------------------------------------------
294
295static mut EXE_PATH: [c_char; 512] = [0; 512];
296
297unsafe fn read_exe_path() -> *const c_char {
298    let n = crate::readlink(
299        b"/proc/self/exe\0".as_ptr() as *const c_char,
300        EXE_PATH.as_mut_ptr(),
301        EXE_PATH.len() - 1,
302    );
303    if n < 0 {
304        EXE_PATH[0] = 0;
305    } else {
306        EXE_PATH[n as usize] = 0;
307    }
308    EXE_PATH.as_ptr()
309}
310
311/// Returns (load bias, phdr pointer, phnum) for the main executable.
312unsafe fn main_program_info() -> (u64, *const u8, u16) {
313    let exe = read_exe_path();
314    if *exe != 0 {
315        let f = crate::fopen(b"/proc/self/maps\0".as_ptr() as *const c_char, b"r\0".as_ptr() as *const c_char);
316        if !f.is_null() {
317            let mut line = [0 as c_char; 1024];
318            while !crate::fgets(line.as_mut_ptr(), line.len() as c_int, f).is_null() {
319                let p = line.as_ptr() as *const u8;
320                let (start, after) = maps_parse_hex(p);
321                if *after != b'-' { continue; }
322                let (_end, after2) = maps_parse_hex(after.add(1));
323                let mut q = after2;
324                while *q == b' ' { q = q.add(1); }
325                while *q != 0 && *q != b' ' { q = q.add(1); }
326                while *q == b' ' { q = q.add(1); }
327                let (off, after_off) = maps_parse_hex(q);
328                if off != 0 { continue; }
329                let mut q2 = after_off;
330                for _ in 0..2 {
331                    while *q2 == b' ' { q2 = q2.add(1); }
332                    while *q2 != 0 && *q2 != b' ' { q2 = q2.add(1); }
333                }
334                while *q2 == b' ' { q2 = q2.add(1); }
335                if *q2 == 0 { continue; }
336                let mut path = [0 as c_char; 512];
337                let mut i = 0usize;
338                while *q2 != 0 && *q2 != b'\n' && i + 1 < path.len() {
339                    path[i] = *q2 as c_char;
340                    i += 1;
341                    q2 = q2.add(1);
342                }
343                path[i] = 0;
344                if crate::strcmp(path.as_ptr() as *const u8, exe as *const u8) != 0 { continue; }
345                let base = start;
346                let eh = base as *const u8;
347                if *eh == 0x7f && *eh.add(1) == b'E' && *eh.add(2) == b'L' && *eh.add(3) == b'F' {
348                    let e_phoff = rd64(eh.add(32));
349                    let e_phentsize = rd16(eh.add(54));
350                    let e_phnum = rd16(eh.add(56));
351                    if e_phentsize as usize == 56 {
352                        crate::fclose(f);
353                        return (base, (base + e_phoff) as *const u8, e_phnum);
354                    }
355                }
356                crate::fclose(f);
357                return (base, core::ptr::null(), 0);
358            }
359            crate::fclose(f);
360        }
361    }
362    let phdr = crate::getauxval(3);
363    let phnum = crate::getauxval(5) as u16;
364    if phdr != 0 && phnum != 0 {
365        let (start, off, path, _f) = maps_lookup(phdr as usize);
366        let base = elf_bias_for(path, start, off);
367        return (base, phdr as *const u8, phnum);
368    }
369    (0, core::ptr::null(), 0)
370}
371
372#[no_mangle]
373pub unsafe extern "C" fn dl_iterate_phdr(
374    callback: Option<unsafe extern "C" fn(*mut DlPhdrInfo, usize, *mut c_void) -> c_int>,
375    data: *mut c_void,
376) -> c_int {
377    let cb = match callback { Some(c) => c, None => return 0 };
378    let (base, phdr, phnum) = main_program_info();
379    if !phdr.is_null() && phnum != 0 {
380        let info = DlPhdrInfo {
381            dlpi_addr: base,
382            dlpi_name: b"\0".as_ptr() as *const c_char,
383            dlpi_phdr: phdr,
384            dlpi_phnum: phnum,
385            __pad0: 0,
386            __pad1: 0,
387            dlpi_adds: 0,
388            dlpi_subs: 0,
389            dlpi_tls_modid: 0,
390            dlpi_tls_data: core::ptr::null_mut(),
391        };
392        let r = cb(&info as *const DlPhdrInfo as *mut DlPhdrInfo, core::mem::size_of::<DlPhdrInfo>(), data);
393        if r != 0 { return r; }
394    }
395    if let Some(f) = LDSO_ITERATE {
396        return f(Some(cb), data);
397    }
398    0
399}
400
401#[no_mangle]
402pub unsafe extern "C" fn dlinfo(handle: *mut c_void, request: c_int, info: *mut c_void) -> c_int {
403    if let Some(f) = LDSO_DLINFO {
404        return f(handle, request, info);
405    }
406    ERRNO = 95; // ENOTSUP
407    -1
408}