]>
Commit | Line | Data |
---|---|---|
8a31787f | 1 | /* |
2 | * GRUB -- GRand Unified Bootloader | |
3 | * Copyright (C) 1999,2000,2001,2002,2003,2004,2005,2007,2008,2009 Free Software Foundation, Inc. | |
4 | * | |
5 | * GRUB is free software: you can redistribute it and/or modify | |
6 | * it under the terms of the GNU General Public License as published by | |
7 | * the Free Software Foundation, either version 3 of the License, or | |
8 | * (at your option) any later version. | |
9 | * | |
10 | * GRUB is distributed in the hope that it will be useful, | |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | * GNU General Public License for more details. | |
14 | * | |
15 | * You should have received a copy of the GNU General Public License | |
16 | * along with GRUB. If not, see <http://www.gnu.org/licenses/>. | |
17 | */ | |
18 | ||
19 | #if defined(MULTIBOOT_LOAD_ELF32) | |
20 | # define XX 32 | |
8c46a785 | 21 | # define E_MACHINE MULTIBOOT_ELF32_MACHINE |
8a31787f | 22 | # define ELFCLASSXX ELFCLASS32 |
23 | # define Elf_Ehdr Elf32_Ehdr | |
24 | # define Elf_Phdr Elf32_Phdr | |
91a1a164 | 25 | # define Elf_Shdr Elf32_Shdr |
8a31787f | 26 | #elif defined(MULTIBOOT_LOAD_ELF64) |
27 | # define XX 64 | |
8c46a785 | 28 | # define E_MACHINE MULTIBOOT_ELF64_MACHINE |
8a31787f | 29 | # define ELFCLASSXX ELFCLASS64 |
30 | # define Elf_Ehdr Elf64_Ehdr | |
31 | # define Elf_Phdr Elf64_Phdr | |
91a1a164 | 32 | # define Elf_Shdr Elf64_Shdr |
8a31787f | 33 | #else |
34 | #error "I'm confused" | |
35 | #endif | |
36 | ||
636813f7 VS |
37 | #include <grub/i386/relocator.h> |
38 | ||
8a31787f | 39 | #define CONCAT(a,b) CONCAT_(a, b) |
40 | #define CONCAT_(a,b) a ## b | |
41 | ||
ebcecdf1 VS |
42 | #pragma GCC diagnostic ignored "-Wcast-align" |
43 | ||
8a31787f | 44 | /* Check if BUFFER contains ELF32 (or ELF64). */ |
45 | static int | |
46 | CONCAT(grub_multiboot_is_elf, XX) (void *buffer) | |
47 | { | |
48 | Elf_Ehdr *ehdr = (Elf_Ehdr *) buffer; | |
49 | ||
50 | return ehdr->e_ident[EI_CLASS] == ELFCLASSXX; | |
51 | } | |
52 | ||
53 | static grub_err_t | |
a620876e | 54 | CONCAT(grub_multiboot_load_elf, XX) (mbi_load_data_t *mld) |
8a31787f | 55 | { |
a620876e | 56 | Elf_Ehdr *ehdr = (Elf_Ehdr *) mld->buffer; |
8a31787f | 57 | char *phdr_base; |
a620876e DK |
58 | grub_err_t err; |
59 | grub_relocator_chunk_t ch; | |
14ec665c | 60 | grub_uint32_t load_offset = 0, load_size; |
8a31787f | 61 | int i; |
14ec665c | 62 | void *source = NULL; |
8a31787f | 63 | |
8a31787f | 64 | if (ehdr->e_ident[EI_MAG0] != ELFMAG0 |
65 | || ehdr->e_ident[EI_MAG1] != ELFMAG1 | |
66 | || ehdr->e_ident[EI_MAG2] != ELFMAG2 | |
67 | || ehdr->e_ident[EI_MAG3] != ELFMAG3 | |
9c4b5c13 | 68 | || ehdr->e_ident[EI_DATA] != ELFDATA2LSB) |
67093bc0 | 69 | return grub_error(GRUB_ERR_UNKNOWN_OS, N_("invalid arch-independent ELF magic")); |
9c4b5c13 VS |
70 | |
71 | if (ehdr->e_ident[EI_CLASS] != ELFCLASSXX || ehdr->e_machine != E_MACHINE | |
72 | || ehdr->e_version != EV_CURRENT) | |
67093bc0 | 73 | return grub_error (GRUB_ERR_UNKNOWN_OS, N_("invalid arch-dependent ELF magic")); |
b39f9d20 | 74 | |
6394042e | 75 | if (ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN) |
9c4b5c13 | 76 | return grub_error (GRUB_ERR_UNKNOWN_OS, N_("this ELF file is not of the right type")); |
b39f9d20 | 77 | |
8a31787f | 78 | /* FIXME: Should we support program headers at strange locations? */ |
31f6506c | 79 | if (ehdr->e_phoff + (grub_uint32_t) ehdr->e_phnum * ehdr->e_phentsize > MULTIBOOT_SEARCH) |
8a31787f | 80 | return grub_error (GRUB_ERR_BAD_OS, "program header at a too high offset"); |
81 | ||
a620876e | 82 | phdr_base = (char *) mld->buffer + ehdr->e_phoff; |
8a31787f | 83 | #define phdr(i) ((Elf_Phdr *) (phdr_base + (i) * ehdr->e_phentsize)) |
84 | ||
a620876e DK |
85 | mld->link_base_addr = ~0; |
86 | ||
87 | /* Calculate lowest and highest load address. */ | |
88 | for (i = 0; i < ehdr->e_phnum; i++) | |
89 | if (phdr(i)->p_type == PT_LOAD) | |
90 | { | |
91 | mld->link_base_addr = grub_min (mld->link_base_addr, phdr(i)->p_paddr); | |
92 | highest_load = grub_max (highest_load, phdr(i)->p_paddr + phdr(i)->p_memsz); | |
93 | } | |
94 | ||
95 | #ifdef MULTIBOOT_LOAD_ELF64 | |
96 | if (highest_load >= 0x100000000) | |
97 | return grub_error (GRUB_ERR_BAD_OS, "segment crosses 4 GiB border"); | |
98 | #endif | |
99 | ||
a620876e DK |
100 | if (mld->relocatable) |
101 | { | |
14ec665c AB |
102 | load_size = highest_load - mld->link_base_addr; |
103 | ||
104 | grub_dprintf ("multiboot_loader", "align=0x%lx, preference=0x%x, " | |
105 | "load_size=0x%x, avoid_efi_boot_services=%d\n", | |
106 | (long) mld->align, mld->preference, load_size, | |
107 | mld->avoid_efi_boot_services); | |
108 | ||
a620876e DK |
109 | if (load_size > mld->max_addr || mld->min_addr > mld->max_addr - load_size) |
110 | return grub_error (GRUB_ERR_BAD_OS, "invalid min/max address and/or load size"); | |
111 | ||
21e4a6fa | 112 | err = grub_relocator_alloc_chunk_align (GRUB_MULTIBOOT (relocator), &ch, |
a620876e DK |
113 | mld->min_addr, mld->max_addr - load_size, |
114 | load_size, mld->align ? mld->align : 1, | |
115 | mld->preference, mld->avoid_efi_boot_services); | |
a620876e | 116 | |
14ec665c AB |
117 | if (err) |
118 | { | |
119 | grub_dprintf ("multiboot_loader", "Cannot allocate memory for OS image\n"); | |
120 | return err; | |
121 | } | |
a620876e | 122 | |
14ec665c AB |
123 | mld->load_base_addr = get_physical_target_address (ch); |
124 | source = get_virtual_current_address (ch); | |
125 | } | |
126 | else | |
127 | mld->load_base_addr = mld->link_base_addr; | |
a620876e | 128 | |
14ec665c | 129 | grub_dprintf ("multiboot_loader", "relocatable=%d, link_base_addr=0x%x, " |
ec2de93f | 130 | "load_base_addr=0x%x\n", mld->relocatable, |
14ec665c | 131 | mld->link_base_addr, mld->load_base_addr); |
a620876e | 132 | |
8a31787f | 133 | /* Load every loadable segment in memory. */ |
134 | for (i = 0; i < ehdr->e_phnum; i++) | |
135 | { | |
ffadea42 | 136 | if (phdr(i)->p_type == PT_LOAD) |
8a31787f | 137 | { |
00bfa988 | 138 | |
2274cc8f | 139 | grub_dprintf ("multiboot_loader", "segment %d: paddr=0x%lx, memsz=0x%lx, vaddr=0x%lx\n", |
140 | i, (long) phdr(i)->p_paddr, (long) phdr(i)->p_memsz, (long) phdr(i)->p_vaddr); | |
8a31787f | 141 | |
14ec665c AB |
142 | if (mld->relocatable) |
143 | { | |
144 | load_offset = phdr(i)->p_paddr - mld->link_base_addr; | |
145 | grub_dprintf ("multiboot_loader", "segment %d: load_offset=0x%x\n", i, load_offset); | |
146 | } | |
147 | else | |
148 | { | |
149 | err = grub_relocator_alloc_chunk_addr (GRUB_MULTIBOOT (relocator), &ch, | |
150 | phdr(i)->p_paddr, phdr(i)->p_memsz); | |
151 | ||
152 | if (err) | |
153 | { | |
154 | grub_dprintf ("multiboot_loader", "Cannot allocate memory for OS image\n"); | |
155 | return err; | |
156 | } | |
157 | ||
158 | source = get_virtual_current_address (ch); | |
159 | } | |
14e43c6e | 160 | |
ffadea42 VS |
161 | if (phdr(i)->p_filesz != 0) |
162 | { | |
a620876e | 163 | if (grub_file_seek (mld->file, (grub_off_t) phdr(i)->p_offset) |
ffadea42 | 164 | == (grub_off_t) -1) |
9c4b5c13 | 165 | return grub_errno; |
ffadea42 | 166 | |
a620876e | 167 | if (grub_file_read (mld->file, (grub_uint8_t *) source + load_offset, phdr(i)->p_filesz) |
ffadea42 | 168 | != (grub_ssize_t) phdr(i)->p_filesz) |
9c4b5c13 VS |
169 | { |
170 | if (!grub_errno) | |
171 | grub_error (GRUB_ERR_FILE_READ_ERROR, N_("premature end of file %s"), | |
a620876e | 172 | mld->filename); |
9c4b5c13 VS |
173 | return grub_errno; |
174 | } | |
ffadea42 | 175 | } |
8a31787f | 176 | |
177 | if (phdr(i)->p_filesz < phdr(i)->p_memsz) | |
a620876e | 178 | grub_memset ((grub_uint8_t *) source + load_offset + phdr(i)->p_filesz, 0, |
8a31787f | 179 | phdr(i)->p_memsz - phdr(i)->p_filesz); |
180 | } | |
181 | } | |
182 | ||
2274cc8f | 183 | for (i = 0; i < ehdr->e_phnum; i++) |
b39f9d20 | 184 | if (phdr(i)->p_vaddr <= ehdr->e_entry |
2274cc8f | 185 | && phdr(i)->p_vaddr + phdr(i)->p_memsz > ehdr->e_entry) |
186 | { | |
21e4a6fa | 187 | GRUB_MULTIBOOT (payload_eip) = (ehdr->e_entry - phdr(i)->p_vaddr) |
14e43c6e | 188 | + phdr(i)->p_paddr; |
88f8d146 VS |
189 | #ifdef MULTIBOOT_LOAD_ELF64 |
190 | # ifdef __mips | |
191 | /* We still in 32-bit mode. */ | |
192 | if ((ehdr->e_entry - phdr(i)->p_vaddr) | |
193 | + phdr(i)->p_paddr < 0xffffffff80000000ULL) | |
194 | return grub_error (GRUB_ERR_BAD_OS, "invalid entry point for ELF64"); | |
195 | # else | |
196 | /* We still in 32-bit mode. */ | |
197 | if ((ehdr->e_entry - phdr(i)->p_vaddr) | |
198 | + phdr(i)->p_paddr > 0xffffffff) | |
199 | return grub_error (GRUB_ERR_BAD_OS, "invalid entry point for ELF64"); | |
200 | # endif | |
201 | #endif | |
2274cc8f | 202 | break; |
203 | } | |
204 | ||
205 | if (i == ehdr->e_phnum) | |
206 | return grub_error (GRUB_ERR_BAD_OS, "entry point isn't in a segment"); | |
8a31787f | 207 | |
506e4d1e VS |
208 | #if defined (__i386__) || defined (__x86_64__) |
209 | ||
210 | #elif defined (__mips) | |
21e4a6fa | 211 | GRUB_MULTIBOOT (payload_eip) |= 0x80000000; |
506e4d1e VS |
212 | #else |
213 | #error Please complete this | |
214 | #endif | |
215 | ||
865a0f8a VS |
216 | if (ehdr->e_shnum) |
217 | { | |
218 | grub_uint8_t *shdr, *shdrptr; | |
219 | ||
4f8fe948 | 220 | shdr = grub_malloc ((grub_uint32_t) ehdr->e_shnum * ehdr->e_shentsize); |
865a0f8a VS |
221 | if (!shdr) |
222 | return grub_errno; | |
223 | ||
a620876e | 224 | if (grub_file_seek (mld->file, ehdr->e_shoff) == (grub_off_t) -1) |
eb33e61b AB |
225 | { |
226 | grub_free (shdr); | |
227 | return grub_errno; | |
228 | } | |
865a0f8a | 229 | |
a620876e | 230 | if (grub_file_read (mld->file, shdr, (grub_uint32_t) ehdr->e_shnum * ehdr->e_shentsize) |
865a0f8a | 231 | != (grub_ssize_t) ehdr->e_shnum * ehdr->e_shentsize) |
9c4b5c13 VS |
232 | { |
233 | if (!grub_errno) | |
234 | grub_error (GRUB_ERR_FILE_READ_ERROR, N_("premature end of file %s"), | |
a620876e | 235 | mld->filename); |
9c4b5c13 VS |
236 | return grub_errno; |
237 | } | |
865a0f8a VS |
238 | |
239 | for (shdrptr = shdr, i = 0; i < ehdr->e_shnum; | |
240 | shdrptr += ehdr->e_shentsize, i++) | |
241 | { | |
242 | Elf_Shdr *sh = (Elf_Shdr *) shdrptr; | |
243 | void *src; | |
244 | grub_addr_t target; | |
a620876e DK |
245 | |
246 | if (mld->mbi_ver >= 2 && (sh->sh_type == SHT_REL || sh->sh_type == SHT_RELA)) | |
247 | return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, "ELF files with relocs are not supported yet"); | |
865a0f8a VS |
248 | |
249 | /* This section is a loaded section, | |
250 | so we don't care. */ | |
251 | if (sh->sh_addr != 0) | |
252 | continue; | |
253 | ||
254 | /* This section is empty, so we don't care. */ | |
255 | if (sh->sh_size == 0) | |
256 | continue; | |
257 | ||
21e4a6fa | 258 | err = grub_relocator_alloc_chunk_align (GRUB_MULTIBOOT (relocator), &ch, 0, |
a620876e DK |
259 | (0xffffffff - sh->sh_size) + 1, |
260 | sh->sh_size, sh->sh_addralign, | |
261 | GRUB_RELOCATOR_PREFERENCE_NONE, | |
262 | mld->avoid_efi_boot_services); | |
263 | if (err) | |
264 | { | |
265 | grub_dprintf ("multiboot_loader", "Error loading shdr %d\n", i); | |
266 | return err; | |
267 | } | |
268 | src = get_virtual_current_address (ch); | |
269 | target = get_physical_target_address (ch); | |
270 | ||
271 | if (grub_file_seek (mld->file, sh->sh_offset) == (grub_off_t) -1) | |
9c4b5c13 | 272 | return grub_errno; |
865a0f8a | 273 | |
a620876e | 274 | if (grub_file_read (mld->file, src, sh->sh_size) |
865a0f8a | 275 | != (grub_ssize_t) sh->sh_size) |
9c4b5c13 VS |
276 | { |
277 | if (!grub_errno) | |
278 | grub_error (GRUB_ERR_FILE_READ_ERROR, N_("premature end of file %s"), | |
a620876e | 279 | mld->filename); |
9c4b5c13 VS |
280 | return grub_errno; |
281 | } | |
865a0f8a VS |
282 | sh->sh_addr = target; |
283 | } | |
21e4a6fa VS |
284 | GRUB_MULTIBOOT (add_elfsyms) (ehdr->e_shnum, ehdr->e_shentsize, |
285 | ehdr->e_shstrndx, shdr); | |
865a0f8a VS |
286 | } |
287 | ||
8a31787f | 288 | #undef phdr |
289 | ||
290 | return grub_errno; | |
291 | } | |
292 | ||
293 | #undef XX | |
294 | #undef E_MACHINE | |
295 | #undef ELFCLASSXX | |
296 | #undef Elf_Ehdr | |
297 | #undef Elf_Phdr | |
91a1a164 | 298 | #undef Elf_Shdr |