Skip to main content

bootloader_x86_64_common/
load_kernel.rs

1use crate::{PAGE_SIZE, level_4_entries::UsedLevel4Entries};
2use bootloader_api::{config::Mapping, info::TlsTemplate};
3use core::{cmp, iter::Step, mem::size_of, ops::Add};
4
5use x86_64::{
6    PhysAddr, VirtAddr, align_up,
7    structures::paging::{
8        FrameAllocator, Page, PageSize, PageTableFlags as Flags, PhysFrame, Size4KiB, Translate,
9        mapper::{MappedFrame, MapperAllSizes, TranslateResult},
10    },
11};
12use xmas_elf::{
13    ElfFile, dynamic, header,
14    program::{self, ProgramHeader, SegmentData, Type},
15    sections::Rela,
16};
17
18use super::Kernel;
19
20/// Used by [`Inner::make_mut`] and [`Inner::clean_copied_flag`].
21const COPIED: Flags = Flags::BIT_9;
22
23struct Loader<'a, M, F> {
24    elf_file: ElfFile<'a>,
25    inner: Inner<'a, M, F>,
26}
27
28struct Inner<'a, M, F> {
29    kernel_offset: PhysAddr,
30    virtual_address_offset: VirtualAddressOffset,
31    page_table: &'a mut M,
32    frame_allocator: &'a mut F,
33}
34
35impl<'a, M, F> Loader<'a, M, F>
36where
37    M: MapperAllSizes + Translate,
38    F: FrameAllocator<Size4KiB>,
39{
40    fn new(
41        kernel: Kernel<'a>,
42        page_table: &'a mut M,
43        frame_allocator: &'a mut F,
44        used_entries: &mut UsedLevel4Entries,
45    ) -> Result<Self, &'static str> {
46        log::info!("Elf file loaded at {:#p}", kernel.elf.input);
47        let kernel_offset = PhysAddr::new(&kernel.elf.input[0] as *const u8 as u64);
48        if !kernel_offset.is_aligned(PAGE_SIZE) {
49            return Err("Loaded kernel ELF file is not sufficiently aligned");
50        }
51
52        let elf_file = kernel.elf;
53        for program_header in elf_file.program_iter() {
54            program::sanity_check(program_header, &elf_file)?;
55        }
56
57        let virtual_address_offset = match elf_file.header.pt2.type_().as_type() {
58            header::Type::None => unimplemented!(),
59            header::Type::Relocatable => unimplemented!(),
60            header::Type::Executable => match kernel.config.mappings.kernel_base {
61                Mapping::Dynamic => VirtualAddressOffset::zero(),
62                _ => {
63                    return Err(concat!(
64                        "Invalid kernel_code mapping. ",
65                        "Executable can only be mapped at virtual_address_offset 0."
66                    ));
67                }
68            },
69            header::Type::SharedObject => {
70                let ElfMemoryRequirements {
71                    size,
72                    align,
73                    min_addr,
74                } = calc_elf_memory_requirements(&elf_file);
75                match kernel.config.mappings.kernel_base {
76                    Mapping::Dynamic => {
77                        let offset = used_entries.get_free_address(size, align).as_u64();
78                        VirtualAddressOffset::new(i128::from(offset) - i128::from(min_addr))
79                    }
80                    Mapping::FixedAddress(address) => {
81                        VirtualAddressOffset::new(i128::from(address))
82                    }
83                }
84            }
85            header::Type::Core => unimplemented!(),
86            header::Type::ProcessorSpecific(_) => unimplemented!(),
87        };
88        log::info!(
89            "virtual_address_offset: {:#x}",
90            virtual_address_offset.virtual_address_offset()
91        );
92
93        used_entries.mark_segments(elf_file.program_iter(), virtual_address_offset);
94
95        header::sanity_check(&elf_file)?;
96        let loader = Loader {
97            elf_file,
98            inner: Inner {
99                kernel_offset,
100                virtual_address_offset,
101                page_table,
102                frame_allocator,
103            },
104        };
105
106        Ok(loader)
107    }
108
109    fn load_segments(&mut self) -> Result<Option<TlsTemplate>, &'static str> {
110        // Load the segments into virtual memory.
111        let mut tls_template = None;
112        for program_header in self.elf_file.program_iter() {
113            match program_header.get_type()? {
114                Type::Load => self.inner.handle_load_segment(program_header)?,
115                Type::Tls => {
116                    if tls_template.is_none() {
117                        tls_template = Some(self.inner.handle_tls_segment(program_header)?);
118                    } else {
119                        return Err("multiple TLS segments not supported");
120                    }
121                }
122                Type::Null
123                | Type::Dynamic
124                | Type::Interp
125                | Type::Note
126                | Type::ShLib
127                | Type::Phdr
128                | Type::GnuRelro
129                | Type::OsSpecific(_)
130                | Type::ProcessorSpecific(_) => {}
131            }
132        }
133
134        // Apply relocations in virtual memory.
135        for program_header in self.elf_file.program_iter() {
136            if let Type::Dynamic = program_header.get_type()? {
137                self.inner
138                    .handle_dynamic_segment(program_header, &self.elf_file)?
139            }
140        }
141
142        // Mark some memory regions as read-only after relocations have been
143        // applied.
144        for program_header in self.elf_file.program_iter() {
145            if let Type::GnuRelro = program_header.get_type()? {
146                self.inner.handle_relro_segment(program_header);
147            }
148        }
149
150        self.inner.remove_copied_flags(&self.elf_file).unwrap();
151
152        Ok(tls_template)
153    }
154
155    fn entry_point(&self) -> VirtAddr {
156        VirtAddr::new(self.inner.virtual_address_offset + self.elf_file.header.pt2.entry_point())
157    }
158}
159
160impl<'a, M, F> Inner<'a, M, F>
161where
162    M: MapperAllSizes + Translate,
163    F: FrameAllocator<Size4KiB>,
164{
165    fn handle_load_segment(&mut self, segment: ProgramHeader) -> Result<(), &'static str> {
166        log::info!("Handling Segment: {:x?}", segment);
167
168        let phys_start_addr = self.kernel_offset + segment.offset();
169        let start_frame: PhysFrame = PhysFrame::containing_address(phys_start_addr);
170        let end_frame: PhysFrame =
171            PhysFrame::containing_address(phys_start_addr + segment.file_size() - 1u64);
172
173        let virt_start_addr = VirtAddr::new(self.virtual_address_offset + segment.virtual_addr());
174        let start_page: Page = Page::containing_address(virt_start_addr);
175
176        let mut segment_flags = Flags::PRESENT;
177        if !segment.flags().is_execute() {
178            segment_flags |= Flags::NO_EXECUTE;
179        }
180        if segment.flags().is_write() {
181            segment_flags |= Flags::WRITABLE;
182        }
183
184        // map all frames of the segment at the desired virtual address
185        for frame in PhysFrame::range_inclusive(start_frame, end_frame) {
186            let offset = frame - start_frame;
187            let page = start_page + offset;
188            let flusher = unsafe {
189                // The parent table flags need to be both readable and writable to
190                // support recursive page tables.
191                // See https://github.com/rust-osdev/bootloader/issues/443#issuecomment-2130010621
192                self.page_table
193                    .map_to_with_table_flags(
194                        page,
195                        frame,
196                        segment_flags,
197                        Flags::PRESENT | Flags::WRITABLE,
198                        self.frame_allocator,
199                    )
200                    .map_err(|_err| "map_to failed")?
201            };
202            // we operate on an inactive page table, so there's no need to flush anything
203            flusher.ignore();
204        }
205
206        // Handle .bss section (mem_size > file_size)
207        if segment.mem_size() > segment.file_size() {
208            // .bss section (or similar), which needs to be mapped and zeroed
209            self.handle_bss_section(&segment, segment_flags)?;
210        }
211
212        Ok(())
213    }
214
215    fn handle_bss_section(
216        &mut self,
217        segment: &ProgramHeader,
218        segment_flags: Flags,
219    ) -> Result<(), &'static str> {
220        log::info!("Mapping bss section");
221
222        let virt_start_addr = VirtAddr::new(self.virtual_address_offset + segment.virtual_addr());
223        let mem_size = segment.mem_size();
224        let file_size = segment.file_size();
225
226        // calculate virtual memory region that must be zeroed
227        let zero_start = virt_start_addr + file_size;
228        let zero_end = virt_start_addr + mem_size;
229
230        // a type alias that helps in efficiently clearing a page
231        type PageArray = [u64; Size4KiB::SIZE as usize / 8];
232        const ZERO_ARRAY: PageArray = [0; Size4KiB::SIZE as usize / 8];
233
234        // In some cases, `zero_start` might not be page-aligned. This requires some
235        // special treatment because we can't safely zero a frame of the original file.
236        let data_bytes_before_zero = zero_start.as_u64() & 0xfff;
237        if data_bytes_before_zero != 0 {
238            // The last non-bss frame of the segment consists partly of data and partly of bss
239            // memory, which must be zeroed. Unfortunately, the file representation might have
240            // reused the part of the frame that should be zeroed to store the next segment. This
241            // means that we can't simply overwrite that part with zeroes, as we might overwrite
242            // other data this way.
243            //
244            // Example:
245            //
246            //   XXXXXXXXXXXXXXX000000YYYYYYY000ZZZZZZZZZZZ     virtual memory (XYZ are data)
247            //   |·············|     /·····/   /·········/
248            //   |·············| ___/·····/   /·········/
249            //   |·············|/·····/‾‾‾   /·········/
250            //   |·············||·····|/·̅·̅·̅·̅·̅·····/‾‾‾‾
251            //   XXXXXXXXXXXXXXXYYYYYYYZZZZZZZZZZZ              file memory (zeros are not saved)
252            //   '       '       '       '        '
253            //   The areas filled with dots (`·`) indicate a mapping between virtual and file
254            //   memory. We see that the data regions `X`, `Y`, `Z` have a valid mapping, while
255            //   the regions that are initialized with 0 have not.
256            //
257            //   The ticks (`'`) below the file memory line indicate the start of a new frame. We
258            //   see that the last frames of the `X` and `Y` regions in the file are followed
259            //   by the bytes of the next region. So we can't zero these parts of the frame
260            //   because they are needed by other memory regions.
261            //
262            // To solve this problem, we need to allocate a new frame for the last segment page
263            // and copy all data content of the original frame over. Afterwards, we can zero
264            // the remaining part of the frame since the frame is no longer shared with other
265            // segments now.
266
267            let last_page = Page::containing_address(virt_start_addr + file_size - 1u64);
268            let new_frame = unsafe { self.make_mut(last_page) };
269            let new_bytes_ptr = new_frame.start_address().as_u64() as *mut u8;
270            unsafe {
271                core::ptr::write_bytes(
272                    new_bytes_ptr.add(data_bytes_before_zero as usize),
273                    0,
274                    (Size4KiB::SIZE - data_bytes_before_zero) as usize,
275                );
276            }
277        }
278
279        // map additional frames for `.bss` memory that is not present in source file
280        let start_page: Page =
281            Page::containing_address(VirtAddr::new(align_up(zero_start.as_u64(), Size4KiB::SIZE)));
282        let end_page = Page::containing_address(zero_end - 1u64);
283        for page in Page::range_inclusive(start_page, end_page) {
284            // allocate a new unused frame
285            let frame = self.frame_allocator.allocate_frame().unwrap();
286
287            // zero frame, utilizing identity-mapping
288            let frame_ptr = frame.start_address().as_u64() as *mut PageArray;
289            unsafe { frame_ptr.write(ZERO_ARRAY) };
290
291            // map frame
292            let flusher = unsafe {
293                // The parent table flags need to be both readable and writable to
294                // support recursive page tables.
295                // See https://github.com/rust-osdev/bootloader/issues/443#issuecomment-2130010621
296                self.page_table
297                    .map_to_with_table_flags(
298                        page,
299                        frame,
300                        segment_flags,
301                        Flags::PRESENT | Flags::WRITABLE,
302                        self.frame_allocator,
303                    )
304                    .map_err(|_err| "Failed to map new frame for bss memory")?
305            };
306            // we operate on an inactive page table, so we don't need to flush our changes
307            flusher.ignore();
308        }
309
310        Ok(())
311    }
312
313    /// Copy from the kernel address space.
314    ///
315    /// ## Panics
316    ///
317    /// Panics if a page is not mapped in `self.page_table`.
318    fn copy_from(&self, addr: VirtAddr, buf: &mut [u8]) {
319        // We can't know for sure that contiguous virtual address are contiguous
320        // in physical memory, so we iterate of the pages spanning the
321        // addresses, translate them to frames and copy the data.
322
323        let end_inclusive_addr = Step::forward_checked(addr, buf.len() - 1)
324            .expect("end address outside of the virtual address space");
325        let start_page = Page::<Size4KiB>::containing_address(addr);
326        let end_inclusive_page = Page::<Size4KiB>::containing_address(end_inclusive_addr);
327
328        for page in start_page..=end_inclusive_page {
329            // Translate the virtual page to the physical frame.
330            let phys_addr = self
331                .page_table
332                .translate_page(page)
333                .expect("address is not mapped to the kernel's memory space");
334
335            // Figure out which address range we want to copy from the frame.
336
337            // This page covers these addresses.
338            let page_start = page.start_address();
339            let page_end_inclusive = page.start_address() + 4095u64;
340
341            // We want to copy from the following address in this frame.
342            let start_copy_address = cmp::max(addr, page_start);
343            let end_inclusive_copy_address = cmp::min(end_inclusive_addr, page_end_inclusive);
344
345            // These are the offsets into the frame we want to copy from.
346            let start_offset_in_frame = start_copy_address - page_start;
347            let end_inclusive_offset_in_frame = end_inclusive_copy_address - page_start;
348
349            // Calculate how many bytes we want to copy from this frame.
350            let copy_len = end_inclusive_offset_in_frame - start_offset_in_frame + 1;
351
352            // Calculate the physical addresses.
353            let start_phys_addr = phys_addr.start_address() + start_offset_in_frame;
354
355            // These are the offsets from the start address. These correspond
356            // to the destination indices in `buf`.
357            let start_offset_in_buf = Step::steps_between(&addr, &start_copy_address).1.unwrap();
358
359            // Calculate the source slice.
360            // Utilize that frames are identity mapped.
361            let src_ptr = start_phys_addr.as_u64() as *const u8;
362            let src = unsafe {
363                // SAFETY: We know that this memory is valid because we got it
364                // as a result from a translation. There are not other
365                // references to it.
366                &*core::ptr::slice_from_raw_parts(src_ptr, copy_len as usize)
367            };
368
369            // Calculate the destination pointer.
370            let dest = &mut buf[start_offset_in_buf..][..copy_len as usize];
371
372            // Do the actual copy.
373            dest.copy_from_slice(src);
374        }
375    }
376
377    /// Write to the kernel address space.
378    ///
379    /// ## Safety
380    /// - `addr` should refer to a page mapped by a Load segment.
381    ///  
382    /// ## Panics
383    ///
384    /// Panics if a page is not mapped in `self.page_table`.
385    unsafe fn copy_to(&mut self, addr: VirtAddr, buf: &[u8]) {
386        // We can't know for sure that contiguous virtual address are contiguous
387        // in physical memory, so we iterate of the pages spanning the
388        // addresses, translate them to frames and copy the data.
389
390        let end_inclusive_addr = Step::forward_checked(addr, buf.len() - 1)
391            .expect("the end address should be in the virtual address space");
392        let start_page = Page::<Size4KiB>::containing_address(addr);
393        let end_inclusive_page = Page::<Size4KiB>::containing_address(end_inclusive_addr);
394
395        for page in start_page..=end_inclusive_page {
396            // Translate the virtual page to the physical frame.
397            let phys_addr = unsafe {
398                // SAFETY: The caller asserts that the pages are mapped by a Load segment.
399                self.make_mut(page)
400            };
401
402            // Figure out which address range we want to copy from the frame.
403
404            // This page covers these addresses.
405            let page_start = page.start_address();
406            let page_end_inclusive = page.start_address() + 4095u64;
407
408            // We want to copy from the following address in this frame.
409            let start_copy_address = cmp::max(addr, page_start);
410            let end_inclusive_copy_address = cmp::min(end_inclusive_addr, page_end_inclusive);
411
412            // These are the offsets into the frame we want to copy from.
413            let start_offset_in_frame = start_copy_address - page_start;
414            let end_inclusive_offset_in_frame = end_inclusive_copy_address - page_start;
415
416            // Calculate how many bytes we want to copy from this frame.
417            let copy_len = end_inclusive_offset_in_frame - start_offset_in_frame + 1;
418
419            // Calculate the physical addresses.
420            let start_phys_addr = phys_addr.start_address() + start_offset_in_frame;
421
422            // These are the offsets from the start address. These correspond
423            // to the destination indices in `buf`.
424            let start_offset_in_buf = Step::steps_between(&addr, &start_copy_address).1.unwrap();
425
426            // Calculate the source slice.
427            // Utilize that frames are identity mapped.
428            let dest_ptr = start_phys_addr.as_u64() as *mut u8;
429            let dest = unsafe {
430                // SAFETY: We know that this memory is valid because we got it
431                // as a result from a translation. There are not other
432                // references to it.
433                &mut *core::ptr::slice_from_raw_parts_mut(dest_ptr, copy_len as usize)
434            };
435
436            // Calculate the destination pointer.
437            let src = &buf[start_offset_in_buf..][..copy_len as usize];
438
439            // Do the actual copy.
440            dest.copy_from_slice(src);
441        }
442    }
443
444    /// This method is intended for making the memory loaded by a Load segment mutable.
445    ///
446    /// All memory from a Load segment starts out by mapped to the same frames that
447    /// contain the elf file. Thus writing to memory in that state will cause aliasing issues.
448    /// To avoid that, we allocate a new frame, copy all bytes from the old frame to the new frame,
449    /// and remap the page to the new frame. At this point the page no longer aliases the elf file
450    /// and we can write to it.
451    ///
452    /// When we map the new frame we also set [`COPIED`] flag in the page table flags, so that
453    /// we can detect if the frame has already been copied when we try to modify the page again.
454    ///
455    /// ## Safety
456    /// - `page` should be a page mapped by a Load segment.
457    ///  
458    /// ## Panics
459    /// Panics if the page is not mapped in `self.page_table`.
460    unsafe fn make_mut(&mut self, page: Page) -> PhysFrame {
461        let (frame, flags) = match self.page_table.translate(page.start_address()) {
462            TranslateResult::Mapped {
463                frame,
464                offset: _,
465                flags,
466            } => (frame, flags),
467            TranslateResult::NotMapped => panic!("{:?} is not mapped", page),
468            TranslateResult::InvalidFrameAddress(_) => unreachable!(),
469        };
470        let frame = if let MappedFrame::Size4KiB(frame) = frame {
471            frame
472        } else {
473            // We only map 4k pages.
474            unreachable!()
475        };
476
477        if flags.contains(COPIED) {
478            // The frame was already copied, we are free to modify it.
479            return frame;
480        }
481
482        // Allocate a new frame and copy the memory, utilizing that both frames are identity mapped.
483        let new_frame = self.frame_allocator.allocate_frame().unwrap();
484        let frame_ptr = frame.start_address().as_u64() as *const u8;
485        let new_frame_ptr = new_frame.start_address().as_u64() as *mut u8;
486        unsafe {
487            core::ptr::copy_nonoverlapping(frame_ptr, new_frame_ptr, Size4KiB::SIZE as usize);
488        }
489
490        // Replace the underlying frame and update the flags.
491        self.page_table.unmap(page).unwrap().1.ignore();
492        let new_flags = flags | COPIED;
493        unsafe {
494            self.page_table
495                .map_to(page, new_frame, new_flags, self.frame_allocator)
496                .unwrap()
497                .ignore();
498        }
499
500        new_frame
501    }
502
503    /// Cleans up the custom flags set by [`Inner::make_mut`].
504    fn remove_copied_flags(&mut self, elf_file: &ElfFile) -> Result<(), &'static str> {
505        for program_header in elf_file.program_iter() {
506            if let Type::Load = program_header.get_type()? {
507                if program_header.mem_size() == 0 {
508                    continue;
509                }
510                let start = self.virtual_address_offset + program_header.virtual_addr();
511                let end = start + (program_header.mem_size() - 1);
512                let start = VirtAddr::new(start);
513                let end = VirtAddr::new(end);
514                let start_page = Page::containing_address(start);
515                let end_page = Page::containing_address(end);
516                for page in Page::<Size4KiB>::range_inclusive(start_page, end_page) {
517                    // Translate the page and get the flags.
518                    let res = self.page_table.translate(page.start_address());
519                    let flags = match res {
520                        TranslateResult::Mapped {
521                            frame: _,
522                            offset: _,
523                            flags,
524                        } => flags,
525                        TranslateResult::NotMapped | TranslateResult::InvalidFrameAddress(_) => {
526                            unreachable!("has the elf file not been mapped correctly?")
527                        }
528                    };
529
530                    if flags.contains(COPIED) {
531                        // Remove the flag.
532                        unsafe {
533                            self.page_table
534                                .update_flags(page, flags & !COPIED)
535                                .unwrap()
536                                .ignore();
537                        }
538                    }
539                }
540            }
541        }
542        Ok(())
543    }
544
545    fn handle_tls_segment(&mut self, segment: ProgramHeader) -> Result<TlsTemplate, &'static str> {
546        Ok(TlsTemplate {
547            start_addr: self.virtual_address_offset + segment.virtual_addr(),
548            mem_size: segment.mem_size(),
549            file_size: segment.file_size(),
550        })
551    }
552
553    fn handle_dynamic_segment(
554        &mut self,
555        segment: ProgramHeader,
556        elf_file: &ElfFile,
557    ) -> Result<(), &'static str> {
558        let data = segment.get_data(elf_file)?;
559        let data = if let SegmentData::Dynamic64(data) = data {
560            data
561        } else {
562            panic!("expected Dynamic64 segment")
563        };
564
565        // Find the `Rela`, `RelaSize` and `RelaEnt` entries.
566        let mut rela = None;
567        let mut rela_size = None;
568        let mut rela_ent = None;
569        for rel in data {
570            let tag = rel.get_tag()?;
571            match tag {
572                dynamic::Tag::Rela => {
573                    let ptr = rel.get_ptr()?;
574                    let prev = rela.replace(ptr);
575                    if prev.is_some() {
576                        return Err("Dynamic section contains more than one Rela entry");
577                    }
578                }
579                dynamic::Tag::RelaSize => {
580                    let val = rel.get_val()?;
581                    let prev = rela_size.replace(val);
582                    if prev.is_some() {
583                        return Err("Dynamic section contains more than one RelaSize entry");
584                    }
585                }
586                dynamic::Tag::RelaEnt => {
587                    let val = rel.get_val()?;
588                    let prev = rela_ent.replace(val);
589                    if prev.is_some() {
590                        return Err("Dynamic section contains more than one RelaEnt entry");
591                    }
592                }
593                _ => {}
594            }
595        }
596        let offset = if let Some(rela) = rela {
597            rela
598        } else {
599            // The section doesn't contain any relocations.
600
601            if rela_size.is_some() || rela_ent.is_some() {
602                return Err("Rela entry is missing but RelaSize or RelaEnt have been provided");
603            }
604
605            return Ok(());
606        };
607        let total_size = rela_size.ok_or("RelaSize entry is missing")?;
608        let entry_size = rela_ent.ok_or("RelaEnt entry is missing")?;
609
610        // Make sure that the reported size matches our `Rela<u64>`.
611        assert_eq!(
612            entry_size,
613            size_of::<Rela<u64>>() as u64,
614            "unsupported entry size: {entry_size}"
615        );
616
617        // Apply the relocations.
618        let num_entries = total_size / entry_size;
619        for idx in 0..num_entries {
620            let rela = self.read_relocation(offset, idx);
621            self.apply_relocation(rela, elf_file)?;
622        }
623
624        Ok(())
625    }
626
627    /// Reads a relocation from a relocation table.
628    fn read_relocation(&self, relocation_table: u64, idx: u64) -> Rela<u64> {
629        // Calculate the address of the entry in the relocation table.
630        let offset = relocation_table + size_of::<Rela<u64>>() as u64 * idx;
631        let value = self.virtual_address_offset + offset;
632        let addr = VirtAddr::try_new(value).expect("relocation table is outside the address space");
633
634        // Read the Rela from the kernel address space.
635        let mut buf = [0; 24];
636        self.copy_from(addr, &mut buf);
637
638        // Convert the bytes we read into a `Rela<u64>`.
639        unsafe {
640            // SAFETY: Any bitpattern is valid for `Rela<u64>` and buf is
641            // valid for reads.
642            core::ptr::read_unaligned(&buf as *const u8 as *const Rela<u64>)
643        }
644    }
645
646    fn apply_relocation(
647        &mut self,
648        rela: Rela<u64>,
649        elf_file: &ElfFile,
650    ) -> Result<(), &'static str> {
651        let symbol_idx = rela.get_symbol_table_index();
652        assert_eq!(
653            symbol_idx, 0,
654            "relocations using the symbol table are not supported"
655        );
656
657        match rela.get_type() {
658            // R_AMD64_RELATIVE
659            8 => {
660                // Make sure that the relocation happens in memory mapped
661                // by a Load segment.
662                check_is_in_load(elf_file, rela.get_offset())?;
663
664                // Calculate the destination of the relocation.
665                let addr = self.virtual_address_offset + rela.get_offset();
666                let addr = VirtAddr::new(addr);
667
668                // Calculate the relocated value.
669                let value = self.virtual_address_offset + rela.get_addend();
670
671                // Write the relocated value to memory.
672                unsafe {
673                    // SAFETY: We just verified that the address is in a Load segment.
674                    self.copy_to(addr, &value.to_ne_bytes());
675                }
676            }
677            ty => unimplemented!("relocation type {:x} not supported", ty),
678        }
679
680        Ok(())
681    }
682
683    /// Mark a region of memory indicated by a GNU_RELRO segment as read-only.
684    ///
685    /// This is a security mitigation used to protect memory regions that
686    /// need to be writable while applying relocations, but should never be
687    /// written to after relocations have been applied.
688    fn handle_relro_segment(&mut self, program_header: ProgramHeader) {
689        let start = self.virtual_address_offset + program_header.virtual_addr();
690        let end = start + program_header.mem_size();
691        let start = VirtAddr::new(start);
692        let end = VirtAddr::new(end);
693        let start_page = Page::containing_address(start);
694        let end_page = Page::containing_address(end - 1u64);
695        for page in Page::<Size4KiB>::range_inclusive(start_page, end_page) {
696            // Translate the page and get the flags.
697            let res = self.page_table.translate(page.start_address());
698            let flags = match res {
699                TranslateResult::Mapped {
700                    frame: _,
701                    offset: _,
702                    flags,
703                } => flags,
704                TranslateResult::NotMapped | TranslateResult::InvalidFrameAddress(_) => {
705                    unreachable!("has the elf file not been mapped correctly?")
706                }
707            };
708
709            if flags.contains(Flags::WRITABLE) {
710                // Remove the WRITABLE flag.
711                unsafe {
712                    self.page_table
713                        .update_flags(page, flags & !Flags::WRITABLE)
714                        .unwrap()
715                        .ignore();
716                }
717            }
718        }
719    }
720}
721
722/// Check that the virtual offset belongs to a load segment.
723fn check_is_in_load(elf_file: &ElfFile, virt_offset: u64) -> Result<(), &'static str> {
724    for program_header in elf_file.program_iter() {
725        if let Type::Load = program_header.get_type()? {
726            if program_header.virtual_addr() <= virt_offset {
727                let offset_in_segment = virt_offset - program_header.virtual_addr();
728                if offset_in_segment < program_header.mem_size() {
729                    return Ok(());
730                }
731            }
732        }
733    }
734    Err("offset is not in load segment")
735}
736
737/// Loads the kernel ELF file given in `bytes` in the given `page_table`.
738///
739/// Returns the kernel entry point address, it's thread local storage template (if any),
740/// and a structure describing which level 4 page table entries are in use.  
741pub fn load_kernel(
742    kernel: Kernel<'_>,
743    page_table: &mut (impl MapperAllSizes + Translate),
744    frame_allocator: &mut impl FrameAllocator<Size4KiB>,
745    used_entries: &mut UsedLevel4Entries,
746) -> Result<(VirtAddr, VirtAddr, Option<TlsTemplate>), &'static str> {
747    let mut loader = Loader::new(kernel, page_table, frame_allocator, used_entries)?;
748    let tls_template = loader.load_segments()?;
749
750    Ok((
751        VirtAddr::new(loader.inner.virtual_address_offset.virtual_address_offset() as u64),
752        loader.entry_point(),
753        tls_template,
754    ))
755}
756
757/// Basic information about the memory segments of an elf file.
758pub struct ElfMemoryRequirements {
759    /// total size needed for all segments
760    pub size: u64,
761    /// memory alignment for the elf file
762    pub align: u64,
763    /// the smallest virtual address used by the elf file
764    pub min_addr: u64,
765}
766
767/// Calculates basic requirements needed to allocate memory for an elf file.
768pub fn calc_elf_memory_requirements(elf_file: &ElfFile) -> ElfMemoryRequirements {
769    // Find the highest virtual memory address and the biggest alignment.
770    let load_program_headers = elf_file
771        .program_iter()
772        .filter(|h| matches!(h.get_type(), Ok(Type::Load)));
773    let max_addr = load_program_headers
774        .clone()
775        .map(|h| h.virtual_addr() + h.mem_size())
776        .max()
777        .unwrap_or(0);
778    let min_addr = load_program_headers
779        .clone()
780        .map(|h| h.virtual_addr())
781        .min()
782        .unwrap_or(0);
783    let size = max_addr - min_addr;
784    let align = load_program_headers.map(|h| h.align()).max().unwrap_or(1);
785    ElfMemoryRequirements {
786        size,
787        align,
788        min_addr,
789    }
790}
791
792/// A helper type used to offset virtual addresses for position independent
793/// executables.
794#[derive(Clone, Copy)]
795pub struct VirtualAddressOffset {
796    virtual_address_offset: i128,
797}
798
799impl VirtualAddressOffset {
800    pub fn zero() -> Self {
801        Self::new(0)
802    }
803
804    pub fn new(virtual_address_offset: i128) -> Self {
805        Self {
806            virtual_address_offset,
807        }
808    }
809
810    pub fn virtual_address_offset(&self) -> i128 {
811        self.virtual_address_offset
812    }
813}
814
815impl Add<u64> for VirtualAddressOffset {
816    type Output = u64;
817
818    fn add(self, offset: u64) -> Self::Output {
819        u64::try_from(
820            self.virtual_address_offset
821                .checked_add(i128::from(offset))
822                .unwrap(),
823        )
824        .unwrap()
825    }
826}