Skip to main content

kmod_loader/arch/
mod.rs

1#![allow(unused)]
2
3cfg_if::cfg_if! {
4    if #[cfg(target_arch = "aarch64")] {
5        mod aarch64;
6        pub use aarch64::*;
7    } else if #[cfg(target_arch = "loongarch64")] {
8        mod loongarch64;
9        pub use loongarch64::*;
10    } else if #[cfg(target_arch = "riscv64")] {
11        mod riscv64;
12        pub use riscv64::*;
13    } else if #[cfg(target_arch = "x86_64")] {
14        mod x86_64;
15        pub use x86_64::*;
16    } else {
17        compile_error!("Unsupported architecture");
18    }
19}
20
21const SZ_128M: u64 = 0x08000000;
22const SZ_512K: u64 = 0x00080000;
23const SZ_128K: u64 = 0x00020000;
24const SZ_2K: u64 = 0x00000800;
25
26/**
27 * sign_extend64 - sign extend a 64-bit value using specified bit as sign-bit
28 * @value: value to sign extend
29 * @index: 0 based bit index (0<=index<64) to sign bit
30 */
31pub const fn sign_extend64(value: u64, index: u32) -> i64 {
32    let shift = 63 - index;
33    ((value << shift) as i64) >> shift
34}
35
36/// Extracts the relocation type from the r_info field of an Elf64_Rela
37const fn get_rela_type(r_info: u64) -> u32 {
38    (r_info & 0xffffffff) as u32
39}
40
41/// Extracts the symbol index from the r_info field of an Elf64_Rela
42const fn get_rela_sym_idx(r_info: u64) -> usize {
43    (r_info >> 32) as usize
44}
45
46#[derive(Debug, Clone, Copy)]
47struct Ptr(u64);
48impl Ptr {
49    fn as_ptr<T>(&self) -> *mut T {
50        self.0 as *mut T
51    }
52
53    /// Writes a value of type T to the pointer location
54    pub fn write<T>(&self, value: T) {
55        unsafe {
56            let ptr = self.as_ptr::<T>();
57            ptr.write(value);
58        }
59    }
60
61    pub fn read<T>(&self) -> T {
62        unsafe {
63            let ptr = self.as_ptr::<T>();
64            ptr.read()
65        }
66    }
67
68    pub fn add(&self, offset: usize) -> Ptr {
69        Ptr(self.0 + offset as u64)
70    }
71
72    pub fn as_slice<T>(&self, len: usize) -> &[T] {
73        unsafe {
74            let ptr = self.as_ptr::<T>();
75            core::slice::from_raw_parts(ptr, len)
76        }
77    }
78}
79
80#[macro_export]
81macro_rules! BIT {
82    ($nr:expr) => {
83        (1u32 << $nr)
84    };
85}
86
87#[macro_export]
88macro_rules! BIT_U64 {
89    ($nr:expr) => {
90        (1u64 << $nr)
91    };
92}
93
94pub use common::*;
95
96mod common {
97    use core::mem::size_of;
98
99    use goblin::elf::{Elf, Reloc, RelocSection, SectionHeader};
100
101    use crate::{ModuleErr, Result};
102
103    #[derive(Debug, Clone, Copy, Default)]
104    #[repr(C)]
105    pub struct ModSection {
106        pub(crate) shndx: usize,
107        pub(crate) num_entries: usize,
108        pub(crate) max_entries: usize,
109    }
110
111    #[derive(Debug, Clone, Copy)]
112    #[repr(C)]
113    pub struct GotEntry {
114        pub(crate) symbol_addr: u64,
115    }
116
117    #[derive(Debug, Clone, Copy)]
118    #[repr(C)]
119    pub struct PltIdxEntry {
120        pub(crate) symbol_addr: u64,
121    }
122
123    #[derive(Debug, Clone, Copy, Default)]
124    #[repr(C)]
125    pub struct IndexedModuleArchSpecific {
126        pub(crate) got: ModSection,
127        pub(crate) plt: ModSection,
128        pub(crate) plt_idx: ModSection,
129    }
130
131    #[cfg(any(target_arch = "loongarch64", target_arch = "riscv64"))]
132    pub type ModuleArchSpecific = IndexedModuleArchSpecific;
133
134    pub fn duplicate_rela(rela_sec: &RelocSection, idx: usize) -> bool {
135        let rela_now = rela_sec.get(idx).expect("Invalid relocation index");
136        for i in 0..idx {
137            let rela_prev = rela_sec.get(i).expect("Invalid relocation index");
138            if is_rela_equal(&rela_now, &rela_prev) {
139                return true;
140            }
141        }
142        false
143    }
144
145    fn is_rela_equal(rela1: &Reloc, rela2: &Reloc) -> bool {
146        rela1.r_addend == rela2.r_addend
147            && rela1.r_type == rela2.r_type
148            && rela1.r_sym == rela2.r_sym
149    }
150
151    fn find_section(elf: &Elf, section_name: &str) -> Option<usize> {
152        elf.section_headers
153            .iter()
154            .enumerate()
155            .find_map(|(idx, shdr)| {
156                let sec_name = elf.shdr_strtab.get_at(shdr.sh_name).unwrap_or("<unknown>");
157                (sec_name == section_name).then_some(idx)
158            })
159    }
160
161    fn get_got_entry(
162        address: u64,
163        sechdrs: &[SectionHeader],
164        sec: &ModSection,
165    ) -> Option<&'static mut GotEntry> {
166        let got_entries_addr = sechdrs[sec.shndx].sh_addr;
167        let got_entries = unsafe {
168            core::slice::from_raw_parts_mut(got_entries_addr as *mut GotEntry, sec.max_entries)
169        };
170
171        got_entries[..sec.num_entries]
172            .iter_mut()
173            .find(|entry| entry.symbol_addr == address)
174    }
175
176    fn emit_got_entry(address: u64) -> GotEntry {
177        GotEntry {
178            symbol_addr: address,
179        }
180    }
181
182    pub fn common_module_emit_got_entry(
183        got_sec: &mut ModSection,
184        sechdrs: &[SectionHeader],
185        address: u64,
186    ) -> Result<&'static mut GotEntry> {
187        if let Some(got) = get_got_entry(address, sechdrs, got_sec) {
188            return Ok(got);
189        }
190
191        if got_sec.num_entries >= got_sec.max_entries {
192            log::error!("too many GOT entries");
193            return Err(ModuleErr::ENOEXEC);
194        }
195
196        let idx = got_sec.num_entries;
197        let got_entries_addr = sechdrs[got_sec.shndx].sh_addr;
198        let got_entries = unsafe {
199            core::slice::from_raw_parts_mut(got_entries_addr as *mut GotEntry, got_sec.max_entries)
200        };
201        got_entries[idx] = emit_got_entry(address);
202        got_sec.num_entries += 1;
203
204        Ok(&mut got_entries[idx])
205    }
206
207    pub fn common_prepare_got_section(
208        elf: &mut Elf,
209        got_sec: &mut ModSection,
210        num_gots: usize,
211        align: u64,
212        extra_entries: usize,
213    ) -> Result<()> {
214        if num_gots + extra_entries == 0 {
215            return Ok(());
216        }
217
218        let Some(got_section_idx) = find_section(elf, ".got") else {
219            log::error!("module .GOT section missing");
220            return Err(ModuleErr::ENOEXEC);
221        };
222
223        let shdr = &mut elf.section_headers[got_section_idx];
224        shdr.sh_type = goblin::elf::section_header::SHT_NOBITS;
225        shdr.sh_flags = goblin::elf::section_header::SHF_ALLOC as u64;
226        shdr.sh_addralign = align;
227        shdr.sh_size = ((num_gots + extra_entries) * size_of::<GotEntry>()) as u64;
228
229        got_sec.shndx = got_section_idx;
230        got_sec.num_entries = 0;
231        got_sec.max_entries = num_gots;
232        Ok(())
233    }
234
235    pub type ArchEmitPlainPltEntryFunc<P> = fn(address: u64, plt_entry_addr: u64) -> Result<P>;
236
237    pub fn common_module_emit_plain_plt_entry<P>(
238        plt_sec: &mut ModSection,
239        sechdrs: &[SectionHeader],
240        address: u64,
241        arch_emit_plt_entry_func: ArchEmitPlainPltEntryFunc<P>,
242    ) -> Result<&'static mut P> {
243        if plt_sec.num_entries >= plt_sec.max_entries {
244            log::error!("too many PLT entries");
245            return Err(ModuleErr::ENOEXEC);
246        }
247
248        let idx = plt_sec.num_entries;
249        let plt_entries_addr = sechdrs[plt_sec.shndx].sh_addr;
250        let plt_entries = unsafe {
251            core::slice::from_raw_parts_mut(plt_entries_addr as *mut P, plt_sec.max_entries)
252        };
253        let plt_entry_addr = &plt_entries[idx] as *const P as u64;
254
255        plt_entries[idx] = arch_emit_plt_entry_func(address, plt_entry_addr)?;
256        plt_sec.num_entries += 1;
257
258        Ok(&mut plt_entries[idx])
259    }
260
261    pub fn common_prepare_plt_section<P>(
262        elf: &mut Elf,
263        plt_sec: &mut ModSection,
264        num_plts: usize,
265        align: u64,
266        extra_entries: usize,
267    ) -> Result<()> {
268        if num_plts + extra_entries == 0 {
269            return Ok(());
270        }
271
272        let Some(plt_section_idx) = find_section(elf, ".plt") else {
273            log::error!("module .PLT section missing");
274            return Err(ModuleErr::ENOEXEC);
275        };
276
277        let shdr = &mut elf.section_headers[plt_section_idx];
278        shdr.sh_type = goblin::elf::section_header::SHT_PROGBITS;
279        shdr.sh_flags = (goblin::elf::section_header::SHF_ALLOC
280            | goblin::elf::section_header::SHF_EXECINSTR) as u64;
281        shdr.sh_addralign = align;
282        shdr.sh_size = ((num_plts + extra_entries) * size_of::<P>()) as u64;
283
284        plt_sec.shndx = plt_section_idx;
285        plt_sec.num_entries = 0;
286        plt_sec.max_entries = num_plts;
287        Ok(())
288    }
289
290    fn get_plt_idx(address: u64, sechdrs: &[SectionHeader], sec: &ModSection) -> Option<usize> {
291        let plt_idx_addr = sechdrs[sec.shndx].sh_addr;
292        let plt_idx_entries = unsafe {
293            core::slice::from_raw_parts_mut(plt_idx_addr as *mut PltIdxEntry, sec.max_entries)
294        };
295        plt_idx_entries[..sec.num_entries]
296            .iter()
297            .position(|entry| entry.symbol_addr == address)
298    }
299
300    fn get_indexed_plt_entry<P>(
301        address: u64,
302        sechdrs: &[SectionHeader],
303        plt_sec: &ModSection,
304        plt_idx_sec: &ModSection,
305    ) -> Option<&'static mut P> {
306        let plt_idx = get_plt_idx(address, sechdrs, plt_idx_sec)?;
307        let plt_entries_addr = sechdrs[plt_sec.shndx].sh_addr;
308        let plt_entries = unsafe {
309            core::slice::from_raw_parts_mut(plt_entries_addr as *mut P, plt_sec.max_entries)
310        };
311        Some(&mut plt_entries[plt_idx])
312    }
313
314    fn emit_plt_idx_entry(address: u64) -> PltIdxEntry {
315        PltIdxEntry {
316            symbol_addr: address,
317        }
318    }
319
320    pub type ArchEmitIndexedPltEntryFunc<P> =
321        fn(address: u64, plt_entry_addr: u64, plt_idx_entry_addr: u64) -> P;
322
323    pub fn common_module_emit_indexed_plt_entry<P>(
324        plt_sec: &mut ModSection,
325        plt_idx_sec: &mut ModSection,
326        sechdrs: &[SectionHeader],
327        address: u64,
328        arch_emit_plt_entry_func: ArchEmitIndexedPltEntryFunc<P>,
329    ) -> Result<&'static mut P> {
330        if let Some(plt) = get_indexed_plt_entry(address, sechdrs, plt_sec, plt_idx_sec) {
331            return Ok(plt);
332        }
333
334        if plt_sec.num_entries >= plt_sec.max_entries {
335            log::error!("too many PLT entries");
336            return Err(ModuleErr::ENOEXEC);
337        }
338
339        let nr = plt_sec.num_entries;
340        let plt_idx_addr = sechdrs[plt_idx_sec.shndx].sh_addr;
341        let plt_idx_entries = unsafe {
342            core::slice::from_raw_parts_mut(
343                plt_idx_addr as *mut PltIdxEntry,
344                plt_idx_sec.max_entries,
345            )
346        };
347        plt_idx_entries[nr] = emit_plt_idx_entry(address);
348
349        let plt_entries_addr = sechdrs[plt_sec.shndx].sh_addr;
350        let plt_entries = unsafe {
351            core::slice::from_raw_parts_mut(plt_entries_addr as *mut P, plt_sec.max_entries)
352        };
353        let plt_entry_addr = &plt_entries[nr] as *const P as u64;
354        let plt_idx_entry_addr = &plt_idx_entries[nr] as *const PltIdxEntry as u64;
355
356        plt_entries[nr] = arch_emit_plt_entry_func(address, plt_entry_addr, plt_idx_entry_addr);
357
358        plt_sec.num_entries += 1;
359        plt_idx_sec.num_entries += 1;
360
361        Ok(&mut plt_entries[nr])
362    }
363
364    pub fn common_prepare_plt_idx_section(
365        elf: &mut Elf,
366        plt_idx_sec: &mut ModSection,
367        num_plts: usize,
368        section_name: &str,
369        align: u64,
370        extra_entries: usize,
371    ) -> Result<()> {
372        if num_plts + extra_entries == 0 {
373            return Ok(());
374        }
375
376        let Some(plt_idx_section_idx) = find_section(elf, section_name) else {
377            log::error!("module {} section missing", section_name);
378            return Err(ModuleErr::ENOEXEC);
379        };
380
381        let shdr = &mut elf.section_headers[plt_idx_section_idx];
382        shdr.sh_type = goblin::elf::section_header::SHT_PROGBITS;
383        shdr.sh_flags = goblin::elf::section_header::SHF_ALLOC as u64;
384        shdr.sh_addralign = align;
385        shdr.sh_size = ((num_plts + extra_entries) * size_of::<PltIdxEntry>()) as u64;
386
387        plt_idx_sec.shndx = plt_idx_section_idx;
388        plt_idx_sec.num_entries = 0;
389        plt_idx_sec.max_entries = num_plts;
390        Ok(())
391    }
392
393    pub type ArchGotPltCounterFunc = fn(rela_sec: &RelocSection) -> (usize, usize);
394
395    #[cfg(any(target_arch = "loongarch64", target_arch = "riscv64"))]
396    pub fn common_module_frob_arch_sections<H: crate::KernelModuleHelper>(
397        elf: &mut Elf,
398        owner: &mut crate::ModuleOwner<H>,
399        got_plt_counter_func: ArchGotPltCounterFunc,
400        plt_idx_name: &str,
401    ) -> Result<()> {
402        let mut num_plts = 0;
403        let mut num_gots = 0;
404        for (idx, rela_sec) in elf.shdr_relocs.iter() {
405            let shdr = &elf.section_headers[*idx];
406            if shdr.sh_type != goblin::elf::section_header::SHT_RELA {
407                continue;
408            }
409            let to_section = &elf.section_headers[shdr.sh_info as usize];
410            if to_section.sh_flags & goblin::elf::section_header::SHF_EXECINSTR as u64 == 0 {
411                continue;
412            }
413            let (plt_entries, got_entries) = got_plt_counter_func(rela_sec);
414            num_plts += plt_entries;
415            num_gots += got_entries;
416        }
417
418        log::info!(
419            "[{:?}]: Need {} PLT entries and {} GOT entries",
420            owner.name(),
421            num_plts,
422            num_gots
423        );
424
425        common_prepare_got_section(elf, &mut owner.arch.got, num_gots, 64, 1)?;
426        common_prepare_plt_section::<crate::arch::PltEntry>(
427            elf,
428            &mut owner.arch.plt,
429            num_plts,
430            64,
431            1,
432        )?;
433        common_prepare_plt_idx_section(
434            elf,
435            &mut owner.arch.plt_idx,
436            num_plts,
437            plt_idx_name,
438            64,
439            1,
440        )?;
441
442        Ok(())
443    }
444}