@@ -11,8 +11,9 @@ use x86_64::{
11
11
PhysAddr , VirtAddr ,
12
12
} ;
13
13
use xmas_elf:: {
14
- header,
15
- program:: { self , ProgramHeader , Type } ,
14
+ dynamic, header,
15
+ program:: { self , ProgramHeader , SegmentData , Type } ,
16
+ sections:: Rela ,
16
17
ElfFile ,
17
18
} ;
18
19
@@ -23,6 +24,7 @@ struct Loader<'a, M, F> {
23
24
24
25
struct Inner < ' a , M , F > {
25
26
kernel_offset : PhysAddr ,
27
+ virtual_address_offset : u64 ,
26
28
page_table : & ' a mut M ,
27
29
frame_allocator : & ' a mut F ,
28
30
}
@@ -44,11 +46,22 @@ where
44
46
}
45
47
46
48
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
+
47
59
header:: sanity_check ( & elf_file) ?;
48
60
let loader = Loader {
49
61
elf_file,
50
62
inner : Inner {
51
63
kernel_offset,
64
+ virtual_address_offset,
52
65
page_table,
53
66
frame_allocator,
54
67
} ,
58
71
}
59
72
60
73
fn load_segments ( & mut self ) -> Result < Option < TlsTemplate > , & ' static str > {
61
- let mut tls_template = None ;
62
74
for program_header in self . elf_file . program_iter ( ) {
63
75
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 ( ) {
64
89
match program_header. get_type ( ) ? {
65
90
Type :: Load => self . inner . handle_load_segment ( program_header) ?,
66
91
Type :: Tls => {
@@ -85,11 +110,14 @@ where
85
110
}
86
111
87
112
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 )
89
114
}
90
115
91
116
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
+ )
93
121
}
94
122
}
95
123
@@ -106,7 +134,7 @@ where
106
134
let end_frame: PhysFrame =
107
135
PhysFrame :: containing_address ( phys_start_addr + segment. file_size ( ) - 1u64 ) ;
108
136
109
- let virt_start_addr = VirtAddr :: new ( segment. virtual_addr ( ) ) ;
137
+ let virt_start_addr = VirtAddr :: new ( segment. virtual_addr ( ) ) + self . virtual_address_offset ;
110
138
let start_page: Page = Page :: containing_address ( virt_start_addr) ;
111
139
112
140
let mut segment_flags = Flags :: PRESENT ;
@@ -146,7 +174,7 @@ where
146
174
) -> Result < ( ) , & ' static str > {
147
175
log:: info!( "Mapping bss section" ) ;
148
176
149
- let virt_start_addr = VirtAddr :: new ( segment. virtual_addr ( ) ) ;
177
+ let virt_start_addr = VirtAddr :: new ( segment. virtual_addr ( ) ) + self . virtual_address_offset ;
150
178
let phys_start_addr = self . kernel_offset + segment. offset ( ) ;
151
179
let mem_size = segment. mem_size ( ) ;
152
180
let file_size = segment. file_size ( ) ;
@@ -262,11 +290,121 @@ where
262
290
263
291
fn handle_tls_segment ( & mut self , segment : ProgramHeader ) -> Result < TlsTemplate , & ' static str > {
264
292
Ok ( TlsTemplate {
265
- start_addr : segment. virtual_addr ( ) ,
293
+ start_addr : segment. virtual_addr ( ) + self . virtual_address_offset ,
266
294
mem_size : segment. mem_size ( ) ,
267
295
file_size : segment. file_size ( ) ,
268
296
} )
269
297
}
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 )
270
408
}
271
409
272
410
/// Loads the kernel ELF file given in `bytes` in the given `page_table`.
0 commit comments