@@ -11,8 +11,9 @@ use x86_64::{
1111 PhysAddr , VirtAddr ,
1212} ;
1313use xmas_elf:: {
14- header,
15- program:: { self , ProgramHeader , Type } ,
14+ dynamic, header,
15+ program:: { self , ProgramHeader , SegmentData , Type } ,
16+ sections:: Rela ,
1617 ElfFile ,
1718} ;
1819
@@ -23,6 +24,7 @@ struct Loader<'a, M, F> {
2324
2425struct Inner < ' a , M , F > {
2526 kernel_offset : PhysAddr ,
27+ virtual_address_offset : u64 ,
2628 page_table : & ' a mut M ,
2729 frame_allocator : & ' a mut F ,
2830}
@@ -44,11 +46,22 @@ where
4446 }
4547
4648 let elf_file = ElfFile :: new ( bytes) ?;
49+
50+ let virtual_address_offset = match elf_file. header . pt2 . type_ ( ) . as_type ( ) {
51+ header:: Type :: None => unimplemented ! ( ) ,
52+ header:: Type :: Relocatable => unimplemented ! ( ) ,
53+ header:: Type :: Executable => 0 ,
54+ header:: Type :: SharedObject => 0x400000 ,
55+ header:: Type :: Core => unimplemented ! ( ) ,
56+ header:: Type :: ProcessorSpecific ( _) => unimplemented ! ( ) ,
57+ } ;
58+
4759 header:: sanity_check ( & elf_file) ?;
4860 let loader = Loader {
4961 elf_file,
5062 inner : Inner {
5163 kernel_offset,
64+ virtual_address_offset,
5265 page_table,
5366 frame_allocator,
5467 } ,
5871 }
5972
6073 fn load_segments ( & mut self ) -> Result < Option < TlsTemplate > , & ' static str > {
61- let mut tls_template = None ;
6274 for program_header in self . elf_file . program_iter ( ) {
6375 program:: sanity_check ( program_header, & self . elf_file ) ?;
76+ }
77+
78+ // Apply relocations in physical memory.
79+ for program_header in self . elf_file . program_iter ( ) {
80+ if let Type :: Dynamic = program_header. get_type ( ) ? {
81+ self . inner
82+ . handle_dynamic_segment ( program_header, & self . elf_file ) ?
83+ }
84+ }
85+
86+ // Load the segments into virtual memory.
87+ let mut tls_template = None ;
88+ for program_header in self . elf_file . program_iter ( ) {
6489 match program_header. get_type ( ) ? {
6590 Type :: Load => self . inner . handle_load_segment ( program_header) ?,
6691 Type :: Tls => {
@@ -85,11 +110,14 @@ where
85110 }
86111
87112 fn entry_point ( & self ) -> VirtAddr {
88- VirtAddr :: new ( self . elf_file . header . pt2 . entry_point ( ) )
113+ VirtAddr :: new ( self . elf_file . header . pt2 . entry_point ( ) + self . inner . virtual_address_offset )
89114 }
90115
91116 fn used_level_4_entries ( & self ) -> UsedLevel4Entries {
92- UsedLevel4Entries :: new ( self . elf_file . program_iter ( ) )
117+ UsedLevel4Entries :: new (
118+ self . elf_file . program_iter ( ) ,
119+ self . inner . virtual_address_offset ,
120+ )
93121 }
94122}
95123
@@ -106,7 +134,7 @@ where
106134 let end_frame: PhysFrame =
107135 PhysFrame :: containing_address ( phys_start_addr + segment. file_size ( ) - 1u64 ) ;
108136
109- let virt_start_addr = VirtAddr :: new ( segment. virtual_addr ( ) ) ;
137+ let virt_start_addr = VirtAddr :: new ( segment. virtual_addr ( ) ) + self . virtual_address_offset ;
110138 let start_page: Page = Page :: containing_address ( virt_start_addr) ;
111139
112140 let mut segment_flags = Flags :: PRESENT ;
@@ -146,7 +174,7 @@ where
146174 ) -> Result < ( ) , & ' static str > {
147175 log:: info!( "Mapping bss section" ) ;
148176
149- let virt_start_addr = VirtAddr :: new ( segment. virtual_addr ( ) ) ;
177+ let virt_start_addr = VirtAddr :: new ( segment. virtual_addr ( ) ) + self . virtual_address_offset ;
150178 let phys_start_addr = self . kernel_offset + segment. offset ( ) ;
151179 let mem_size = segment. mem_size ( ) ;
152180 let file_size = segment. file_size ( ) ;
@@ -262,11 +290,121 @@ where
262290
263291 fn handle_tls_segment ( & mut self , segment : ProgramHeader ) -> Result < TlsTemplate , & ' static str > {
264292 Ok ( TlsTemplate {
265- start_addr : segment. virtual_addr ( ) ,
293+ start_addr : segment. virtual_addr ( ) + self . virtual_address_offset ,
266294 mem_size : segment. mem_size ( ) ,
267295 file_size : segment. file_size ( ) ,
268296 } )
269297 }
298+
299+ fn handle_dynamic_segment (
300+ & mut self ,
301+ segment : ProgramHeader ,
302+ elf_file : & ElfFile ,
303+ ) -> Result < ( ) , & ' static str > {
304+ let data = segment. get_data ( elf_file) ?;
305+ let data = if let SegmentData :: Dynamic64 ( data) = data {
306+ data
307+ } else {
308+ unreachable ! ( )
309+ } ;
310+
311+ // Find the `Rela`, `RelaSize` and `RelaEnt` entries.
312+ let mut rela = None ;
313+ let mut rela_size = None ;
314+ let mut rela_ent = None ;
315+ for rel in data {
316+ let tag = rel. get_tag ( ) ?;
317+ match tag {
318+ dynamic:: Tag :: Rela => {
319+ let ptr = rel. get_ptr ( ) ?;
320+ let prev = rela. replace ( ptr) ;
321+ if prev. is_some ( ) {
322+ return Err ( "Dynamic section contains more than one Rela entry" ) ;
323+ }
324+ }
325+ dynamic:: Tag :: RelaSize => {
326+ let val = rel. get_val ( ) ?;
327+ let prev = rela_size. replace ( val) ;
328+ if prev. is_some ( ) {
329+ return Err ( "Dynamic section contains more than one RelaSize entry" ) ;
330+ }
331+ }
332+ dynamic:: Tag :: RelaEnt => {
333+ let val = rel. get_val ( ) ?;
334+ let prev = rela_ent. replace ( val) ;
335+ if prev. is_some ( ) {
336+ return Err ( "Dynamic section contains more than one RelaEnt entry" ) ;
337+ }
338+ }
339+ _ => { }
340+ }
341+ }
342+ let offset = if let Some ( rela) = rela {
343+ rela
344+ } else {
345+ // The section doesn't contain any relocations.
346+
347+ assert_eq ! ( rela_size, None ) ;
348+ assert_eq ! ( rela_ent, None ) ;
349+
350+ return Ok ( ( ) ) ;
351+ } ;
352+ let total_size = rela_size. ok_or ( "RelaSize entry is missing" ) ?;
353+ let entry_size = rela_ent. ok_or ( "RelaEnt entry is missing" ) ?;
354+
355+ // Apply the mappings.
356+ let entries = total_size / entry_size;
357+ let relas = unsafe {
358+ core:: slice:: from_raw_parts :: < Rela < u64 > > (
359+ elf_file. input . as_ptr ( ) . add ( offset as usize ) . cast ( ) ,
360+ entries as usize ,
361+ )
362+ } ;
363+ for rela in relas {
364+ let idx = rela. get_symbol_table_index ( ) ;
365+ assert_eq ! (
366+ idx, 0 ,
367+ "relocations using the symbol table are not supported"
368+ ) ;
369+
370+ match rela. get_type ( ) {
371+ 8 => {
372+ let offset_in_file = find_offset ( elf_file, rela. get_offset ( ) ) ?
373+ . ok_or ( "Destination of relocation is not mapped in physical memory" ) ?;
374+ let dest_addr = self . kernel_offset + offset_in_file;
375+ let dest_ptr = dest_addr. as_u64 ( ) as * mut u64 ;
376+
377+ let value = self
378+ . virtual_address_offset
379+ . checked_add ( rela. get_addend ( ) )
380+ . unwrap ( ) ;
381+
382+ unsafe {
383+ // write new value, utilizing that the address identity-mapped
384+ dest_ptr. write ( value) ;
385+ }
386+ }
387+ ty => unimplemented ! ( "relocation type {:x} not supported" , ty) ,
388+ }
389+ }
390+
391+ Ok ( ( ) )
392+ }
393+ }
394+
395+ /// Locate the offset into the elf file corresponding to a virtual address.
396+ fn find_offset ( elf_file : & ElfFile , virt_offset : u64 ) -> Result < Option < u64 > , & ' static str > {
397+ for program_header in elf_file. program_iter ( ) {
398+ if let Type :: Load = program_header. get_type ( ) ? {
399+ if program_header. virtual_addr ( ) <= virt_offset {
400+ let offset_in_segment = virt_offset - program_header. virtual_addr ( ) ;
401+ if offset_in_segment < program_header. file_size ( ) {
402+ return Ok ( Some ( program_header. offset ( ) + offset_in_segment) ) ;
403+ }
404+ }
405+ }
406+ }
407+ Ok ( None )
270408}
271409
272410/// Loads the kernel ELF file given in `bytes` in the given `page_table`.
0 commit comments