1 // Copyright 2017 The ChromiumOS Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 //! Linux kernel ELF file loader.
6
7 use std::mem;
8
9 use base::FileReadWriteAtVolatile;
10 use base::VolatileSlice;
11 use remain::sorted;
12 use resources::AddressRange;
13 use thiserror::Error;
14 use vm_memory::GuestAddress;
15 use vm_memory::GuestMemory;
16 use zerocopy::AsBytes;
17 use zerocopy::FromBytes;
18
19 mod multiboot;
20
21 #[allow(dead_code)]
22 #[allow(non_camel_case_types)]
23 #[allow(non_snake_case)]
24 #[allow(non_upper_case_globals)]
25 #[allow(clippy::all)]
26 mod elf;
27
28 mod arm64;
29
30 pub use arm64::load_arm64_kernel;
31 pub use arm64::load_arm64_kernel_lz4;
32 pub use multiboot::load_multiboot;
33 pub use multiboot::multiboot_header_from_file;
34
35 #[sorted]
36 #[derive(Error, Debug, PartialEq, Eq)]
37 pub enum Error {
38 #[error("trying to load big-endian binary on little-endian machine")]
39 BigEndianOnLittle,
40 #[error("invalid elf class")]
41 InvalidElfClass,
42 #[error("invalid elf version")]
43 InvalidElfVersion,
44 #[error("invalid entry point")]
45 InvalidEntryPoint,
46 #[error("invalid flags")]
47 InvalidFlags,
48 #[error("invalid kernel offset")]
49 InvalidKernelOffset,
50 #[error("invalid kernel size")]
51 InvalidKernelSize,
52 #[error("invalid magic number")]
53 InvalidMagicNumber,
54 #[error("invalid Program Header Address")]
55 InvalidProgramHeaderAddress,
56 #[error("invalid Program Header memory size")]
57 InvalidProgramHeaderMemSize,
58 #[error("invalid program header offset")]
59 InvalidProgramHeaderOffset,
60 #[error("invalid program header size")]
61 InvalidProgramHeaderSize,
62 #[error("no loadable program headers found")]
63 NoLoadableProgramHeaders,
64 #[error("program header address out of allowed address range")]
65 ProgramHeaderAddressOutOfRange,
66 #[error("unable to read header")]
67 ReadHeader,
68 #[error("unable to read kernel image")]
69 ReadKernelImage,
70 #[error("unable to read program header")]
71 ReadProgramHeader,
72 #[error("unable to seek to kernel end")]
73 SeekKernelEnd,
74 #[error("unable to seek to kernel start")]
75 SeekKernelStart,
76 #[error("unable to seek to program header")]
77 SeekProgramHeader,
78 }
79 pub type Result<T> = std::result::Result<T, Error>;
80
81 #[derive(Debug, Copy, Clone, PartialEq, Eq)]
82 /// Information about a kernel loaded with the [`load_elf`] function.
83 pub struct LoadedKernel {
84 /// Address range containg the bounds of the loaded program headers.
85 /// `address_range.start` is the start of the lowest loaded program header.
86 /// `address_range.end` is the end of the highest loaded program header.
87 pub address_range: AddressRange,
88
89 /// Size of the kernel image in bytes.
90 pub size: u64,
91
92 /// Entry point address of the kernel.
93 pub entry: GuestAddress,
94 }
95
96 /// Loads a kernel from a 32-bit ELF image into memory.
97 ///
98 /// The ELF file will be loaded at the physical address specified by the `p_paddr` fields of its
99 /// program headers.
100 ///
101 /// # Arguments
102 ///
103 /// * `guest_mem` - The guest memory region the kernel is written to.
104 /// * `kernel_start` - The minimum guest address to allow when loading program headers.
105 /// * `kernel_image` - Input vmlinux image.
106 /// * `phys_offset` - An offset in bytes to add to each physical address (`p_paddr`).
load_elf32<F>( guest_mem: &GuestMemory, kernel_start: GuestAddress, kernel_image: &mut F, phys_offset: u64, ) -> Result<LoadedKernel> where F: FileReadWriteAtVolatile,107 pub fn load_elf32<F>(
108 guest_mem: &GuestMemory,
109 kernel_start: GuestAddress,
110 kernel_image: &mut F,
111 phys_offset: u64,
112 ) -> Result<LoadedKernel>
113 where
114 F: FileReadWriteAtVolatile,
115 {
116 load_elf_for_class(
117 guest_mem,
118 kernel_start,
119 kernel_image,
120 phys_offset,
121 Some(elf::ELFCLASS32),
122 )
123 }
124
125 /// Loads a kernel from a 64-bit ELF image into memory.
126 ///
127 /// The ELF file will be loaded at the physical address specified by the `p_paddr` fields of its
128 /// program headers.
129 ///
130 /// # Arguments
131 ///
132 /// * `guest_mem` - The guest memory region the kernel is written to.
133 /// * `kernel_start` - The minimum guest address to allow when loading program headers.
134 /// * `kernel_image` - Input vmlinux image.
135 /// * `phys_offset` - An offset in bytes to add to each physical address (`p_paddr`).
load_elf64<F>( guest_mem: &GuestMemory, kernel_start: GuestAddress, kernel_image: &mut F, phys_offset: u64, ) -> Result<LoadedKernel> where F: FileReadWriteAtVolatile,136 pub fn load_elf64<F>(
137 guest_mem: &GuestMemory,
138 kernel_start: GuestAddress,
139 kernel_image: &mut F,
140 phys_offset: u64,
141 ) -> Result<LoadedKernel>
142 where
143 F: FileReadWriteAtVolatile,
144 {
145 load_elf_for_class(
146 guest_mem,
147 kernel_start,
148 kernel_image,
149 phys_offset,
150 Some(elf::ELFCLASS64),
151 )
152 }
153
154 /// Loads a kernel from a 32-bit or 64-bit ELF image into memory.
155 ///
156 /// The ELF file will be loaded at the physical address specified by the `p_paddr` fields of its
157 /// program headers.
158 ///
159 /// # Arguments
160 ///
161 /// * `guest_mem` - The guest memory region the kernel is written to.
162 /// * `kernel_start` - The minimum guest address to allow when loading program headers.
163 /// * `kernel_image` - Input vmlinux image.
164 /// * `phys_offset` - An offset in bytes to add to each physical address (`p_paddr`).
load_elf<F>( guest_mem: &GuestMemory, kernel_start: GuestAddress, kernel_image: &mut F, phys_offset: u64, ) -> Result<LoadedKernel> where F: FileReadWriteAtVolatile,165 pub fn load_elf<F>(
166 guest_mem: &GuestMemory,
167 kernel_start: GuestAddress,
168 kernel_image: &mut F,
169 phys_offset: u64,
170 ) -> Result<LoadedKernel>
171 where
172 F: FileReadWriteAtVolatile,
173 {
174 load_elf_for_class(guest_mem, kernel_start, kernel_image, phys_offset, None)
175 }
176
load_elf_for_class<F>( guest_mem: &GuestMemory, kernel_start: GuestAddress, kernel_image: &mut F, phys_offset: u64, ei_class: Option<u32>, ) -> Result<LoadedKernel> where F: FileReadWriteAtVolatile,177 fn load_elf_for_class<F>(
178 guest_mem: &GuestMemory,
179 kernel_start: GuestAddress,
180 kernel_image: &mut F,
181 phys_offset: u64,
182 ei_class: Option<u32>,
183 ) -> Result<LoadedKernel>
184 where
185 F: FileReadWriteAtVolatile,
186 {
187 let elf = read_elf(kernel_image, ei_class)?;
188 let mut start = None;
189 let mut end = 0;
190
191 // Read in each section pointed to by the program headers.
192 for phdr in &elf.program_headers {
193 if phdr.p_type != elf::PT_LOAD {
194 continue;
195 }
196
197 let paddr = phdr
198 .p_paddr
199 .checked_add(phys_offset)
200 .ok_or(Error::ProgramHeaderAddressOutOfRange)?;
201
202 if paddr < kernel_start.offset() {
203 return Err(Error::ProgramHeaderAddressOutOfRange);
204 }
205
206 if start.is_none() {
207 start = Some(paddr);
208 }
209
210 end = paddr
211 .checked_add(phdr.p_memsz)
212 .ok_or(Error::InvalidProgramHeaderMemSize)?;
213
214 if phdr.p_filesz == 0 {
215 continue;
216 }
217
218 let guest_slice = guest_mem
219 .get_slice_at_addr(GuestAddress(paddr), phdr.p_filesz as usize)
220 .map_err(|_| Error::ReadKernelImage)?;
221 kernel_image
222 .read_exact_at_volatile(guest_slice, phdr.p_offset)
223 .map_err(|_| Error::ReadKernelImage)?;
224 }
225
226 // We should have found at least one PT_LOAD program header. If not, `start` will not be set.
227 let start = start.ok_or(Error::NoLoadableProgramHeaders)?;
228
229 let size = end
230 .checked_sub(start)
231 .ok_or(Error::InvalidProgramHeaderSize)?;
232
233 let address_range = AddressRange { start, end };
234
235 // The entry point address must fall within one of the loaded sections.
236 // We approximate this by checking whether it within the bounds of the first and last sections.
237 let entry = elf
238 .file_header
239 .e_entry
240 .checked_add(phys_offset)
241 .ok_or(Error::InvalidEntryPoint)?;
242 if !address_range.contains(entry) {
243 return Err(Error::InvalidEntryPoint);
244 }
245
246 Ok(LoadedKernel {
247 address_range,
248 size,
249 entry: GuestAddress(entry),
250 })
251 }
252
253 struct Elf64 {
254 file_header: elf::Elf64_Ehdr,
255 program_headers: Vec<elf::Elf64_Phdr>,
256 }
257
258 /// Reads the headers of an ELF32 or ELF64 object file. Returns ELF file and program headers,
259 /// converted to ELF64 format. If `required_ei_class` is Some and the file's ELF ei_class doesn't
260 /// match, an Err is returned.
read_elf<F>(file: &mut F, required_ei_class: Option<u32>) -> Result<Elf64> where F: FileReadWriteAtVolatile,261 fn read_elf<F>(file: &mut F, required_ei_class: Option<u32>) -> Result<Elf64>
262 where
263 F: FileReadWriteAtVolatile,
264 {
265 // Read the ELF identification (e_ident) block.
266 let mut ident = [0u8; 16];
267 file.read_exact_at_volatile(VolatileSlice::new(&mut ident), 0)
268 .map_err(|_| Error::ReadHeader)?;
269
270 // e_ident checks
271 if ident[elf::EI_MAG0 as usize] != elf::ELFMAG0 as u8
272 || ident[elf::EI_MAG1 as usize] != elf::ELFMAG1
273 || ident[elf::EI_MAG2 as usize] != elf::ELFMAG2
274 || ident[elf::EI_MAG3 as usize] != elf::ELFMAG3
275 {
276 return Err(Error::InvalidMagicNumber);
277 }
278 if ident[elf::EI_DATA as usize] != elf::ELFDATA2LSB as u8 {
279 return Err(Error::BigEndianOnLittle);
280 }
281 if ident[elf::EI_VERSION as usize] != elf::EV_CURRENT as u8 {
282 return Err(Error::InvalidElfVersion);
283 }
284
285 let ei_class = ident[elf::EI_CLASS as usize] as u32;
286 if let Some(required_ei_class) = required_ei_class {
287 if ei_class != required_ei_class {
288 return Err(Error::InvalidElfClass);
289 }
290 }
291 match ei_class {
292 elf::ELFCLASS32 => read_elf_by_type::<_, elf::Elf32_Ehdr, elf::Elf32_Phdr>(file),
293 elf::ELFCLASS64 => read_elf_by_type::<_, elf::Elf64_Ehdr, elf::Elf64_Phdr>(file),
294 _ => Err(Error::InvalidElfClass),
295 }
296 }
297
298 /// Reads the headers of an ELF32 or ELF64 object file. Returns ELF file and program headers,
299 /// converted to ELF64 format. `FileHeader` and `ProgramHeader` are the ELF32 or ELF64 ehdr/phdr
300 /// types to read from the file. Caller should check that `file` is a valid ELF file before calling
301 /// this function.
read_elf_by_type<F, FileHeader, ProgramHeader>(file: &mut F) -> Result<Elf64> where F: FileReadWriteAtVolatile, FileHeader: AsBytes + FromBytes + Default + Into<elf::Elf64_Ehdr>, ProgramHeader: AsBytes + FromBytes + Clone + Default + Into<elf::Elf64_Phdr>,302 fn read_elf_by_type<F, FileHeader, ProgramHeader>(file: &mut F) -> Result<Elf64>
303 where
304 F: FileReadWriteAtVolatile,
305 FileHeader: AsBytes + FromBytes + Default + Into<elf::Elf64_Ehdr>,
306 ProgramHeader: AsBytes + FromBytes + Clone + Default + Into<elf::Elf64_Phdr>,
307 {
308 let mut ehdr = FileHeader::new_zeroed();
309 file.read_exact_at_volatile(VolatileSlice::new(ehdr.as_bytes_mut()), 0)
310 .map_err(|_| Error::ReadHeader)?;
311 let ehdr: elf::Elf64_Ehdr = ehdr.into();
312
313 if ehdr.e_phentsize as usize != mem::size_of::<ProgramHeader>() {
314 return Err(Error::InvalidProgramHeaderSize);
315 }
316 if (ehdr.e_phoff as usize) < mem::size_of::<FileHeader>() {
317 // If the program header is backwards, bail.
318 return Err(Error::InvalidProgramHeaderOffset);
319 }
320
321 let num_phdrs = ehdr.e_phnum as usize;
322 let mut phdrs = vec![ProgramHeader::default(); num_phdrs];
323 file.read_exact_at_volatile(VolatileSlice::new(phdrs.as_bytes_mut()), ehdr.e_phoff)
324 .map_err(|_| Error::ReadProgramHeader)?;
325
326 Ok(Elf64 {
327 file_header: ehdr,
328 program_headers: phdrs.into_iter().map(|ph| ph.into()).collect(),
329 })
330 }
331
332 impl From<elf::Elf32_Ehdr> for elf::Elf64_Ehdr {
from(ehdr32: elf::Elf32_Ehdr) -> Self333 fn from(ehdr32: elf::Elf32_Ehdr) -> Self {
334 elf::Elf64_Ehdr {
335 e_ident: ehdr32.e_ident,
336 e_type: ehdr32.e_type as elf::Elf64_Half,
337 e_machine: ehdr32.e_machine as elf::Elf64_Half,
338 e_version: ehdr32.e_version as elf::Elf64_Word,
339 e_entry: ehdr32.e_entry as elf::Elf64_Addr,
340 e_phoff: ehdr32.e_phoff as elf::Elf64_Off,
341 e_shoff: ehdr32.e_shoff as elf::Elf64_Off,
342 e_flags: ehdr32.e_flags as elf::Elf64_Word,
343 e_ehsize: ehdr32.e_ehsize as elf::Elf64_Half,
344 e_phentsize: ehdr32.e_phentsize as elf::Elf64_Half,
345 e_phnum: ehdr32.e_phnum as elf::Elf64_Half,
346 e_shentsize: ehdr32.e_shentsize as elf::Elf64_Half,
347 e_shnum: ehdr32.e_shnum as elf::Elf64_Half,
348 e_shstrndx: ehdr32.e_shstrndx as elf::Elf64_Half,
349 }
350 }
351 }
352
353 impl From<elf::Elf32_Phdr> for elf::Elf64_Phdr {
from(phdr32: elf::Elf32_Phdr) -> Self354 fn from(phdr32: elf::Elf32_Phdr) -> Self {
355 elf::Elf64_Phdr {
356 p_type: phdr32.p_type as elf::Elf64_Word,
357 p_flags: phdr32.p_flags as elf::Elf64_Word,
358 p_offset: phdr32.p_offset as elf::Elf64_Off,
359 p_vaddr: phdr32.p_vaddr as elf::Elf64_Addr,
360 p_paddr: phdr32.p_paddr as elf::Elf64_Addr,
361 p_filesz: phdr32.p_filesz as elf::Elf64_Xword,
362 p_memsz: phdr32.p_memsz as elf::Elf64_Xword,
363 p_align: phdr32.p_align as elf::Elf64_Xword,
364 }
365 }
366 }
367
368 #[cfg(test)]
369 mod test {
370 use std::fs::File;
371 use std::io::Seek;
372 use std::io::SeekFrom;
373 use std::io::Write;
374
375 use tempfile::tempfile;
376 use vm_memory::GuestAddress;
377 use vm_memory::GuestMemory;
378
379 use super::*;
380
381 const MEM_SIZE: u64 = 0x40_0000;
382
create_guest_mem() -> GuestMemory383 fn create_guest_mem() -> GuestMemory {
384 GuestMemory::new(&[(GuestAddress(0x0), MEM_SIZE)]).unwrap()
385 }
386
387 // Elf32 image that prints hello world on x86.
make_elf32_bin() -> File388 fn make_elf32_bin() -> File {
389 // test_elf32.bin built on Linux with gcc -m32 -static-pie
390 let bytes = include_bytes!("test_elf32.bin");
391 make_elf_bin(bytes)
392 }
393
394 // Elf64 image that prints hello world on x86_64.
make_elf64_bin() -> File395 fn make_elf64_bin() -> File {
396 let bytes = include_bytes!("test_elf64.bin");
397 make_elf_bin(bytes)
398 }
399
make_elf_bin(elf_bytes: &[u8]) -> File400 fn make_elf_bin(elf_bytes: &[u8]) -> File {
401 let mut file = tempfile().expect("failed to create tempfile");
402 file.write_all(elf_bytes)
403 .expect("failed to write elf to shared memory");
404 file
405 }
406
mutate_elf_bin(mut f: &File, offset: u64, val: u8)407 fn mutate_elf_bin(mut f: &File, offset: u64, val: u8) {
408 f.seek(SeekFrom::Start(offset))
409 .expect("failed to seek file");
410 f.write_all(&[val])
411 .expect("failed to write mutated value to file");
412 }
413
414 #[test]
load_elf32()415 fn load_elf32() {
416 let gm = create_guest_mem();
417 let kernel_addr = GuestAddress(0x0);
418 let mut image = make_elf32_bin();
419 let kernel = load_elf(&gm, kernel_addr, &mut image, 0).unwrap();
420 assert_eq!(kernel.address_range.start, 0);
421 assert_eq!(kernel.address_range.end, 0xa_2038);
422 assert_eq!(kernel.size, 0xa_2038);
423 assert_eq!(kernel.entry, GuestAddress(0x3dc0));
424 }
425
426 #[test]
load_elf64()427 fn load_elf64() {
428 let gm = create_guest_mem();
429 let kernel_addr = GuestAddress(0x0);
430 let mut image = make_elf64_bin();
431 let kernel = load_elf(&gm, kernel_addr, &mut image, 0).expect("failed to load ELF");
432 assert_eq!(kernel.address_range.start, 0x20_0000);
433 assert_eq!(kernel.address_range.end, 0x20_0035);
434 assert_eq!(kernel.size, 0x35);
435 assert_eq!(kernel.entry, GuestAddress(0x20_000e));
436 }
437
438 #[test]
bad_magic()439 fn bad_magic() {
440 let gm = create_guest_mem();
441 let kernel_addr = GuestAddress(0x0);
442 let mut bad_image = make_elf64_bin();
443 mutate_elf_bin(&bad_image, 0x1, 0x33);
444 assert_eq!(
445 Err(Error::InvalidMagicNumber),
446 load_elf(&gm, kernel_addr, &mut bad_image, 0)
447 );
448 }
449
450 #[test]
bad_endian()451 fn bad_endian() {
452 // Only little endian is supported
453 let gm = create_guest_mem();
454 let kernel_addr = GuestAddress(0x20_0000);
455 let mut bad_image = make_elf64_bin();
456 mutate_elf_bin(&bad_image, 0x5, 2);
457 assert_eq!(
458 Err(Error::BigEndianOnLittle),
459 load_elf(&gm, kernel_addr, &mut bad_image, 0)
460 );
461 }
462
463 #[test]
bad_phoff()464 fn bad_phoff() {
465 // program header has to be past the end of the elf header
466 let gm = create_guest_mem();
467 let kernel_addr = GuestAddress(0x0);
468 let mut bad_image = make_elf64_bin();
469 mutate_elf_bin(&bad_image, 0x20, 0x10);
470 assert_eq!(
471 Err(Error::InvalidProgramHeaderOffset),
472 load_elf(&gm, kernel_addr, &mut bad_image, 0)
473 );
474 }
475
476 #[test]
paddr_below_start()477 fn paddr_below_start() {
478 let gm = create_guest_mem();
479 // test_elf.bin loads a phdr at 0x20_0000, so this will fail due to an out-of-bounds address
480 let kernel_addr = GuestAddress(0x30_0000);
481 let mut image = make_elf64_bin();
482 let res = load_elf(&gm, kernel_addr, &mut image, 0);
483 assert_eq!(res, Err(Error::ProgramHeaderAddressOutOfRange));
484 }
485 }
486