1use crate::importer::GuestArch;
14use crate::importer::GuestArchKind;
15use crate::importer::ImageLoad;
16use hvdef::HV_PAGE_SIZE;
17use object::ReadCache;
18use object::ReadRef;
19use object::elf;
20use object::read::elf::FileHeader;
21use std::io::Read;
22use std::io::Seek;
23use thiserror::Error;
24
25type LE = object::LittleEndian;
26const LE: LE = LE {};
27
28#[derive(Debug, Error)]
29pub enum Error {
30 #[error("failed to read file header")]
31 ReadFileHeader,
32 #[error("invalid file header")]
33 InvalidFileHeader,
34 #[error("target machine mismatch")]
35 TargetMachineMismatch,
36 #[error("unsupported ELF file byte order")]
37 BigEndianElfOnLittle,
38 #[error(
39 "invalid entry address found in ELF header: {e_entry:#x}, start address: {start_address:#x}, load offset: {load_offset:#x}"
40 )]
41 InvalidEntryAddress {
42 e_entry: u64,
43 start_address: u64,
44 load_offset: u64,
45 },
46 #[error("failed to parse ELF program header")]
47 InvalidProgramHeader(#[source] object::read::Error),
48 #[error("adding load offset {load_offset} to paddr {p_paddr} overflowed")]
49 LoadOffsetOverflow { load_offset: u64, p_paddr: u64 },
50 #[error("invalid ELF program header memory offset {mem_offset}, below start {start_address}")]
51 InvalidProgramHeaderMemoryOffset { mem_offset: u64, start_address: u64 },
52 #[error(
53 "adding reloc bias {reloc_bias} and load offset {load_offset} to paddr {p_paddr} overflowed"
54 )]
55 RelocBiasOverflow {
56 load_offset: u64,
57 reloc_bias: u64,
58 p_paddr: u64,
59 },
60 #[error("failed to read kernel image")]
61 ReadKernelImage,
62 #[error("failed during import pages call")]
63 ImportPages(#[source] anyhow::Error),
64 #[error("failed to seek to offset of kernel image")]
65 SeekKernelImage,
66}
67
68pub type Result<T> = std::result::Result<T, Error>;
69
70#[derive(Debug)]
72pub struct LoadInfo {
73 pub minimum_address_used: u64,
76 pub next_available_address: u64,
78 pub entrypoint: u64,
80}
81
82pub fn load_static_elf<F, R: GuestArch>(
96 importer: &mut dyn ImageLoad<R>,
97 kernel_image: &mut F,
98 start_address: u64,
99 load_offset: u64,
100 assume_pic: bool,
101 acceptance: crate::importer::BootPageAcceptance,
102 tag: &str,
103) -> Result<LoadInfo>
104where
105 F: Read + Seek,
106{
107 let reader = ReadCache::new(kernel_image);
108 let ehdr: &elf::FileHeader64<LE> = reader.read_at(0).map_err(|_| Error::ReadFileHeader)?;
109
110 if !ehdr.is_supported() {
112 return Err(Error::InvalidFileHeader);
113 }
114 if ehdr.is_big_endian() {
115 return Err(Error::BigEndianElfOnLittle);
116 }
117
118 match R::arch() {
119 GuestArchKind::Aarch64 => {
120 if ehdr.e_machine != object::U16::new(object::LittleEndian, elf::EM_AARCH64) {
121 tracing::error!(
122 "ELF file target machine mismatch, was the file built for aarch64?"
123 );
124 return Err(Error::TargetMachineMismatch);
125 }
126 }
127 GuestArchKind::X86_64 => {
128 if ehdr.e_machine != object::U16::new(object::LittleEndian, elf::EM_X86_64) {
129 tracing::error!("ELF file target machine mismatch, was the file built for X86_64?");
130 return Err(Error::TargetMachineMismatch);
131 }
132 }
133 }
134
135 let e_entry = ehdr.e_entry.get(LE);
136 let load_offset = if assume_pic && e_entry < start_address {
137 start_address + load_offset
139 } else {
140 load_offset
141 };
142
143 let entry = e_entry
144 .checked_add(load_offset)
145 .ok_or(Error::InvalidEntryAddress {
146 e_entry,
147 start_address,
148 load_offset,
149 })?;
150 if entry < start_address {
151 return Err(Error::InvalidEntryAddress {
152 e_entry,
153 start_address,
154 load_offset,
155 });
156 }
157
158 let phdrs = ehdr
159 .program_headers(LE, &reader)
160 .map_err(Error::InvalidProgramHeader)?;
161
162 let (lowest_addr, last_offset, reloc_bias) = {
164 let mut lowest_addr = u64::MAX;
165 let mut last_offset = load_offset;
166
167 for phdr in phdrs {
169 if phdr.p_type.get(LE) != elf::PT_LOAD {
170 continue;
171 }
172
173 let p_paddr = phdr.p_paddr.get(LE);
174 let mem_offset = p_paddr
175 .checked_add(load_offset)
176 .ok_or(Error::LoadOffsetOverflow {
177 load_offset,
178 p_paddr,
179 })?;
180
181 if mem_offset < start_address {
182 return Err(Error::InvalidProgramHeaderMemoryOffset {
183 mem_offset,
184 start_address,
185 });
186 }
187
188 let page_mask = HV_PAGE_SIZE - 1;
189 let page_base = mem_offset / HV_PAGE_SIZE;
190 let page_count: u64 =
191 ((mem_offset & page_mask) + phdr.p_memsz.get(LE) + page_mask) / HV_PAGE_SIZE;
192
193 lowest_addr = lowest_addr.min(page_base * HV_PAGE_SIZE);
194 last_offset = last_offset.max((page_base + page_count) * HV_PAGE_SIZE);
195 }
196
197 (
198 lowest_addr,
199 last_offset,
200 if assume_pic {
201 lowest_addr - start_address
202 } else {
203 0
204 },
205 )
206 };
207
208 for phdr in phdrs {
211 if phdr.p_type.get(LE) != elf::PT_LOAD {
212 continue;
213 }
214
215 let p_paddr = phdr.p_paddr.get(LE);
216 let mem_offset = p_paddr
217 .checked_add(load_offset)
218 .ok_or(Error::LoadOffsetOverflow {
219 load_offset,
220 p_paddr,
221 })?
222 .checked_sub(reloc_bias)
223 .ok_or(Error::RelocBiasOverflow {
224 load_offset,
225 reloc_bias,
226 p_paddr,
227 })?;
228
229 if mem_offset < start_address {
230 return Err(Error::InvalidProgramHeaderMemoryOffset {
231 mem_offset,
232 start_address,
233 });
234 }
235
236 let page_mask = HV_PAGE_SIZE - 1;
237
238 let filesz = phdr.p_filesz.get(LE);
239 let mut v = vec![0; ((mem_offset & page_mask) + filesz) as usize];
240 if filesz != 0 {
241 let v_start_offset = (mem_offset & page_mask) as usize;
242 let read_length = (v.len() - v_start_offset) as u64;
243 v[v_start_offset..].copy_from_slice(
244 reader
245 .read_bytes_at(phdr.p_offset.get(LE), read_length)
246 .map_err(|_| Error::ReadKernelImage)?,
247 );
248 }
249
250 let page_base = mem_offset / HV_PAGE_SIZE;
251 let page_count =
252 ((mem_offset & page_mask) + phdr.p_memsz.get(LE) + page_mask) / HV_PAGE_SIZE;
253 importer
254 .import_pages(page_base, page_count, tag, acceptance, &v)
255 .map_err(Error::ImportPages)?;
256 }
257
258 Ok(LoadInfo {
259 minimum_address_used: lowest_addr - reloc_bias,
260 next_available_address: last_offset - reloc_bias,
261 entrypoint: entry - reloc_bias,
262 })
263}