1use goblin::elf::{Elf, RelocSection, SectionHeader};
2use int_enum::IntEnum;
3
4use crate::{
5 ModuleErr, Result,
6 arch::*,
7 loader::{KernelModuleHelper, ModuleLoadInfo, ModuleOwner},
8};
9
10#[derive(Debug, Clone, Copy, Default)]
11#[repr(C)]
12pub struct ModuleArchSpecific {
13 got: ModSection,
14}
15
16#[repr(u32)]
17#[derive(Debug, Clone, Copy, IntEnum)]
18#[allow(non_camel_case_types)]
19pub enum ArchRelocationType {
21 R_X86_64_NONE = 0,
23 R_X86_64_64 = 1,
25 R_X86_64_PC32 = 2,
27 R_X86_64_GOT32 = 3,
29 R_X86_64_PLT32 = 4,
31 R_X86_64_COPY = 5,
33 R_X86_64_GLOB_DAT = 6,
35 R_X86_64_JUMP_SLOT = 7,
37 R_X86_64_RELATIVE = 8,
39 R_X86_64_GOTPCREL = 9,
41 R_X86_64_32 = 10,
43 R_X86_64_32S = 11,
45 R_X86_64_16 = 12,
47 R_X86_64_PC16 = 13,
49 R_X86_64_8 = 14,
51 R_X86_64_PC8 = 15,
53 R_X86_64_PC64 = 24,
55}
56
57type X64RelTy = ArchRelocationType;
58
59impl ArchRelocationType {
60 fn apply_r_x86_64_gotpcrel(
61 &self,
62 module: &mut ModuleOwner<impl KernelModuleHelper>,
63 sechdrs: &[SectionHeader],
64 location: u64,
65 symbol_addr: u64,
66 addend: i64,
67 ) -> Result<()> {
68 let location = Ptr(location);
69 let got = common_module_emit_got_entry(&mut module.arch.got, sechdrs, symbol_addr)?;
70 let got_addr = got as *const GotEntry as u64;
71 let value = (got_addr as i64)
72 .wrapping_add(addend)
73 .wrapping_sub(location.0 as i64);
74
75 if value != value as i32 as i64 {
76 log::error!(
77 "overflow in relocation type {:?}, displacement {:#x}",
78 self,
79 value
80 );
81 return Err(ModuleErr::ENOEXEC);
82 }
83
84 if location.as_slice::<u8>(4).iter().any(|&b| b != 0) {
85 log::error!(
86 "x86/modules: Invalid relocation target, existing value is nonzero for type {:?}, loc: {:#x}, value: {:#x}",
87 self,
88 location.0,
89 value
90 );
91 return Err(ModuleErr::ENOEXEC);
92 }
93
94 location.write::<u32>(value as u32);
95 Ok(())
96 }
97
98 fn apply_relocation(&self, location: u64, mut target_addr: u64) -> Result<()> {
99 let size;
100 let location = Ptr(location);
101 let overflow = || {
102 log::error!(
103 "overflow in relocation type {:?}, target address {:#x}",
104 self,
105 target_addr
106 );
107 log::error!("module likely not compiled with -mcmodel=kernel");
108 ModuleErr::ENOEXEC
109 };
110 match self {
111 X64RelTy::R_X86_64_NONE => return Ok(()),
112 X64RelTy::R_X86_64_64 => {
113 size = 8;
114 }
115 X64RelTy::R_X86_64_32 => {
116 if target_addr != target_addr as u32 as u64 {
117 return Err(overflow());
118 }
119 size = 4;
120 }
121 X64RelTy::R_X86_64_32S => {
122 if (target_addr as i64) != ((target_addr as i32) as i64) {
126 return Err(overflow());
127 }
128 size = 4;
129 }
130 X64RelTy::R_X86_64_PC32 | X64RelTy::R_X86_64_PLT32 => {
131 target_addr = target_addr.wrapping_sub(location.0);
132 size = 4;
133 }
134 X64RelTy::R_X86_64_PC64 => {
135 target_addr = target_addr.wrapping_sub(location.0);
136 size = 8;
137 }
138 _ => {
139 log::error!("x86/modules: Unsupported relocation type: {:?}", self);
140 return Err(ModuleErr::ENOEXEC);
141 }
142 }
143 if location.as_slice::<u8>(size).iter().any(|&b| b != 0) {
145 log::error!(
146 "x86/modules: Invalid relocation target, existing value is nonzero for type {:?}, loc: {:#x}, value: {:#x}",
147 self,
148 location.0,
149 target_addr
150 );
151 return Err(ModuleErr::ENOEXEC);
152 } else {
153 match size {
155 4 => location.write::<u32>(target_addr as u32),
156 8 => location.write::<u64>(target_addr),
157 _ => unreachable!(),
158 }
159 }
160 Ok(())
161 }
162}
163
164pub struct ArchRelocate;
165
166#[allow(unused_assignments)]
167impl ArchRelocate {
168 pub fn apply_relocate_add<H: KernelModuleHelper>(
170 rela_list: &[goblin::elf64::reloc::Rela],
171 rel_section: &SectionHeader,
172 sechdrs: &[SectionHeader],
173 load_info: &ModuleLoadInfo,
174 module: &mut ModuleOwner<H>,
175 ) -> Result<()> {
176 for rela in rela_list {
177 let rel_type = get_rela_type(rela.r_info);
178 let sym_idx = get_rela_sym_idx(rela.r_info);
179
180 let location = sechdrs[rel_section.sh_info as usize].sh_addr + rela.r_offset;
182 let (sym, sym_name) = &load_info.syms[sym_idx];
183
184 let reloc_type = ArchRelocationType::try_from(rel_type).map_err(|_| {
185 log::error!(
186 "[{:?}]: Invalid relocation type: {}",
187 module.name(),
188 rel_type
189 );
190 ModuleErr::ENOEXEC
191 })?;
192
193 let target_addr = sym.st_value.wrapping_add(rela.r_addend as u64);
194
195 log::info!(
196 "[{:?}]: Applying relocation {:?} at location {:#x} with target addr {:#x}",
197 module.name(),
198 reloc_type,
199 location,
200 target_addr
201 );
202
203 let res = match reloc_type {
204 X64RelTy::R_X86_64_GOTPCREL => reloc_type.apply_r_x86_64_gotpcrel(
205 module,
206 sechdrs,
207 location,
208 sym.st_value,
209 rela.r_addend,
210 ),
211 _ => reloc_type.apply_relocation(location, target_addr),
212 };
213 match res {
214 Err(e) => {
215 log::error!("[{:?}]: '{}' {:?}", module.name(), sym_name, e);
216 return Err(e);
217 }
218 Ok(_) => { }
219 }
220 }
221 Ok(())
222 }
223}
224
225pub fn module_frob_arch_sections<H: KernelModuleHelper>(
226 elf: &mut Elf,
227 owner: &mut ModuleOwner<H>,
228) -> Result<()> {
229 let mut num_gots = 0usize;
230 for (idx, rela_sec) in elf.shdr_relocs.iter() {
231 let shdr = &elf.section_headers[*idx];
232 if shdr.sh_type != goblin::elf::section_header::SHT_RELA {
233 continue;
234 }
235
236 let to_section = &elf.section_headers[shdr.sh_info as usize];
237 if to_section.sh_flags & goblin::elf::section_header::SHF_ALLOC as u64 == 0 {
238 continue;
239 }
240
241 num_gots += count_gots(rela_sec);
242 }
243
244 if num_gots == 0 {
245 return Ok(());
246 }
247
248 common_prepare_got_section(
249 elf,
250 &mut owner.arch.got,
251 num_gots,
252 core::mem::align_of::<GotEntry>() as u64,
253 0,
254 )?;
255
256 Ok(())
257}
258
259fn count_gots(rela_sec: &RelocSection) -> usize {
260 rela_sec
261 .iter()
262 .filter(|rela| {
263 matches!(
264 X64RelTy::try_from(rela.r_type),
265 Ok(X64RelTy::R_X86_64_GOTPCREL)
266 )
267 })
268 .count()
269}