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 phdrs = ehdr
137 .program_headers(LE, &reader)
138 .map_err(Error::InvalidProgramHeader)?;
139
140 let load_offset = if assume_pic {
147 let mut lowest_paddr = u64::MAX;
148 for phdr in phdrs {
149 if phdr.p_type.get(LE) == elf::PT_LOAD {
150 let p_paddr = phdr.p_paddr.get(LE);
151 lowest_paddr = lowest_paddr.min(p_paddr);
152 }
153 }
154 if lowest_paddr < start_address {
155 start_address - lowest_paddr + load_offset
156 } else {
157 load_offset
158 }
159 } else {
160 load_offset
161 };
162
163 let entry = e_entry
164 .checked_add(load_offset)
165 .ok_or(Error::InvalidEntryAddress {
166 e_entry,
167 start_address,
168 load_offset,
169 })?;
170 if entry < start_address {
171 return Err(Error::InvalidEntryAddress {
172 e_entry,
173 start_address,
174 load_offset,
175 });
176 }
177
178 let (lowest_addr, last_offset, reloc_bias) = {
180 let mut lowest_addr = u64::MAX;
181 let mut last_offset = load_offset;
182
183 for phdr in phdrs {
185 if phdr.p_type.get(LE) != elf::PT_LOAD {
186 continue;
187 }
188
189 let p_paddr = phdr.p_paddr.get(LE);
190 let mem_offset = p_paddr
191 .checked_add(load_offset)
192 .ok_or(Error::LoadOffsetOverflow {
193 load_offset,
194 p_paddr,
195 })?;
196
197 if mem_offset < start_address {
198 return Err(Error::InvalidProgramHeaderMemoryOffset {
199 mem_offset,
200 start_address,
201 });
202 }
203
204 let page_mask = HV_PAGE_SIZE - 1;
205 let page_base = mem_offset / HV_PAGE_SIZE;
206 let page_count: u64 =
207 ((mem_offset & page_mask) + phdr.p_memsz.get(LE) + page_mask) / HV_PAGE_SIZE;
208
209 lowest_addr = lowest_addr.min(page_base * HV_PAGE_SIZE);
210 last_offset = last_offset.max((page_base + page_count) * HV_PAGE_SIZE);
211 }
212
213 (
214 lowest_addr,
215 last_offset,
216 if assume_pic {
217 lowest_addr - start_address
218 } else {
219 0
220 },
221 )
222 };
223
224 for phdr in phdrs {
227 if phdr.p_type.get(LE) != elf::PT_LOAD {
228 continue;
229 }
230
231 let p_paddr = phdr.p_paddr.get(LE);
232 let mem_offset = p_paddr
233 .checked_add(load_offset)
234 .ok_or(Error::LoadOffsetOverflow {
235 load_offset,
236 p_paddr,
237 })?
238 .checked_sub(reloc_bias)
239 .ok_or(Error::RelocBiasOverflow {
240 load_offset,
241 reloc_bias,
242 p_paddr,
243 })?;
244
245 if mem_offset < start_address {
246 return Err(Error::InvalidProgramHeaderMemoryOffset {
247 mem_offset,
248 start_address,
249 });
250 }
251
252 let page_mask = HV_PAGE_SIZE - 1;
253
254 let filesz = phdr.p_filesz.get(LE);
255 let mut v = vec![0; ((mem_offset & page_mask) + filesz) as usize];
256 if filesz != 0 {
257 let v_start_offset = (mem_offset & page_mask) as usize;
258 let read_length = (v.len() - v_start_offset) as u64;
259 v[v_start_offset..].copy_from_slice(
260 reader
261 .read_bytes_at(phdr.p_offset.get(LE), read_length)
262 .map_err(|_| Error::ReadKernelImage)?,
263 );
264 }
265
266 let page_base = mem_offset / HV_PAGE_SIZE;
267 let page_count =
268 ((mem_offset & page_mask) + phdr.p_memsz.get(LE) + page_mask) / HV_PAGE_SIZE;
269 if page_count > 0 {
270 importer
271 .import_pages(page_base, page_count, tag, acceptance, &v)
272 .map_err(Error::ImportPages)?;
273 }
274 }
275
276 Ok(LoadInfo {
277 minimum_address_used: lowest_addr - reloc_bias,
278 next_available_address: last_offset - reloc_bias,
279 entrypoint: entry - reloc_bias,
280 })
281}