Skip to main content

kmod_loader/arch/
x86_64.rs

1use goblin::elf::{Elf, SectionHeader};
2use int_enum::IntEnum;
3
4use crate::{
5    ModuleErr, Result,
6    arch::{Ptr, get_rela_sym_idx, get_rela_type},
7    loader::{KernelModuleHelper, ModuleLoadInfo, ModuleOwner},
8};
9
10#[derive(Debug, Clone, Copy, Default)]
11#[repr(C)]
12pub struct ModuleArchSpecific {}
13
14#[repr(u32)]
15#[derive(Debug, Clone, Copy, IntEnum)]
16#[allow(non_camel_case_types)]
17/// See <https://elixir.bootlin.com/linux/v6.6/source/arch/x86/include/asm/elf.h#L47>
18pub enum ArchRelocationType {
19    /// No reloc
20    R_X86_64_NONE = 0,
21    /// Direct 64 bit
22    R_X86_64_64 = 1,
23    /// PC relative 32 bit signed
24    R_X86_64_PC32 = 2,
25    /// 32 bit GOT entry
26    R_X86_64_GOT32 = 3,
27    /// 32 bit PLT address
28    R_X86_64_PLT32 = 4,
29    /// Copy symbol at runtime
30    R_X86_64_COPY = 5,
31    /// Create GOT entry
32    R_X86_64_GLOB_DAT = 6,
33    /// Create PLT entry
34    R_X86_64_JUMP_SLOT = 7,
35    /// Adjust by program base
36    R_X86_64_RELATIVE = 8,
37    /// 32 bit signed pc relative offset to GOT
38    R_X86_64_GOTPCREL = 9,
39    /// Direct 32 bit zero extended
40    R_X86_64_32 = 10,
41    /// Direct 32 bit sign extended
42    R_X86_64_32S = 11,
43    /// Direct 16 bit zero extended
44    R_X86_64_16 = 12,
45    /// 16 bit sign extended pc relative
46    R_X86_64_PC16 = 13,
47    /// Direct 8 bit sign extended
48    R_X86_64_8 = 14,
49    /// 8 bit sign extended pc relative
50    R_X86_64_PC8 = 15,
51    /// Place relative 64-bit signed
52    R_X86_64_PC64 = 24,
53}
54
55type X64RelTy = ArchRelocationType;
56
57impl ArchRelocationType {
58    fn apply_relocation(&self, location: u64, mut target_addr: u64) -> Result<()> {
59        let size;
60        let location = Ptr(location);
61        let overflow = || {
62            log::error!(
63                "overflow in relocation type {:?}, target address {:#x}",
64                self,
65                target_addr
66            );
67            log::error!("module likely not compiled with -mcmodel=kernel");
68            ModuleErr::ENOEXEC
69        };
70        match self {
71            X64RelTy::R_X86_64_NONE => return Ok(()),
72            X64RelTy::R_X86_64_64 => {
73                size = 8;
74            }
75            X64RelTy::R_X86_64_32 => {
76                if target_addr != target_addr as u32 as u64 {
77                    return Err(overflow());
78                }
79                size = 4;
80            }
81            X64RelTy::R_X86_64_32S => {
82                // Check if the value fits in a signed 32-bit integer
83                // C code: if ((s64)val != *(s32 *)&val) goto overflow;
84                // This checks: i64_value != sign_extend(low_32_bits_as_i32)
85                if (target_addr as i64) != ((target_addr as i32) as i64) {
86                    return Err(overflow());
87                }
88                size = 4;
89            }
90            X64RelTy::R_X86_64_PC32 | X64RelTy::R_X86_64_PLT32 => {
91                target_addr = target_addr.wrapping_sub(location.0);
92                size = 4;
93            }
94            X64RelTy::R_X86_64_PC64 => {
95                target_addr = target_addr.wrapping_sub(location.0);
96                size = 8;
97            }
98            _ => {
99                log::error!("x86/modules: Unsupported relocation type: {:?}", self);
100                return Err(ModuleErr::ENOEXEC);
101            }
102        }
103        // if (memcmp(loc, &zero, size))
104        if location.as_slice::<u8>(size).iter().any(|&b| b != 0) {
105            log::error!(
106                "x86/modules: Invalid relocation target, existing value is nonzero for type {:?}, loc: {:#x}, value: {:#x}",
107                self,
108                location.0,
109                target_addr
110            );
111            return Err(ModuleErr::ENOEXEC);
112        } else {
113            // Write the relocated value
114            match size {
115                4 => location.write::<u32>(target_addr as u32),
116                8 => location.write::<u64>(target_addr),
117                _ => unreachable!(),
118            }
119        }
120        Ok(())
121    }
122}
123
124pub struct ArchRelocate;
125
126#[allow(unused_assignments)]
127impl ArchRelocate {
128    /// See https://elixir.bootlin.com/linux/v6.6/source/arch/x86/kernel/module.c#L252
129    pub fn apply_relocate_add<H: KernelModuleHelper>(
130        rela_list: &[goblin::elf64::reloc::Rela],
131        rel_section: &SectionHeader,
132        sechdrs: &[SectionHeader],
133        load_info: &ModuleLoadInfo,
134        module: &ModuleOwner<H>,
135    ) -> Result<()> {
136        for rela in rela_list {
137            let rel_type = get_rela_type(rela.r_info);
138            let sym_idx = get_rela_sym_idx(rela.r_info);
139
140            // This is where to make the change
141            let location = sechdrs[rel_section.sh_info as usize].sh_addr + rela.r_offset;
142            let (sym, sym_name) = &load_info.syms[sym_idx];
143
144            let reloc_type = ArchRelocationType::try_from(rel_type).map_err(|_| {
145                log::error!(
146                    "[{:?}]: Invalid relocation type: {}",
147                    module.name(),
148                    rel_type
149                );
150                ModuleErr::ENOEXEC
151            })?;
152
153            let target_addr = sym.st_value.wrapping_add(rela.r_addend as u64);
154
155            log::info!(
156                "[{:?}]: Applying relocation {:?} at location {:#x} with target addr {:#x}",
157                module.name(),
158                reloc_type,
159                location,
160                target_addr
161            );
162
163            let res = reloc_type.apply_relocation(location, target_addr);
164            match res {
165                Err(e) => {
166                    log::error!("[{:?}]: '{}' {:?}", module.name(), sym_name, e);
167                    return Err(e);
168                }
169                Ok(_) => { /* Successfully applied relocation */ }
170            }
171        }
172        Ok(())
173    }
174}
175
176pub fn module_frob_arch_sections<H: KernelModuleHelper>(
177    elf: &mut Elf,
178    owner: &mut ModuleOwner<H>,
179) -> Result<()> {
180    Ok(())
181}