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::FromBytes;
17 use zerocopy::IntoBytes;
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 =
234 AddressRange::from_start_and_size(start, size).ok_or(Error::InvalidProgramHeaderSize)?;
235
236 // The entry point address must fall within one of the loaded sections.
237 // We approximate this by checking whether it within the bounds of the first and last sections.
238 let entry = elf
239 .file_header
240 .e_entry
241 .checked_add(phys_offset)
242 .ok_or(Error::InvalidEntryPoint)?;
243 if !address_range.contains(entry) {
244 return Err(Error::InvalidEntryPoint);
245 }
246
247 Ok(LoadedKernel {
248 address_range,
249 size,
250 entry: GuestAddress(entry),
251 })
252 }
253
254 struct Elf64 {
255 file_header: elf::Elf64_Ehdr,
256 program_headers: Vec<elf::Elf64_Phdr>,
257 }
258
259 /// Reads the headers of an ELF32 or ELF64 object file. Returns ELF file and program headers,
260 /// converted to ELF64 format. If `required_ei_class` is Some and the file's ELF ei_class doesn't
261 /// match, an Err is returned.
read_elf<F>(file: &mut F, required_ei_class: Option<u32>) -> Result<Elf64> where F: FileReadWriteAtVolatile,262 fn read_elf<F>(file: &mut F, required_ei_class: Option<u32>) -> Result<Elf64>
263 where
264 F: FileReadWriteAtVolatile,
265 {
266 // Read the ELF identification (e_ident) block.
267 let mut ident = [0u8; 16];
268 file.read_exact_at_volatile(VolatileSlice::new(&mut ident), 0)
269 .map_err(|_| Error::ReadHeader)?;
270
271 // e_ident checks
272 if ident[elf::EI_MAG0 as usize] != elf::ELFMAG0 as u8
273 || ident[elf::EI_MAG1 as usize] != elf::ELFMAG1
274 || ident[elf::EI_MAG2 as usize] != elf::ELFMAG2
275 || ident[elf::EI_MAG3 as usize] != elf::ELFMAG3
276 {
277 return Err(Error::InvalidMagicNumber);
278 }
279 if ident[elf::EI_DATA as usize] != elf::ELFDATA2LSB as u8 {
280 return Err(Error::BigEndianOnLittle);
281 }
282 if ident[elf::EI_VERSION as usize] != elf::EV_CURRENT as u8 {
283 return Err(Error::InvalidElfVersion);
284 }
285
286 let ei_class = ident[elf::EI_CLASS as usize] as u32;
287 if let Some(required_ei_class) = required_ei_class {
288 if ei_class != required_ei_class {
289 return Err(Error::InvalidElfClass);
290 }
291 }
292 match ei_class {
293 elf::ELFCLASS32 => read_elf_by_type::<_, elf::Elf32_Ehdr, elf::Elf32_Phdr>(file),
294 elf::ELFCLASS64 => read_elf_by_type::<_, elf::Elf64_Ehdr, elf::Elf64_Phdr>(file),
295 _ => Err(Error::InvalidElfClass),
296 }
297 }
298
299 /// Reads the headers of an ELF32 or ELF64 object file. Returns ELF file and program headers,
300 /// converted to ELF64 format. `FileHeader` and `ProgramHeader` are the ELF32 or ELF64 ehdr/phdr
301 /// types to read from the file. Caller should check that `file` is a valid ELF file before calling
302 /// this function.
read_elf_by_type<F, FileHeader, ProgramHeader>(file: &mut F) -> Result<Elf64> where F: FileReadWriteAtVolatile, FileHeader: IntoBytes + FromBytes + Default + Into<elf::Elf64_Ehdr>, ProgramHeader: IntoBytes + FromBytes + Clone + Default + Into<elf::Elf64_Phdr>,303 fn read_elf_by_type<F, FileHeader, ProgramHeader>(file: &mut F) -> Result<Elf64>
304 where
305 F: FileReadWriteAtVolatile,
306 FileHeader: IntoBytes + FromBytes + Default + Into<elf::Elf64_Ehdr>,
307 ProgramHeader: IntoBytes + FromBytes + Clone + Default + Into<elf::Elf64_Phdr>,
308 {
309 let mut ehdr = FileHeader::new_zeroed();
310 file.read_exact_at_volatile(VolatileSlice::new(ehdr.as_mut_bytes()), 0)
311 .map_err(|_| Error::ReadHeader)?;
312 let ehdr: elf::Elf64_Ehdr = ehdr.into();
313
314 if ehdr.e_phentsize as usize != mem::size_of::<ProgramHeader>() {
315 return Err(Error::InvalidProgramHeaderSize);
316 }
317 if (ehdr.e_phoff as usize) < mem::size_of::<FileHeader>() {
318 // If the program header is backwards, bail.
319 return Err(Error::InvalidProgramHeaderOffset);
320 }
321
322 let num_phdrs = ehdr.e_phnum as usize;
323 let mut phdrs = vec![ProgramHeader::default(); num_phdrs];
324 file.read_exact_at_volatile(VolatileSlice::new(phdrs.as_mut_bytes()), ehdr.e_phoff)
325 .map_err(|_| Error::ReadProgramHeader)?;
326
327 Ok(Elf64 {
328 file_header: ehdr,
329 program_headers: phdrs.into_iter().map(|ph| ph.into()).collect(),
330 })
331 }
332
333 impl From<elf::Elf32_Ehdr> for elf::Elf64_Ehdr {
from(ehdr32: elf::Elf32_Ehdr) -> Self334 fn from(ehdr32: elf::Elf32_Ehdr) -> Self {
335 elf::Elf64_Ehdr {
336 e_ident: ehdr32.e_ident,
337 e_type: ehdr32.e_type as elf::Elf64_Half,
338 e_machine: ehdr32.e_machine as elf::Elf64_Half,
339 e_version: ehdr32.e_version as elf::Elf64_Word,
340 e_entry: ehdr32.e_entry as elf::Elf64_Addr,
341 e_phoff: ehdr32.e_phoff as elf::Elf64_Off,
342 e_shoff: ehdr32.e_shoff as elf::Elf64_Off,
343 e_flags: ehdr32.e_flags as elf::Elf64_Word,
344 e_ehsize: ehdr32.e_ehsize as elf::Elf64_Half,
345 e_phentsize: ehdr32.e_phentsize as elf::Elf64_Half,
346 e_phnum: ehdr32.e_phnum as elf::Elf64_Half,
347 e_shentsize: ehdr32.e_shentsize as elf::Elf64_Half,
348 e_shnum: ehdr32.e_shnum as elf::Elf64_Half,
349 e_shstrndx: ehdr32.e_shstrndx as elf::Elf64_Half,
350 }
351 }
352 }
353
354 impl From<elf::Elf32_Phdr> for elf::Elf64_Phdr {
from(phdr32: elf::Elf32_Phdr) -> Self355 fn from(phdr32: elf::Elf32_Phdr) -> Self {
356 elf::Elf64_Phdr {
357 p_type: phdr32.p_type as elf::Elf64_Word,
358 p_flags: phdr32.p_flags as elf::Elf64_Word,
359 p_offset: phdr32.p_offset as elf::Elf64_Off,
360 p_vaddr: phdr32.p_vaddr as elf::Elf64_Addr,
361 p_paddr: phdr32.p_paddr as elf::Elf64_Addr,
362 p_filesz: phdr32.p_filesz as elf::Elf64_Xword,
363 p_memsz: phdr32.p_memsz as elf::Elf64_Xword,
364 p_align: phdr32.p_align as elf::Elf64_Xword,
365 }
366 }
367 }
368
369 #[cfg(test)]
370 mod test {
371 use std::fs::File;
372 use std::io::Seek;
373 use std::io::SeekFrom;
374 use std::io::Write;
375
376 use tempfile::tempfile;
377 use vm_memory::GuestAddress;
378 use vm_memory::GuestMemory;
379
380 use super::*;
381
382 const MEM_SIZE: u64 = 0x40_0000;
383
create_guest_mem() -> GuestMemory384 fn create_guest_mem() -> GuestMemory {
385 GuestMemory::new(&[(GuestAddress(0x0), MEM_SIZE)]).unwrap()
386 }
387
388 // Elf32 image that prints hello world on x86.
make_elf32_bin() -> File389 fn make_elf32_bin() -> File {
390 // test_elf32.bin built on Linux with gcc -m32 -static-pie
391 let bytes = include_bytes!("test_elf32.bin");
392 make_elf_bin(bytes)
393 }
394
395 // Elf64 image that prints hello world on x86_64.
make_elf64_bin() -> File396 fn make_elf64_bin() -> File {
397 let bytes = include_bytes!("test_elf64.bin");
398 make_elf_bin(bytes)
399 }
400
make_elf_bin(elf_bytes: &[u8]) -> File401 fn make_elf_bin(elf_bytes: &[u8]) -> File {
402 let mut file = tempfile().expect("failed to create tempfile");
403 file.write_all(elf_bytes)
404 .expect("failed to write elf to shared memory");
405 file
406 }
407
mutate_elf_bin(mut f: &File, offset: u64, val: u8)408 fn mutate_elf_bin(mut f: &File, offset: u64, val: u8) {
409 f.seek(SeekFrom::Start(offset))
410 .expect("failed to seek file");
411 f.write_all(&[val])
412 .expect("failed to write mutated value to file");
413 }
414
415 #[test]
load_elf32()416 fn load_elf32() {
417 let gm = create_guest_mem();
418 let kernel_addr = GuestAddress(0x0);
419 let mut image = make_elf32_bin();
420 let kernel = load_elf(&gm, kernel_addr, &mut image, 0).unwrap();
421 assert_eq!(kernel.address_range.start, 0);
422 assert_eq!(kernel.address_range.end, 0xa_2037);
423 assert_eq!(kernel.size, 0xa_2038);
424 assert_eq!(kernel.entry, GuestAddress(0x3dc0));
425 }
426
427 #[test]
load_elf64()428 fn load_elf64() {
429 let gm = create_guest_mem();
430 let kernel_addr = GuestAddress(0x0);
431 let mut image = make_elf64_bin();
432 let kernel = load_elf(&gm, kernel_addr, &mut image, 0).expect("failed to load ELF");
433 assert_eq!(kernel.address_range.start, 0x20_0000);
434 assert_eq!(kernel.address_range.end, 0x20_0034);
435 assert_eq!(kernel.size, 0x35);
436 assert_eq!(kernel.entry, GuestAddress(0x20_000e));
437 }
438
439 #[test]
bad_magic()440 fn bad_magic() {
441 let gm = create_guest_mem();
442 let kernel_addr = GuestAddress(0x0);
443 let mut bad_image = make_elf64_bin();
444 mutate_elf_bin(&bad_image, 0x1, 0x33);
445 assert_eq!(
446 Err(Error::InvalidMagicNumber),
447 load_elf(&gm, kernel_addr, &mut bad_image, 0)
448 );
449 }
450
451 #[test]
bad_endian()452 fn bad_endian() {
453 // Only little endian is supported
454 let gm = create_guest_mem();
455 let kernel_addr = GuestAddress(0x20_0000);
456 let mut bad_image = make_elf64_bin();
457 mutate_elf_bin(&bad_image, 0x5, 2);
458 assert_eq!(
459 Err(Error::BigEndianOnLittle),
460 load_elf(&gm, kernel_addr, &mut bad_image, 0)
461 );
462 }
463
464 #[test]
bad_phoff()465 fn bad_phoff() {
466 // program header has to be past the end of the elf header
467 let gm = create_guest_mem();
468 let kernel_addr = GuestAddress(0x0);
469 let mut bad_image = make_elf64_bin();
470 mutate_elf_bin(&bad_image, 0x20, 0x10);
471 assert_eq!(
472 Err(Error::InvalidProgramHeaderOffset),
473 load_elf(&gm, kernel_addr, &mut bad_image, 0)
474 );
475 }
476
477 #[test]
paddr_below_start()478 fn paddr_below_start() {
479 let gm = create_guest_mem();
480 // test_elf.bin loads a phdr at 0x20_0000, so this will fail due to an out-of-bounds address
481 let kernel_addr = GuestAddress(0x30_0000);
482 let mut image = make_elf64_bin();
483 let res = load_elf(&gm, kernel_addr, &mut image, 0);
484 assert_eq!(res, Err(Error::ProgramHeaderAddressOutOfRange));
485 }
486 }
487