linux_loader/loader/elf/
mod.rs

1// Copyright © 2020, Oracle and/or its affiliates.
2// Copyright (c) 2019 Intel Corporation. All rights reserved.
3// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
4//
5// Copyright 2017 The Chromium OS Authors. All rights reserved.
6// Use of this source code is governed by a BSD-style license that can be
7// found in the LICENSE-BSD-3-Clause file.
8//
9// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
10
11//! Traits and structs for loading elf image kernels into guest memory.
12
13#![cfg(all(feature = "elf", any(target_arch = "x86", target_arch = "x86_64")))]
14
15use std::fmt;
16use std::io::{Read, Seek, SeekFrom};
17use std::mem;
18use std::result;
19
20use vm_memory::{Address, ByteValued, GuestAddress, GuestMemory, GuestUsize, ReadVolatile};
21
22use crate::loader::{Error as KernelLoaderError, KernelLoader, KernelLoaderResult, Result};
23use crate::loader_gen::elf;
24pub use crate::loader_gen::start_info;
25
26// SAFETY: The layout of the structure is fixed and can be initialized by
27// reading its content from byte array.
28unsafe impl ByteValued for elf::Elf64_Ehdr {}
29
30// SAFETY: The layout of the structure is fixed and can be initialized by
31// reading its content from byte array.
32unsafe impl ByteValued for elf::Elf64_Nhdr {}
33
34// SAFETY: The layout of the structure is fixed and can be initialized by
35// reading its content from byte array.
36unsafe impl ByteValued for elf::Elf64_Phdr {}
37
38#[derive(Debug, PartialEq, Eq)]
39/// Elf kernel loader errors.
40pub enum Error {
41    /// Invalid alignment.
42    Align,
43    /// Loaded big endian binary on a little endian platform.
44    BigEndianElfOnLittle,
45    /// Invalid ELF magic number.
46    InvalidElfMagicNumber,
47    /// Invalid program header size.
48    InvalidProgramHeaderSize,
49    /// Invalid program header offset.
50    InvalidProgramHeaderOffset,
51    /// Invalid program header address.
52    InvalidProgramHeaderAddress,
53    /// Invalid entry address.
54    InvalidEntryAddress,
55    /// Overflow occurred during an arithmetic operation.
56    Overflow,
57    /// Unable to read ELF header.
58    ReadElfHeader,
59    /// Unable to read kernel image.
60    ReadKernelImage,
61    /// Unable to read program header.
62    ReadProgramHeader,
63    /// Unable to seek to kernel start.
64    SeekKernelStart,
65    /// Unable to seek to ELF start.
66    SeekElfStart,
67    /// Unable to seek to program header.
68    SeekProgramHeader,
69    /// Unable to seek to note header.
70    SeekNoteHeader,
71    /// Unable to read note header.
72    ReadNoteHeader,
73    /// Invalid PVH note.
74    InvalidPvhNote,
75}
76
77impl fmt::Display for Error {
78    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
79        let desc = match self {
80            Error::Align => "Invalid alignment",
81            Error::BigEndianElfOnLittle => {
82                "Trying to load big-endian binary on little-endian machine"
83            }
84            Error::InvalidElfMagicNumber => "Invalid Elf magic number",
85            Error::InvalidProgramHeaderSize => "Invalid program header size",
86            Error::InvalidProgramHeaderOffset => "Invalid program header offset",
87            Error::InvalidProgramHeaderAddress => "Invalid Program Header Address",
88            Error::InvalidEntryAddress => "Invalid entry address",
89            Error::Overflow => "Overflow occurred during an arithmetic operation",
90            Error::ReadElfHeader => "Unable to read elf header",
91            Error::ReadKernelImage => "Unable to read kernel image",
92            Error::ReadProgramHeader => "Unable to read program header",
93            Error::SeekKernelStart => "Unable to seek to kernel start",
94            Error::SeekElfStart => "Unable to seek to elf start",
95            Error::SeekProgramHeader => "Unable to seek to program header",
96            Error::SeekNoteHeader => "Unable to seek to note header",
97            Error::ReadNoteHeader => "Unable to read note header",
98            Error::InvalidPvhNote => "Invalid PVH note header",
99        };
100
101        write!(f, "Kernel Loader: {}", desc)
102    }
103}
104
105impl std::error::Error for Error {}
106
107#[derive(Clone, Default, Copy, Debug, PartialEq, Eq)]
108/// Availability of PVH entry point in the kernel, which allows the VMM
109/// to use the PVH boot protocol to start guests.
110pub enum PvhBootCapability {
111    /// PVH entry point is present
112    PvhEntryPresent(GuestAddress),
113    /// PVH entry point is not present
114    PvhEntryNotPresent,
115    /// PVH entry point is ignored, even if available
116    #[default]
117    PvhEntryIgnored,
118}
119
120impl fmt::Display for PvhBootCapability {
121    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
122        use self::PvhBootCapability::*;
123        match self {
124            PvhEntryPresent(pvh_entry_addr) => write!(
125                f,
126                "PVH entry point present at guest address: {:#x}",
127                pvh_entry_addr.raw_value()
128            ),
129            PvhEntryNotPresent => write!(f, "PVH entry point not present"),
130            PvhEntryIgnored => write!(f, "PVH entry point ignored"),
131        }
132    }
133}
134
135/// Raw ELF (a.k.a. vmlinux) kernel image support.
136pub struct Elf;
137
138impl Elf {
139    /// Verifies that magic numbers are present in the Elf header.
140    fn validate_header(ehdr: &elf::Elf64_Ehdr) -> std::result::Result<(), Error> {
141        // Sanity checks
142        if ehdr.e_ident[elf::EI_MAG0 as usize] != elf::ELFMAG0 as u8
143            || ehdr.e_ident[elf::EI_MAG1 as usize] != elf::ELFMAG1
144            || ehdr.e_ident[elf::EI_MAG2 as usize] != elf::ELFMAG2
145            || ehdr.e_ident[elf::EI_MAG3 as usize] != elf::ELFMAG3
146        {
147            return Err(Error::InvalidElfMagicNumber);
148        }
149        if ehdr.e_ident[elf::EI_DATA as usize] != elf::ELFDATA2LSB as u8 {
150            return Err(Error::BigEndianElfOnLittle);
151        }
152        if ehdr.e_phentsize as usize != mem::size_of::<elf::Elf64_Phdr>() {
153            return Err(Error::InvalidProgramHeaderSize);
154        }
155        if (ehdr.e_phoff as usize) < mem::size_of::<elf::Elf64_Ehdr>() {
156            return Err(Error::InvalidProgramHeaderOffset);
157        }
158        Ok(())
159    }
160}
161
162impl KernelLoader for Elf {
163    /// Loads a kernel from a vmlinux elf image into guest memory.
164    ///
165    /// By default, the kernel is loaded into guest memory at offset `phdr.p_paddr` specified
166    /// by the elf image. When used, `kernel_offset` specifies a fixed offset from `phdr.p_paddr`
167    /// at which to load the kernel. If `kernel_offset` is requested, the `pvh_entry_addr` field
168    /// of the result will not be populated.
169    ///
170    /// # Arguments
171    ///
172    /// * `guest_mem`: [`GuestMemory`] to load the kernel in.
173    /// * `kernel_offset`: Offset to be added to default kernel load address in guest memory.
174    /// * `kernel_image` - Input vmlinux image.
175    /// * `highmem_start_address`: Address where high memory starts.
176    ///
177    /// # Examples
178    ///
179    /// ```rust
180    /// # extern crate vm_memory;
181    /// # use std::io::Cursor;
182    /// # use linux_loader::loader::*;
183    /// # use vm_memory::{Address, GuestAddress};
184    /// # type GuestMemoryMmap = vm_memory::GuestMemoryMmap<()>;
185    /// let mem_size: usize = 0x1000000;
186    /// let himem_start = GuestAddress(0x0);
187    /// let kernel_addr = GuestAddress(0x200000);
188    /// let gm = GuestMemoryMmap::from_ranges(&[(GuestAddress(0x0), mem_size)]).unwrap();
189    /// let mut kernel_image = vec![];
190    /// kernel_image.extend_from_slice(include_bytes!("test_elf.bin"));
191    /// elf::Elf::load(
192    ///     &gm,
193    ///     Some(kernel_addr),
194    ///     &mut Cursor::new(&kernel_image),
195    ///     Some(himem_start),
196    /// )
197    /// .unwrap();
198    /// ```
199    ///
200    /// [`GuestMemory`]: https://docs.rs/vm-memory/latest/vm_memory/guest_memory/trait.GuestMemory.html
201    fn load<F, M: GuestMemory>(
202        guest_mem: &M,
203        kernel_offset: Option<GuestAddress>,
204        kernel_image: &mut F,
205        highmem_start_address: Option<GuestAddress>,
206    ) -> Result<KernelLoaderResult>
207    where
208        F: Read + ReadVolatile + Seek,
209    {
210        kernel_image.rewind().map_err(|_| Error::SeekElfStart)?;
211
212        let mut ehdr = elf::Elf64_Ehdr::default();
213        kernel_image
214            .read_exact(ehdr.as_mut_slice())
215            .map_err(|_| Error::ReadElfHeader)?;
216
217        // Sanity checks.
218        Self::validate_header(&ehdr)?;
219        if let Some(addr) = highmem_start_address {
220            if (ehdr.e_entry) < addr.raw_value() {
221                return Err(Error::InvalidEntryAddress.into());
222            }
223        }
224
225        let mut loader_result = KernelLoaderResult {
226            kernel_load: match kernel_offset {
227                Some(k_offset) => GuestAddress(
228                    k_offset
229                        .raw_value()
230                        .checked_add(ehdr.e_entry)
231                        .ok_or(Error::Overflow)?,
232                ),
233                None => GuestAddress(ehdr.e_entry),
234            },
235            ..Default::default()
236        };
237
238        kernel_image
239            .seek(SeekFrom::Start(ehdr.e_phoff))
240            .map_err(|_| Error::SeekProgramHeader)?;
241
242        let mut phdrs: Vec<elf::Elf64_Phdr> = vec![];
243        for _ in 0usize..ehdr.e_phnum as usize {
244            let mut phdr = elf::Elf64_Phdr::default();
245            kernel_image
246                .read_exact(phdr.as_mut_slice())
247                .map_err(|_| Error::ReadProgramHeader)?;
248            phdrs.push(phdr);
249        }
250
251        // Read in each section pointed to by the program headers.
252        for phdr in phdrs {
253            if phdr.p_type != elf::PT_LOAD || phdr.p_filesz == 0 {
254                if phdr.p_type == elf::PT_NOTE {
255                    // The PVH boot protocol currently requires that the kernel is loaded at
256                    // the default kernel load address in guest memory (specified at kernel
257                    // build time by the value of CONFIG_PHYSICAL_START). Therefore, only
258                    // attempt to use PVH if an offset from the default load address has not
259                    // been requested using the kernel_offset parameter.
260                    if let Some(_offset) = kernel_offset {
261                        loader_result.pvh_boot_cap = PvhBootCapability::PvhEntryIgnored;
262                    } else {
263                        // If kernel_offset is not requested, check if PVH entry point is present
264                        loader_result.pvh_boot_cap = parse_elf_note(&phdr, kernel_image)?;
265                    }
266                }
267                continue;
268            }
269
270            kernel_image
271                .seek(SeekFrom::Start(phdr.p_offset))
272                .map_err(|_| Error::SeekKernelStart)?;
273
274            // if the vmm does not specify where the kernel should be loaded, just
275            // load it to the physical address p_paddr for each segment.
276            let mem_offset = match kernel_offset {
277                Some(k_offset) => k_offset
278                    .checked_add(phdr.p_paddr)
279                    .ok_or(Error::InvalidProgramHeaderAddress)?,
280                None => GuestAddress(phdr.p_paddr),
281            };
282
283            guest_mem
284                .read_exact_volatile_from(mem_offset, kernel_image, phdr.p_filesz as usize)
285                .map_err(|_| Error::ReadKernelImage)?;
286
287            let kernel_end = mem_offset
288                .raw_value()
289                .checked_add(phdr.p_memsz as GuestUsize)
290                .ok_or(KernelLoaderError::MemoryOverflow)?;
291            loader_result.kernel_end = std::cmp::max(loader_result.kernel_end, kernel_end);
292        }
293
294        // elf image has no setup_header which is defined for bzImage
295        loader_result.setup_header = None;
296
297        Ok(loader_result)
298    }
299}
300
301// Size of string "Xen", including the terminating NULL.
302const PVH_NOTE_STR_SZ: usize = 4;
303
304/// Examines a supplied elf program header of type `PT_NOTE` to determine if it contains an entry
305/// of type `XEN_ELFNOTE_PHYS32_ENTRY` (0x12). Notes of this type encode a physical 32-bit entry
306/// point address into the kernel, which is used when launching guests in 32-bit (protected) mode
307/// with paging disabled, as described by the PVH boot protocol.
308/// Returns the encoded entry point address, or `None` if no `XEN_ELFNOTE_PHYS32_ENTRY` entries
309/// are found in the note header.
310fn parse_elf_note<F>(phdr: &elf::Elf64_Phdr, kernel_image: &mut F) -> Result<PvhBootCapability>
311where
312    F: Read + ReadVolatile + Seek,
313{
314    // Type of note header that encodes a 32-bit entry point address to boot a guest kernel using
315    // the PVH boot protocol.
316    const XEN_ELFNOTE_PHYS32_ENTRY: u32 = 18;
317
318    // Alignment of ELF notes, starting address of name field and descriptor field have a 4-byte
319    // alignment.
320    //
321    // See refer from:
322    //  - 'Note Section' of 'Executable and Linking Format (ELF) Specification' v1.2.
323    //  - Linux implementations, https://elixir.bootlin.com/linux/v6.1/source/include/linux/elfnote.h#L56
324    const ELFNOTE_ALIGN: u64 = 4;
325
326    // Seek to the beginning of the note segment.
327    kernel_image
328        .seek(SeekFrom::Start(phdr.p_offset))
329        .map_err(|_| Error::SeekNoteHeader)?;
330
331    // Now that the segment has been found, we must locate an ELF note with the correct type that
332    // encodes the PVH entry point if there is one.
333    let mut nhdr: elf::Elf64_Nhdr = Default::default();
334    let mut read_size: usize = 0;
335    let nhdr_sz = mem::size_of::<elf::Elf64_Nhdr>();
336
337    while read_size < phdr.p_filesz as usize {
338        kernel_image
339            .read_exact(nhdr.as_mut_slice())
340            .map_err(|_| Error::ReadNoteHeader)?;
341
342        // Check if the note header's name and type match the ones specified by the PVH ABI.
343        if nhdr.n_type == XEN_ELFNOTE_PHYS32_ENTRY && nhdr.n_namesz as usize == PVH_NOTE_STR_SZ {
344            let mut buf = [0u8; PVH_NOTE_STR_SZ];
345            kernel_image
346                .read_exact(&mut buf)
347                .map_err(|_| Error::ReadNoteHeader)?;
348            if buf == [b'X', b'e', b'n', b'\0'] {
349                break;
350            }
351        }
352
353        // Skip the note header plus the size of its fields (with alignment).
354        let namesz_aligned = align_up(u64::from(nhdr.n_namesz), ELFNOTE_ALIGN)?;
355        let descsz_aligned = align_up(u64::from(nhdr.n_descsz), ELFNOTE_ALIGN)?;
356
357        // `namesz` and `descsz` are both `u32`s. We need to also verify for overflow, to be sure
358        // we do not lose information.
359        if namesz_aligned > u32::MAX.into() || descsz_aligned > u32::MAX.into() {
360            return Err(Error::Overflow.into());
361        }
362
363        read_size = read_size
364            .checked_add(nhdr_sz) // Skip the ELF_NOTE known sized fields.
365            // Safe to truncate or change the type to `usize` (4 or 8 bytes depending on the
366            // architecture 32/64 bits) since we validated that we do not lose information.
367            .and_then(|read_size| read_size.checked_add(namesz_aligned as usize))
368            .and_then(|read_size| read_size.checked_add(descsz_aligned as usize))
369            .ok_or(Error::Overflow)?;
370
371        kernel_image
372            // The conversion here does not truncate, since `read_size` is of `usize` type, which
373            // can be at maximum 8 bytes long.
374            .seek(SeekFrom::Start(phdr.p_offset + read_size as u64))
375            .map_err(|_| Error::SeekNoteHeader)?;
376    }
377
378    if read_size >= phdr.p_filesz as usize {
379        // PVH ELF note not found, nothing else to do.
380        return Ok(PvhBootCapability::PvhEntryNotPresent);
381    }
382
383    // Otherwise the correct note type was found.
384    // The note header struct has already been read, so we can seek from the current position and
385    // just skip the name field contents.
386    kernel_image
387        .seek(SeekFrom::Current(
388            // Safe conversion since it is not losing data.
389            align_up(u64::from(nhdr.n_namesz), ELFNOTE_ALIGN)? as i64 - PVH_NOTE_STR_SZ as i64,
390        ))
391        .map_err(|_| Error::SeekNoteHeader)?;
392
393    // The PVH entry point is a 32-bit address, so the descriptor field must be capable of storing
394    // all such addresses.
395    if (nhdr.n_descsz as usize) < mem::size_of::<u32>() {
396        return Err(Error::InvalidPvhNote.into());
397    }
398
399    let mut pvh_addr_bytes = [0; mem::size_of::<u32>()];
400
401    // Read 32-bit address stored in the PVH note descriptor field.
402    kernel_image
403        .read_exact(&mut pvh_addr_bytes)
404        .map_err(|_| Error::ReadNoteHeader)?;
405
406    Ok(PvhBootCapability::PvhEntryPresent(GuestAddress(
407        u32::from_le_bytes(pvh_addr_bytes).into(),
408    )))
409}
410
411/// Align address upwards. Adapted from x86_64 crate:
412/// https://docs.rs/x86_64/latest/x86_64/addr/fn.align_up.html
413///
414/// Returns the smallest x with alignment `align` so that x >= addr if the alignment is a power of
415/// 2, or an error otherwise.
416fn align_up(addr: u64, align: u64) -> result::Result<u64, Error> {
417    if !align.is_power_of_two() {
418        return Err(Error::Align);
419    }
420    let align_mask = align - 1;
421    if addr & align_mask == 0 {
422        Ok(addr) // already aligned
423    } else {
424        // Safe to unchecked add because this can be at maximum `2^64` - 1, which is not
425        // overflowing.
426        Ok((addr | align_mask) + 1)
427    }
428}
429
430#[cfg(test)]
431mod tests {
432    use super::*;
433    use std::io::Cursor;
434    use vm_memory::{Address, GuestAddress};
435    type GuestMemoryMmap = vm_memory::GuestMemoryMmap<()>;
436
437    const MEM_SIZE: u64 = 0x100_0000;
438
439    fn create_guest_mem() -> GuestMemoryMmap {
440        GuestMemoryMmap::from_ranges(&[(GuestAddress(0x0), (MEM_SIZE as usize))]).unwrap()
441    }
442
443    fn make_elf_bin() -> Vec<u8> {
444        let mut v = Vec::new();
445        v.extend_from_slice(include_bytes!("test_elf.bin"));
446        v
447    }
448
449    fn make_elfnote() -> Vec<u8> {
450        include_bytes!("test_elfnote.bin").to_vec()
451    }
452
453    fn make_elfnote_8byte_align() -> Vec<u8> {
454        include_bytes!("test_elfnote_8byte_align.bin").to_vec()
455    }
456
457    fn make_dummy_elfnote() -> Vec<u8> {
458        include_bytes!("test_dummy_note.bin").to_vec()
459    }
460
461    fn make_invalid_pvh_note() -> Vec<u8> {
462        include_bytes!("test_invalid_pvh_note.bin").to_vec()
463    }
464
465    fn make_elfnote_bad_align() -> Vec<u8> {
466        include_bytes!("test_bad_align.bin").to_vec()
467    }
468
469    #[test]
470    fn test_load_elf() {
471        let gm = create_guest_mem();
472        let image = make_elf_bin();
473        let kernel_addr = GuestAddress(0x200000);
474        let mut highmem_start_address = GuestAddress(0x0);
475        let mut loader_result = Elf::load(
476            &gm,
477            Some(kernel_addr),
478            &mut Cursor::new(&image),
479            Some(highmem_start_address),
480        )
481        .unwrap();
482        assert_eq!(loader_result.kernel_load.raw_value(), 0x200400);
483
484        loader_result = Elf::load(&gm, Some(kernel_addr), &mut Cursor::new(&image), None).unwrap();
485        assert_eq!(loader_result.kernel_load.raw_value(), 0x200400);
486
487        loader_result = Elf::load(
488            &gm,
489            None,
490            &mut Cursor::new(&image),
491            Some(highmem_start_address),
492        )
493        .unwrap();
494        assert_eq!(loader_result.kernel_load.raw_value(), 0x400);
495
496        highmem_start_address = GuestAddress(0xa00000);
497        assert_eq!(
498            Some(KernelLoaderError::Elf(Error::InvalidEntryAddress)),
499            Elf::load(
500                &gm,
501                None,
502                &mut Cursor::new(&image),
503                Some(highmem_start_address)
504            )
505            .err()
506        );
507    }
508
509    #[test]
510    fn test_bad_magic_number() {
511        let gm = create_guest_mem();
512        let kernel_addr = GuestAddress(0x0);
513        let mut bad_image = make_elf_bin();
514        bad_image[0x1] = 0x33;
515        assert_eq!(
516            Some(KernelLoaderError::Elf(Error::InvalidElfMagicNumber)),
517            Elf::load(&gm, Some(kernel_addr), &mut Cursor::new(&bad_image), None).err()
518        );
519    }
520
521    #[test]
522    fn test_bad_endian() {
523        // Only little endian is supported.
524        let gm = create_guest_mem();
525        let kernel_addr = GuestAddress(0x0);
526        let mut bad_image = make_elf_bin();
527        bad_image[0x5] = 2;
528        assert_eq!(
529            Some(KernelLoaderError::Elf(Error::BigEndianElfOnLittle)),
530            Elf::load(&gm, Some(kernel_addr), &mut Cursor::new(&bad_image), None).err()
531        );
532    }
533
534    #[test]
535    fn test_bad_phoff() {
536        // Program header has to be past the end of the elf header.
537        let gm = create_guest_mem();
538        let kernel_addr = GuestAddress(0x0);
539        let mut bad_image = make_elf_bin();
540        bad_image[0x20] = 0x10;
541        assert_eq!(
542            Some(KernelLoaderError::Elf(Error::InvalidProgramHeaderOffset)),
543            Elf::load(&gm, Some(kernel_addr), &mut Cursor::new(&bad_image), None).err()
544        );
545    }
546
547    #[test]
548    fn test_load_pvh() {
549        let gm = create_guest_mem();
550        let pvhnote_image = make_elfnote();
551        let loader_result = Elf::load(&gm, None, &mut Cursor::new(&pvhnote_image), None).unwrap();
552        assert_eq!(
553            loader_result.pvh_boot_cap,
554            PvhBootCapability::PvhEntryPresent(GuestAddress(0x1e1fe1f))
555        );
556
557        // Verify that PVH is ignored when kernel_start is requested
558        let loader_result = Elf::load(
559            &gm,
560            Some(GuestAddress(0x0020_0000)),
561            &mut Cursor::new(&pvhnote_image),
562            None,
563        )
564        .unwrap();
565        assert_eq!(
566            loader_result.pvh_boot_cap,
567            PvhBootCapability::PvhEntryIgnored
568        );
569    }
570
571    #[test]
572    fn test_dummy_elfnote() {
573        let gm = create_guest_mem();
574        let dummynote_image = make_dummy_elfnote();
575        let loader_result = Elf::load(&gm, None, &mut Cursor::new(&dummynote_image), None).unwrap();
576        assert_eq!(
577            loader_result.pvh_boot_cap,
578            PvhBootCapability::PvhEntryNotPresent
579        );
580    }
581
582    #[test]
583    fn test_bad_elfnote() {
584        let gm = create_guest_mem();
585        let badnote_image = make_invalid_pvh_note();
586        assert_eq!(
587            Some(KernelLoaderError::Elf(Error::InvalidPvhNote)),
588            Elf::load(&gm, None, &mut Cursor::new(&badnote_image), None).err()
589        );
590    }
591
592    #[test]
593    fn test_load_pvh_with_align() {
594        // Alignment of ELF notes is always const value (4-bytes), ELF notes parse should not get Align
595        // error.
596        {
597            let gm =
598                GuestMemoryMmap::from_ranges(&[(GuestAddress(0x0), (0x1000_0000_usize))]).unwrap();
599            let bad_align_image = make_elfnote_bad_align();
600            assert_ne!(
601                Some(KernelLoaderError::Elf(Error::Align)),
602                Elf::load(&gm, None, &mut Cursor::new(&bad_align_image), None).err()
603            );
604        }
605
606        // Alignment of ELF notes is always const value (4-byte), ELF notes parse should always
607        // success even there is incorrect p_align in phdr.
608        {
609            let gm = create_guest_mem();
610            let pvhnote_image = make_elfnote_8byte_align();
611            let loader_result =
612                Elf::load(&gm, None, &mut Cursor::new(&pvhnote_image), None).unwrap();
613            assert_eq!(
614                loader_result.pvh_boot_cap,
615                PvhBootCapability::PvhEntryPresent(GuestAddress(0x1e1fe1f))
616            );
617        }
618    }
619
620    #[test]
621    fn test_overflow_loadaddr() {
622        let gm = create_guest_mem();
623        let image = make_elf_bin();
624        assert_eq!(
625            Some(KernelLoaderError::Elf(Error::Overflow)),
626            Elf::load(
627                &gm,
628                Some(GuestAddress(u64::MAX)),
629                &mut Cursor::new(&image),
630                None
631            )
632            .err()
633        );
634    }
635}