1use alloc::{
2 boxed::Box,
3 ffi::CString,
4 string::{String, ToString},
5 vec::Vec,
6};
7use core::{ffi::CStr, fmt::Display};
8
9use bitflags::bitflags;
10use goblin::elf::{Elf, SectionHeader};
11use kmod_tools::Module;
12
13use crate::{ModuleErr, Result, arch::ModuleArchSpecific, module::ModuleInfo};
14
15bitflags! {
16 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
17 pub struct SectionPerm: u8 {
18 const READ = 0b001;
19 const WRITE = 0b010;
20 const EXECUTE = 0b100;
21 }
22}
23
24impl Display for SectionPerm {
25 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
26 let mut perms = String::new();
27 if self.contains(SectionPerm::READ) {
28 perms.push('R');
29 }
30 if self.contains(SectionPerm::WRITE) {
31 perms.push('W');
32 }
33 if self.contains(SectionPerm::EXECUTE) {
34 perms.push('X');
35 }
36 write!(f, "{}", perms)
37 }
38}
39
40impl SectionPerm {
41 pub fn from_elf_flags(sh_flags: u64) -> Self {
43 let mut perms = SectionPerm::empty();
44 if (sh_flags & goblin::elf::section_header::SHF_ALLOC as u64) != 0 {
45 perms |= SectionPerm::READ;
46 }
47 if (sh_flags & goblin::elf::section_header::SHF_WRITE as u64) != 0 {
48 perms |= SectionPerm::WRITE;
49 }
50 if (sh_flags & goblin::elf::section_header::SHF_EXECINSTR as u64) != 0 {
51 perms |= SectionPerm::EXECUTE;
52 }
53 perms
54 }
55}
56
57pub trait SectionMemOps: Send + Sync {
59 fn as_ptr(&self) -> *const u8;
60 fn as_mut_ptr(&mut self) -> *mut u8;
61 fn change_perms(&mut self, perms: SectionPerm) -> bool;
63}
64
65pub trait KernelModuleHelper {
67 fn vmalloc(size: usize) -> Box<dyn SectionMemOps>;
69 fn resolve_symbol(name: &str) -> Option<usize>;
71 fn flsuh_cache(_addr: usize, _size: usize) {
73 }
75}
76
77pub struct ModuleLoader<'a, H: KernelModuleHelper> {
78 elf: Elf<'a>,
79 elf_data: &'a [u8],
80 __helper: core::marker::PhantomData<H>,
81}
82
83struct SectionPages {
84 name: String,
85 addr: Box<dyn SectionMemOps>,
86 size: usize,
87 perms: SectionPerm,
88}
89
90pub struct ModuleOwner<H: KernelModuleHelper> {
91 module_info: ModuleInfo,
92 pages: Vec<SectionPages>,
93 name: String,
94 module: Module,
95 #[allow(unused)]
96 pub(crate) arch: ModuleArchSpecific,
97 _helper: core::marker::PhantomData<H>,
98}
99
100impl<H: KernelModuleHelper> ModuleOwner<H> {
101 pub fn name(&self) -> &str {
103 &self.name
104 }
105
106 pub(crate) fn set_name(&mut self, name: &str) {
107 self.name = name.to_string();
108 }
109
110 pub fn call_init(&mut self) -> Result<i32> {
112 if let Some(init_fn) = self.module.take_init_fn() {
113 let result = unsafe { init_fn() };
114 Ok(result)
115 } else {
116 log::warn!("The init function can only be called once.");
117 Err(ModuleErr::EINVAL)
118 }
119 }
120
121 pub fn call_exit(&mut self) {
123 if let Some(exit_fn) = self.module.take_exit_fn() {
124 log::warn!("Calling module exit function...");
125 unsafe {
126 exit_fn();
127 }
128 } else {
129 log::warn!("The exit function can only be called once.");
130 }
131 }
132}
133
134const fn align_up(addr: usize, align: usize) -> usize {
135 (addr + align - 1) & !(align - 1)
136}
137
138const SKIP_SECTIONS: &[&str] = &[".note", ".modinfo", "__version"];
143
144pub(crate) struct ModuleLoadInfo {
145 pub(crate) syms: Vec<(goblin::elf::sym::Sym, String)>,
146}
147
148impl<'a, H: KernelModuleHelper> ModuleLoader<'a, H> {
149 pub fn new(elf_data: &'a [u8]) -> Result<Self> {
151 let elf = Elf::parse(elf_data).map_err(|_| ModuleErr::ENOEXEC)?;
152 if !elf.is_64 {
153 return Err(ModuleErr::ENOEXEC);
154 }
155 Ok(ModuleLoader {
156 elf,
157 elf_data,
158 __helper: core::marker::PhantomData,
159 })
160 }
161
162 fn module_sig_check(&self) -> bool {
166 true
168 }
169
170 fn elf_validity_cache_copy(&self) -> Result<ModuleOwner<H>> {
175 if self.elf.header.e_type != goblin::elf::header::ET_REL {
176 log::error!(
177 "Invalid ELF type: {}, expected ET_REL",
178 self.elf.header.e_type
179 );
180 return Err(ModuleErr::ENOEXEC);
181 }
182
183 elf_check_arch(&self.elf)?;
184
185 if self.elf.header.e_shstrndx == goblin::elf::section_header::SHN_UNDEF as _
187 || self.elf.header.e_shstrndx as usize >= self.elf.section_headers.len()
188 {
189 log::error!(
190 "Invalid ELF section name index: {} || e_shstrndx ({}) >= e_shnum ({})",
191 self.elf.header.e_shstrndx,
192 self.elf.header.e_shstrndx,
193 self.elf.section_headers.len()
194 );
195 return Err(ModuleErr::ENOEXEC);
196 }
197
198 if self.elf.shdr_strtab.len() == 0 {
202 log::error!("ELF section name string table is empty");
203 return Err(ModuleErr::ENOEXEC);
204 }
205
206 if self.elf.section_headers[0].sh_type != goblin::elf::section_header::SHT_NULL
209 || self.elf.section_headers[0].sh_size != 0
210 || self.elf.section_headers[0].sh_addr != 0
211 {
212 log::error!(
213 "ELF Spec violation: section 0 type({})!=SH_NULL or non-zero len or addr",
214 self.elf.section_headers[0].sh_type
215 );
216 return Err(ModuleErr::ENOEXEC);
217 }
218
219 let mut num_sym_secs = 0;
220 let mut num_mod_secs = 0;
221 let mut num_info_secs = 0;
222 let mut info_idx = 0;
223 let mut mod_idx = 0;
224 for (idx, shdr) in self.elf.section_headers.iter().enumerate() {
225 let ty = shdr.sh_type;
226 match ty {
227 goblin::elf::section_header::SHT_NULL | goblin::elf::section_header::SHT_NOBITS => {
228 continue;
229 }
230 goblin::elf::section_header::SHT_SYMTAB => {
231 if shdr.sh_link == goblin::elf::section_header::SHN_UNDEF
232 || shdr.sh_link as usize >= self.elf.section_headers.len()
233 {
234 log::error!(
235 "Invalid ELF sh_link!=SHN_UNDEF({}) or (sh_link({}) >= hdr->e_shnum({})",
236 shdr.sh_link,
237 shdr.sh_link,
238 self.elf.section_headers.len()
239 );
240 return Err(ModuleErr::ENOEXEC);
241 }
242 num_sym_secs += 1;
243 }
244 _ => {
245 let shdr_name = self
246 .elf
247 .shdr_strtab
248 .get_at(shdr.sh_name)
249 .ok_or(ModuleErr::ENOEXEC)?;
250 if shdr_name == ".gnu.linkonce.this_module" {
251 num_mod_secs += 1;
252 mod_idx = idx;
253 } else if shdr_name == ".modinfo" {
254 num_info_secs += 1;
255 info_idx = idx;
256 }
257
258 if shdr.sh_flags == goblin::elf::section_header::SHF_ALLOC as _ {
259 let _name = self
261 .elf
262 .shdr_strtab
263 .get_at(shdr.sh_name)
264 .ok_or(ModuleErr::ENOEXEC)?;
265 }
266 }
267 }
268 }
269
270 let mut owner = None;
271 if num_info_secs > 1 {
272 log::error!("Only one .modinfo section must exist.");
273 return Err(ModuleErr::ENOEXEC);
274 } else if num_info_secs == 1 {
275 owner = Some(self.pre_read_modinfo(info_idx)?);
276 if let Some(ref o) = owner {
277 log::error!("Module({:?}) info: {:?}", o.name(), o.module_info);
278 }
279 }
280 let mut owner = owner.ok_or(ModuleErr::ENOEXEC)?;
281 let module_name = owner.name();
282
283 if num_sym_secs != 1 {
284 log::error!("{}: module has no symbols (stripped?)", module_name);
285 return Err(ModuleErr::ENOEXEC);
286 }
287 if num_mod_secs != 1 {
303 log::error!(
304 "{}: Only one .gnu.linkonce.this_module section must exist.",
305 module_name
306 );
307 return Err(ModuleErr::ENOEXEC);
308 }
309
310 let this_module_shdr = &self.elf.section_headers[mod_idx];
311 if this_module_shdr.sh_type == goblin::elf::section_header::SHT_NOBITS {
312 log::error!(
313 "{}: .gnu.linkonce.this_module section must have a size set",
314 module_name
315 );
316 return Err(ModuleErr::ENOEXEC);
317 }
318
319 if this_module_shdr.sh_flags & goblin::elf::section_header::SHF_ALLOC as u64 == 0 {
320 log::error!(
321 "{}: .gnu.linkonce.this_module section size must match the kernel's built struct module size at run time",
322 module_name
323 );
324 return Err(ModuleErr::ENOEXEC);
325 }
326 if owner.name().is_empty() {
329 self.pre_read_this_module(mod_idx, &mut owner)?;
330 }
331 Ok(owner)
332 }
333
334 pub fn load_module(mut self, args: CString) -> Result<ModuleOwner<H>> {
336 if !self.module_sig_check() {
337 log::error!("Module signature check failed");
338 return Err(ModuleErr::ENOEXEC);
339 }
340 let mut owner = self.elf_validity_cache_copy()?;
343
344 self.layout_and_allocate(&mut owner)?;
345 let load_info = self.simplify_symbols(&owner)?;
346 self.apply_relocations(load_info, &mut owner)?;
347
348 self.post_read_this_module(&mut owner)?;
349
350 self.find_module_sections(&mut owner)?;
351
352 self.complete_formation(&mut owner)?;
353
354 self.parse_args(&mut owner, args)?;
355
356 log::error!("Module({:?}) loaded successfully!", owner.name());
357 Ok(owner)
358 }
359
360 fn parse_args(&self, owner: &mut ModuleOwner<H>, args: CString) -> Result<()> {
362 let name = owner.name().to_string();
363 let kparams = owner.module.params_mut();
364 let after_dashes = crate::param::parse_args(&name, args, kparams, i16::MIN, i16::MAX)?;
365 if !after_dashes.is_empty() {
366 log::warn!(
367 "[{}]: parameters '{}' after '--' ignored",
368 name,
369 after_dashes.to_str().unwrap_or("<invalid UTF-8>")
370 );
371 }
372 Ok(())
373 }
374
375 fn find_section(&self, name: &str) -> Result<&SectionHeader> {
377 for shdr in &self.elf.section_headers {
378 let sec_name = self
379 .elf
380 .shdr_strtab
381 .get_at(shdr.sh_name)
382 .ok_or(ModuleErr::ENOEXEC)?;
383
384 if sec_name == name {
385 return Ok(shdr);
386 }
387 }
388 log::error!("Section '{}' not found", name);
389 Err(ModuleErr::ENOEXEC)
390 }
391
392 fn pre_read_modinfo(&self, info_idx: usize) -> Result<ModuleOwner<H>> {
393 let modinfo_shdr = &self.elf.section_headers[info_idx];
394 let file_offset = modinfo_shdr.sh_offset as usize;
395 let size = modinfo_shdr.sh_size as usize;
396
397 let mut modinfo_data = &self.elf_data[file_offset..file_offset + size];
398 let mut module_info = ModuleInfo::new();
399
400 log::info!("Reading .modinfo section (size: {:#x})", size);
401
402 loop {
405 if modinfo_data.is_empty() {
406 break;
407 }
408 let cstr = CStr::from_bytes_until_nul(modinfo_data)
409 .map_err(|_| ModuleErr::EINVAL)
410 .unwrap();
411 let str_slice = cstr.to_str().map_err(|_| ModuleErr::EINVAL)?;
412 modinfo_data = &modinfo_data[cstr.to_bytes_with_nul().len()..];
413
414 let mut split = str_slice.splitn(2, '=');
415 let key = split.next().ok_or(ModuleErr::EINVAL)?.to_string();
416 let value = split.next().ok_or(ModuleErr::EINVAL)?.to_string();
417 module_info.add_kv(key, value);
418 }
419
420 let name = module_info
421 .get("name")
422 .map(|s| s.to_string())
423 .unwrap_or_else(|| "".to_string());
424
425 Ok(ModuleOwner {
426 name,
427 module_info,
428 pages: Vec::new(),
429 module: Module::default(),
430 arch: ModuleArchSpecific::default(),
431 _helper: core::marker::PhantomData,
432 })
433 }
434
435 fn pre_read_this_module(&self, idx: usize, owner: &mut ModuleOwner<H>) -> Result<()> {
438 let this_module_shdr = &self.elf.section_headers[idx];
439 let size = this_module_shdr.sh_size as usize;
440 if size != core::mem::size_of::<Module>() {
441 log::error!(
442 "Invalid .gnu.linkonce.this_module section size: {}, expected: {}",
443 size,
444 core::mem::size_of::<Module>()
445 );
446 return Err(ModuleErr::ENOEXEC);
447 }
448 let modinfo_data = this_module_shdr.sh_addr as *mut u8;
449 let module = unsafe { core::ptr::read(modinfo_data as *const Module) };
450 let name = module.name();
451 owner.set_name(name);
452 Ok(())
453 }
454
455 fn post_read_this_module(&mut self, owner: &mut ModuleOwner<H>) -> Result<()> {
457 let this_module_shdr = self.find_section(".gnu.linkonce.this_module")?;
458 let modinfo_data = this_module_shdr.sh_addr as *mut u8;
460 let module = unsafe { core::ptr::read(modinfo_data as *const Module) };
461
462 let init_fn = module.init_fn();
463 let exit_fn = module.exit_fn();
464
465 log::error!(
466 "Module init_fn: {:?}, exit_fn: {:?}",
467 init_fn.map(|f| f as *const ()),
468 exit_fn.map(|f| f as *const ())
469 );
470
471 owner.module = module;
472 Ok(())
473 }
474
475 fn section_objs(&self, name: &str, object_size: usize) -> Result<(usize, *const u8)> {
477 let section = self
478 .find_section(name)
479 .unwrap_or(&self.elf.section_headers[0]); let num = section.sh_size as usize / object_size;
481 let addr = section.sh_addr as *const u8;
482 Ok((num, addr))
483 }
484
485 fn find_module_sections(&self, owner: &mut ModuleOwner<H>) -> Result<()> {
486 let (num_kparams, kparam_addr) =
487 self.section_objs("__param", size_of::<kmod_tools::kernel_param>())?;
488 let raw_module = owner.module.raw_mod();
489 raw_module.kp = kparam_addr as *mut kmod_tools::kernel_param;
490 raw_module.num_kp = num_kparams as _;
491
492 Ok(())
498 }
499
500 fn complete_formation(&self, owner: &mut ModuleOwner<H>) -> Result<()> {
502 for page in &mut owner.pages {
503 if !page.addr.change_perms(page.perms) {
504 log::error!(
505 "Failed to change permissions of section '{}' to {}",
506 page.name,
507 page.perms
508 );
509 return Err(ModuleErr::EINVAL);
510 }
511 H::flsuh_cache(page.addr.as_ptr() as usize, page.size);
512 }
513 Ok(())
514 }
515
516 fn layout_and_allocate(&mut self, owner: &mut ModuleOwner<H>) -> Result<()> {
519 #[cfg(feature = "module-sections")]
521 crate::arch::module_frob_arch_sections(&mut self.elf, owner)?;
522 for shdr in self.elf.section_headers.iter_mut() {
523 let sec_name = self
524 .elf
525 .shdr_strtab
526 .get_at(shdr.sh_name)
527 .unwrap_or("<unknown>");
528
529 if (shdr.sh_flags & goblin::elf::section_header::SHF_ALLOC as u64) == 0 {
531 log::debug!("Skipping non-allocatable section '{}'", sec_name);
532 continue;
533 }
534
535 if SKIP_SECTIONS.iter().any(|&s| sec_name.starts_with(s)) {
537 log::warn!("Skipping section '{}' in skip list", sec_name);
538 continue;
539 }
540
541 let file_offset = shdr.sh_offset as usize;
542 let size = shdr.sh_size as usize;
543
544 let perms = SectionPerm::from_elf_flags(shdr.sh_flags);
545
546 if size == 0 {
547 log::error!("Skipping zero-size section '{}'", sec_name);
548 continue;
549 }
550
551 let aligned_size = align_up(size, 4096);
552
553 let mut addr = H::vmalloc(aligned_size);
555 if addr.as_ptr().is_null() {
556 return Err(ModuleErr::ENOSPC);
557 }
558
559 let raw_addr = addr.as_ptr() as u64;
560
561 if shdr.sh_type != goblin::elf::section_header::SHT_NOBITS {
564 let section_data = &self.elf_data[file_offset..file_offset + size];
565 unsafe {
566 core::ptr::copy_nonoverlapping(section_data.as_ptr(), addr.as_mut_ptr(), size);
567 }
568 }
569
570 owner.pages.push(SectionPages {
572 name: sec_name.to_string(),
573 addr,
574 size: aligned_size,
575 perms,
576 });
577
578 shdr.sh_addr = raw_addr;
582 }
583
584 for page in &owner.pages {
585 log::error!(
586 "Allocated section '{:>26}' at {:p} [{}] ({:8<#x})",
587 page.name,
588 page.addr.as_ptr(),
589 page.perms,
590 page.size
591 );
592 }
593
594 Ok(())
595 }
596
597 fn simplify_symbols(&self, owner: &ModuleOwner<H>) -> Result<ModuleLoadInfo> {
601 let mut loadinfo = ModuleLoadInfo { syms: Vec::new() };
602
603 for (idx, sym) in self.elf.syms.iter().enumerate() {
605 if idx == 0 {
606 loadinfo.syms.push((sym, "".to_string()));
607 continue;
609 }
610
611 let sym_name = self
612 .elf
613 .strtab
614 .get_at(sym.st_name)
615 .unwrap_or("<unknown>")
616 .to_string();
617
618 let sym_value = sym.st_value;
619 let sym_size = sym.st_size;
620
621 log::debug!(
623 "Symbol: ('{}') [{}] Value: 0x{:016x} Size: {}",
624 sym_name,
625 sym_section_to_str(sym.st_shndx as _),
626 sym_value,
627 sym_size
628 );
629
630 let mut updated_sym = sym;
632
633 match sym.st_shndx as _ {
634 goblin::elf::section_header::SHN_UNDEF => {
635 let sym_address = H::resolve_symbol(&sym_name);
637 if let Some(addr) = sym_address {
639 log::error!(
640 " -> Resolved undefined symbol '{}' ({}) to address 0x{:016x}",
641 sym_name,
642 sym_bind_to_str(sym.st_bind()),
643 addr
644 );
645 updated_sym.st_value = addr as u64;
647 } else {
648 if sym.st_bind() == goblin::elf::sym::STB_WEAK {
650 log::warn!(
651 " -> Unresolved weak symbol '{}' ({})",
652 sym_name,
653 sym_bind_to_str(sym.st_bind())
654 );
655 } else {
656 log::warn!(
657 " -> Unresolved symbol '{}' ({})",
658 sym_name,
659 sym_bind_to_str(sym.st_bind())
660 );
661 return Err(ModuleErr::ENOENT);
662 }
663 }
664 }
665 goblin::elf::section_header::SHN_ABS => {
666 log::debug!("Absolute symbol: {} 0x{:x}", sym_name, sym_value);
668 }
669 goblin::elf::section_header::SHN_COMMON => {
670 log::debug!("Common symbol: {}", sym_name);
673 log::warn!("{:?}: please compile with -fno-common", owner.name());
674 return Err(ModuleErr::ENOEXEC);
675 }
676 ty => {
677 let secbase = self.elf.section_headers[ty as usize].sh_addr;
688 updated_sym.st_value = sym.st_value.wrapping_add(secbase);
689 log::trace!(
690 " -> Defined symbol '{}' in section {} at address 0x{:016x} (base: 0x{:016x} + offset: 0x{:016x})",
691 sym_name,
692 ty,
693 updated_sym.st_value,
694 secbase,
695 sym.st_value
696 );
697 }
698 }
699
700 loadinfo.syms.push((updated_sym, sym_name));
702 }
703
704 Ok(loadinfo)
705 }
706
707 fn apply_relocations(
709 &self,
710 load_info: ModuleLoadInfo,
711 owner: &mut ModuleOwner<H>,
712 ) -> Result<()> {
713 for shdr in self.elf.section_headers.iter() {
714 let infosec = shdr.sh_info;
715
716 let sec_name = self
717 .elf
718 .shdr_strtab
719 .get_at(shdr.sh_name)
720 .ok_or(ModuleErr::ENOEXEC)?;
721
722 if infosec >= self.elf.section_headers.len() as u32 {
724 continue;
725 }
726 if self.elf.section_headers[infosec as usize].sh_flags
728 & goblin::elf::section_header::SHF_ALLOC as u64
729 == 0
730 {
731 continue;
732 }
733
734 if shdr.sh_type != goblin::elf::section_header::SHT_RELA {
736 continue;
737 }
738
739 let to_section = &self.elf.section_headers[infosec as usize];
740 let to_sec_name = self
741 .elf
742 .shdr_strtab
743 .get_at(to_section.sh_name)
744 .ok_or(ModuleErr::ENOEXEC)?;
745
746 let rela_entries = shdr.sh_size as usize / shdr.sh_entsize as usize;
747 log::error!(
748 "Applying relocations for section '{}' to '{}', {} entries",
749 sec_name,
750 to_sec_name,
751 rela_entries
752 );
753
754 let offset = shdr.sh_offset as usize;
755 debug_assert!(shdr.sh_entsize == 24);
757
758 let data_buf = &self.elf_data[offset..offset + shdr.sh_size as usize];
759 let rela_list = unsafe {
760 goblin::elf64::reloc::from_raw_rela(data_buf.as_ptr() as _, shdr.sh_size as usize)
761 };
762
763 crate::arch::ArchRelocate::apply_relocate_add(
764 rela_list,
765 shdr,
766 &self.elf.section_headers,
767 &load_info,
768 owner,
769 )?;
770 }
771 Ok(())
772 }
773}
774
775const fn sym_bind_to_str(bind: u8) -> &'static str {
776 match bind {
777 goblin::elf::sym::STB_LOCAL => "LOCAL",
778 goblin::elf::sym::STB_GLOBAL => "GLOBAL",
779 goblin::elf::sym::STB_WEAK => "WEAK",
780 _ => "UNKNOWN",
781 }
782}
783
784const fn sym_section_to_str(shndx: u32) -> &'static str {
785 match shndx {
786 goblin::elf::section_header::SHN_UNDEF => "UNDEF(0)",
787 goblin::elf::section_header::SHN_LORESERVE => "LORESERVE(0xff00)",
788 goblin::elf::section_header::SHN_HIPROC => "HIPROC(0xff1f)",
790 goblin::elf::section_header::SHN_ABS => "ABS(0xfff1)",
791 goblin::elf::section_header::SHN_COMMON => "COMMON(0xfff2)",
792 goblin::elf::section_header::SHN_HIRESERVE => "HIRESERVE(0xffff)",
793 _ => "OTHER",
794 }
795}
796
797fn elf_check_arch(elf: &goblin::elf::Elf) -> Result<()> {
801 if elf.header.e_machine != goblin::elf::header::EM_AARCH64
802 && elf.header.e_machine != goblin::elf::header::EM_X86_64
803 && elf.header.e_machine != goblin::elf::header::EM_RISCV
804 && elf.header.e_machine != goblin::elf::header::EM_LOONGARCH
805 {
806 log::error!(
807 "Invalid ELF machine: {}, expected AARCH64({}), X86_64({}), RISC-V({}), LOONGARCH({})",
808 elf.header.e_machine,
809 goblin::elf::header::EM_AARCH64,
810 goblin::elf::header::EM_X86_64,
811 goblin::elf::header::EM_RISCV,
812 goblin::elf::header::EM_LOONGARCH
813 );
814 return Err(ModuleErr::ENOEXEC);
815 }
816 Ok(())
817}