]>
Commit | Line | Data |
---|---|---|
0253aeb7 VS |
1 | /* grub-mkimage.c - make a bootable image */ |
2 | /* | |
3 | * GRUB -- GRand Unified Bootloader | |
4 | * Copyright (C) 2002,2003,2004,2005,2006,2007,2008,2009,2010 Free Software Foundation, Inc. | |
5 | * | |
6 | * GRUB is free software: you can redistribute it and/or modify | |
7 | * it under the terms of the GNU General Public License as published by | |
8 | * the Free Software Foundation, either version 3 of the License, or | |
9 | * (at your option) any later version. | |
10 | * | |
11 | * GRUB is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | * GNU General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU General Public License | |
17 | * along with GRUB. If not, see <http://www.gnu.org/licenses/>. | |
18 | */ | |
19 | ||
8ea6ecb3 VS |
20 | #undef ELF_R_SYM |
21 | #undef ELF_R_TYPE | |
22 | ||
0253aeb7 VS |
23 | #if defined(MKIMAGE_ELF32) |
24 | # define SUFFIX(x) x ## 32 | |
25 | # define ELFCLASSXX ELFCLASS32 | |
26 | # define Elf_Ehdr Elf32_Ehdr | |
27 | # define Elf_Phdr Elf32_Phdr | |
9612ebc0 | 28 | # define Elf_Nhdr Elf32_Nhdr |
0253aeb7 VS |
29 | # define Elf_Addr Elf32_Addr |
30 | # define Elf_Sym Elf32_Sym | |
31 | # define Elf_Off Elf32_Off | |
32 | # define Elf_Shdr Elf32_Shdr | |
33 | # define Elf_Rela Elf32_Rela | |
34 | # define Elf_Rel Elf32_Rel | |
c36e5cd1 VS |
35 | # define Elf_Word Elf32_Word |
36 | # define Elf_Half Elf32_Half | |
37 | # define Elf_Section Elf32_Section | |
0253aeb7 VS |
38 | # define ELF_R_SYM(val) ELF32_R_SYM(val) |
39 | # define ELF_R_TYPE(val) ELF32_R_TYPE(val) | |
c36e5cd1 | 40 | # define ELF_ST_TYPE(val) ELF32_ST_TYPE(val) |
9612ebc0 | 41 | #define XEN_NOTE_SIZE 132 |
0253aeb7 VS |
42 | #elif defined(MKIMAGE_ELF64) |
43 | # define SUFFIX(x) x ## 64 | |
44 | # define ELFCLASSXX ELFCLASS64 | |
45 | # define Elf_Ehdr Elf64_Ehdr | |
46 | # define Elf_Phdr Elf64_Phdr | |
9612ebc0 | 47 | # define Elf_Nhdr Elf64_Nhdr |
0253aeb7 VS |
48 | # define Elf_Addr Elf64_Addr |
49 | # define Elf_Sym Elf64_Sym | |
50 | # define Elf_Off Elf64_Off | |
51 | # define Elf_Shdr Elf64_Shdr | |
52 | # define Elf_Rela Elf64_Rela | |
53 | # define Elf_Rel Elf64_Rel | |
c36e5cd1 VS |
54 | # define Elf_Word Elf64_Word |
55 | # define Elf_Half Elf64_Half | |
56 | # define Elf_Section Elf64_Section | |
0253aeb7 VS |
57 | # define ELF_R_SYM(val) ELF64_R_SYM(val) |
58 | # define ELF_R_TYPE(val) ELF64_R_TYPE(val) | |
c36e5cd1 | 59 | # define ELF_ST_TYPE(val) ELF64_ST_TYPE(val) |
9612ebc0 | 60 | #define XEN_NOTE_SIZE 120 |
0253aeb7 VS |
61 | #else |
62 | #error "I'm confused" | |
63 | #endif | |
64 | ||
389b31cd LL |
65 | static Elf_Addr SUFFIX (entry_point); |
66 | ||
9612ebc0 VS |
67 | static void |
68 | SUFFIX (generate_elf) (const struct grub_install_image_target_desc *image_target, | |
69 | int note, char **core_img, size_t *core_size, | |
70 | Elf_Addr target_addr, grub_size_t align, | |
71 | size_t kernel_size, size_t bss_size) | |
72 | { | |
73 | char *elf_img; | |
74 | size_t program_size; | |
75 | Elf_Ehdr *ehdr; | |
76 | Elf_Phdr *phdr; | |
77 | Elf_Shdr *shdr; | |
78 | int header_size, footer_size = 0; | |
79 | int phnum = 1; | |
80 | int shnum = 4; | |
81 | int string_size = sizeof (".text") + sizeof ("mods") + 1; | |
82 | ||
83 | if (image_target->id != IMAGE_LOONGSON_ELF) | |
84 | phnum += 2; | |
85 | ||
86 | if (note) | |
87 | { | |
88 | phnum++; | |
89 | footer_size += sizeof (struct grub_ieee1275_note); | |
90 | } | |
91 | if (image_target->id == IMAGE_XEN) | |
92 | { | |
93 | phnum++; | |
94 | shnum++; | |
95 | string_size += sizeof (".xen"); | |
96 | footer_size += XEN_NOTE_SIZE; | |
97 | } | |
98 | header_size = ALIGN_UP (sizeof (*ehdr) + phnum * sizeof (*phdr) | |
99 | + shnum * sizeof (*shdr) + string_size, align); | |
100 | ||
101 | program_size = ALIGN_ADDR (*core_size); | |
102 | ||
103 | elf_img = xmalloc (program_size + header_size + footer_size); | |
104 | memset (elf_img, 0, program_size + header_size); | |
105 | memcpy (elf_img + header_size, *core_img, *core_size); | |
106 | ehdr = (void *) elf_img; | |
107 | phdr = (void *) (elf_img + sizeof (*ehdr)); | |
108 | shdr = (void *) (elf_img + sizeof (*ehdr) + phnum * sizeof (*phdr)); | |
109 | memcpy (ehdr->e_ident, ELFMAG, SELFMAG); | |
110 | ehdr->e_ident[EI_CLASS] = ELFCLASSXX; | |
111 | if (!image_target->bigendian) | |
112 | ehdr->e_ident[EI_DATA] = ELFDATA2LSB; | |
113 | else | |
114 | ehdr->e_ident[EI_DATA] = ELFDATA2MSB; | |
115 | ehdr->e_ident[EI_VERSION] = EV_CURRENT; | |
116 | ehdr->e_ident[EI_OSABI] = ELFOSABI_NONE; | |
117 | ehdr->e_type = grub_host_to_target16 (ET_EXEC); | |
118 | ehdr->e_machine = grub_host_to_target16 (image_target->elf_target); | |
119 | ehdr->e_version = grub_host_to_target32 (EV_CURRENT); | |
120 | ||
121 | ehdr->e_phoff = grub_host_to_target32 ((char *) phdr - (char *) ehdr); | |
122 | ehdr->e_phentsize = grub_host_to_target16 (sizeof (*phdr)); | |
123 | ehdr->e_phnum = grub_host_to_target16 (phnum); | |
124 | ||
125 | ehdr->e_shoff = grub_host_to_target32 ((grub_uint8_t *) shdr | |
126 | - (grub_uint8_t *) ehdr); | |
127 | if (image_target->id == IMAGE_LOONGSON_ELF) | |
128 | ehdr->e_shentsize = grub_host_to_target16 (0); | |
129 | else | |
130 | ehdr->e_shentsize = grub_host_to_target16 (sizeof (Elf_Shdr)); | |
131 | ehdr->e_shnum = grub_host_to_target16 (shnum); | |
132 | ehdr->e_shstrndx = grub_host_to_target16 (1); | |
133 | ||
134 | ehdr->e_ehsize = grub_host_to_target16 (sizeof (*ehdr)); | |
135 | ||
136 | phdr->p_type = grub_host_to_target32 (PT_LOAD); | |
137 | phdr->p_offset = grub_host_to_target32 (header_size); | |
138 | phdr->p_flags = grub_host_to_target32 (PF_R | PF_W | PF_X); | |
139 | ||
140 | ehdr->e_entry = grub_host_to_target32 (target_addr); | |
141 | phdr->p_vaddr = grub_host_to_target32 (target_addr); | |
142 | phdr->p_paddr = grub_host_to_target32 (target_addr); | |
143 | phdr->p_align = grub_host_to_target32 (align > image_target->link_align ? align : image_target->link_align); | |
144 | if (image_target->id == IMAGE_LOONGSON_ELF) | |
145 | ehdr->e_flags = grub_host_to_target32 (0x1000 | EF_MIPS_NOREORDER | |
146 | | EF_MIPS_PIC | EF_MIPS_CPIC); | |
147 | else | |
148 | ehdr->e_flags = 0; | |
149 | if (image_target->id == IMAGE_LOONGSON_ELF) | |
150 | { | |
151 | phdr->p_filesz = grub_host_to_target32 (*core_size); | |
152 | phdr->p_memsz = grub_host_to_target32 (*core_size); | |
153 | } | |
154 | else | |
155 | { | |
156 | grub_uint32_t target_addr_mods; | |
157 | phdr->p_filesz = grub_host_to_target32 (kernel_size); | |
158 | phdr->p_memsz = grub_host_to_target32 (kernel_size + bss_size); | |
159 | ||
160 | phdr++; | |
161 | phdr->p_type = grub_host_to_target32 (PT_GNU_STACK); | |
162 | phdr->p_offset = grub_host_to_target32 (header_size + kernel_size); | |
163 | phdr->p_paddr = phdr->p_vaddr = phdr->p_filesz = phdr->p_memsz = 0; | |
164 | phdr->p_flags = grub_host_to_target32 (PF_R | PF_W | PF_X); | |
165 | phdr->p_align = grub_host_to_target32 (image_target->link_align); | |
166 | ||
167 | phdr++; | |
168 | phdr->p_type = grub_host_to_target32 (PT_LOAD); | |
169 | phdr->p_offset = grub_host_to_target32 (header_size + kernel_size); | |
170 | phdr->p_flags = grub_host_to_target32 (PF_R | PF_W | PF_X); | |
171 | phdr->p_filesz = phdr->p_memsz | |
172 | = grub_host_to_target32 (*core_size - kernel_size); | |
173 | ||
881c6a10 VS |
174 | if (image_target->id == IMAGE_COREBOOT) |
175 | target_addr_mods = GRUB_KERNEL_I386_COREBOOT_MODULES_ADDR; | |
176 | else | |
177 | target_addr_mods = ALIGN_UP (target_addr + kernel_size + bss_size | |
178 | + image_target->mod_gap, | |
179 | image_target->mod_align); | |
9612ebc0 VS |
180 | phdr->p_vaddr = grub_host_to_target_addr (target_addr_mods); |
181 | phdr->p_paddr = grub_host_to_target_addr (target_addr_mods); | |
182 | phdr->p_align = grub_host_to_target32 (image_target->link_align); | |
183 | } | |
184 | ||
185 | if (image_target->id == IMAGE_XEN) | |
186 | { | |
187 | char *note_start = (elf_img + program_size + header_size); | |
188 | Elf_Nhdr *note_ptr; | |
189 | char *ptr = (char *) note_start; | |
190 | ||
191 | grub_util_info ("adding XEN NOTE segment"); | |
192 | ||
193 | /* Guest OS. */ | |
194 | note_ptr = (Elf_Nhdr *) ptr; | |
195 | note_ptr->n_namesz = grub_host_to_target32 (sizeof (GRUB_XEN_NOTE_NAME)); | |
196 | note_ptr->n_descsz = grub_host_to_target32 (sizeof (PACKAGE_NAME)); | |
197 | note_ptr->n_type = grub_host_to_target32 (6); | |
198 | ptr += sizeof (Elf_Nhdr); | |
199 | memcpy (ptr, GRUB_XEN_NOTE_NAME, sizeof (GRUB_XEN_NOTE_NAME)); | |
200 | ptr += ALIGN_UP (sizeof (GRUB_XEN_NOTE_NAME), 4); | |
201 | memcpy (ptr, PACKAGE_NAME, sizeof (PACKAGE_NAME)); | |
202 | ptr += ALIGN_UP (sizeof (PACKAGE_NAME), 4); | |
203 | ||
204 | /* Loader. */ | |
205 | note_ptr = (Elf_Nhdr *) ptr; | |
206 | note_ptr->n_namesz = grub_host_to_target32 (sizeof (GRUB_XEN_NOTE_NAME)); | |
207 | note_ptr->n_descsz = grub_host_to_target32 (sizeof ("generic")); | |
208 | note_ptr->n_type = grub_host_to_target32 (8); | |
209 | ptr += sizeof (Elf_Nhdr); | |
210 | memcpy (ptr, GRUB_XEN_NOTE_NAME, sizeof (GRUB_XEN_NOTE_NAME)); | |
211 | ptr += ALIGN_UP (sizeof (GRUB_XEN_NOTE_NAME), 4); | |
212 | memcpy (ptr, "generic", sizeof ("generic")); | |
213 | ptr += ALIGN_UP (sizeof ("generic"), 4); | |
214 | ||
215 | /* Version. */ | |
216 | note_ptr = (Elf_Nhdr *) ptr; | |
217 | note_ptr->n_namesz = grub_host_to_target32 (sizeof (GRUB_XEN_NOTE_NAME)); | |
218 | note_ptr->n_descsz = grub_host_to_target32 (sizeof ("xen-3.0")); | |
219 | note_ptr->n_type = grub_host_to_target32 (5); | |
220 | ptr += sizeof (Elf_Nhdr); | |
221 | memcpy (ptr, GRUB_XEN_NOTE_NAME, sizeof (GRUB_XEN_NOTE_NAME)); | |
222 | ptr += ALIGN_UP (sizeof (GRUB_XEN_NOTE_NAME), 4); | |
223 | memcpy (ptr, "xen-3.0", sizeof ("xen-3.0")); | |
224 | ptr += ALIGN_UP (sizeof ("xen-3.0"), 4); | |
225 | ||
226 | /* Entry. */ | |
227 | note_ptr = (Elf_Nhdr *) ptr; | |
228 | note_ptr->n_namesz = grub_host_to_target32 (sizeof (GRUB_XEN_NOTE_NAME)); | |
229 | note_ptr->n_descsz = grub_host_to_target32 (image_target->voidp_sizeof); | |
230 | note_ptr->n_type = grub_host_to_target32 (1); | |
231 | ptr += sizeof (Elf_Nhdr); | |
232 | memcpy (ptr, GRUB_XEN_NOTE_NAME, sizeof (GRUB_XEN_NOTE_NAME)); | |
233 | ptr += ALIGN_UP (sizeof (GRUB_XEN_NOTE_NAME), 4); | |
234 | memset (ptr, 0, image_target->voidp_sizeof); | |
235 | ptr += image_target->voidp_sizeof; | |
236 | ||
237 | /* Virt base. */ | |
238 | note_ptr = (Elf_Nhdr *) ptr; | |
239 | note_ptr->n_namesz = grub_host_to_target32 (sizeof (GRUB_XEN_NOTE_NAME)); | |
240 | note_ptr->n_descsz = grub_host_to_target32 (image_target->voidp_sizeof); | |
241 | note_ptr->n_type = grub_host_to_target32 (3); | |
242 | ptr += sizeof (Elf_Nhdr); | |
243 | memcpy (ptr, GRUB_XEN_NOTE_NAME, sizeof (GRUB_XEN_NOTE_NAME)); | |
244 | ptr += ALIGN_UP (sizeof (GRUB_XEN_NOTE_NAME), 4); | |
245 | memset (ptr, 0, image_target->voidp_sizeof); | |
246 | ptr += image_target->voidp_sizeof; | |
247 | ||
248 | /* PAE. */ | |
249 | if (image_target->elf_target == EM_386) | |
250 | { | |
251 | note_ptr = (Elf_Nhdr *) ptr; | |
252 | note_ptr->n_namesz = grub_host_to_target32 (sizeof (GRUB_XEN_NOTE_NAME)); | |
253 | note_ptr->n_descsz = grub_host_to_target32 (sizeof ("yes,bimodal")); | |
254 | note_ptr->n_type = grub_host_to_target32 (9); | |
255 | ptr += sizeof (Elf_Nhdr); | |
256 | memcpy (ptr, GRUB_XEN_NOTE_NAME, sizeof (GRUB_XEN_NOTE_NAME)); | |
257 | ptr += ALIGN_UP (sizeof (GRUB_XEN_NOTE_NAME), 4); | |
258 | memcpy (ptr, "yes", sizeof ("yes")); | |
259 | ptr += ALIGN_UP (sizeof ("yes"), 4); | |
260 | } | |
261 | ||
262 | assert (XEN_NOTE_SIZE == (ptr - note_start)); | |
263 | ||
264 | phdr++; | |
265 | phdr->p_type = grub_host_to_target32 (PT_NOTE); | |
266 | phdr->p_flags = grub_host_to_target32 (PF_R); | |
267 | phdr->p_align = grub_host_to_target32 (image_target->voidp_sizeof); | |
268 | phdr->p_vaddr = 0; | |
269 | phdr->p_paddr = 0; | |
270 | phdr->p_filesz = grub_host_to_target32 (XEN_NOTE_SIZE); | |
271 | phdr->p_memsz = 0; | |
272 | phdr->p_offset = grub_host_to_target32 (header_size + program_size); | |
273 | } | |
274 | ||
275 | if (note) | |
276 | { | |
277 | int note_size = sizeof (struct grub_ieee1275_note); | |
278 | struct grub_ieee1275_note *note_ptr = (struct grub_ieee1275_note *) | |
279 | (elf_img + program_size + header_size); | |
280 | ||
281 | grub_util_info ("adding CHRP NOTE segment"); | |
282 | ||
283 | note_ptr->header.n_namesz = grub_host_to_target32 (sizeof (GRUB_IEEE1275_NOTE_NAME)); | |
284 | note_ptr->header.n_descsz = grub_host_to_target32 (note_size); | |
285 | note_ptr->header.n_type = grub_host_to_target32 (GRUB_IEEE1275_NOTE_TYPE); | |
286 | strcpy (note_ptr->name, GRUB_IEEE1275_NOTE_NAME); | |
287 | note_ptr->descriptor.real_mode = grub_host_to_target32 (0xffffffff); | |
288 | note_ptr->descriptor.real_base = grub_host_to_target32 (0x00c00000); | |
289 | note_ptr->descriptor.real_size = grub_host_to_target32 (0xffffffff); | |
290 | note_ptr->descriptor.virt_base = grub_host_to_target32 (0xffffffff); | |
291 | note_ptr->descriptor.virt_size = grub_host_to_target32 (0xffffffff); | |
292 | note_ptr->descriptor.load_base = grub_host_to_target32 (0x00004000); | |
293 | ||
294 | phdr++; | |
295 | phdr->p_type = grub_host_to_target32 (PT_NOTE); | |
296 | phdr->p_flags = grub_host_to_target32 (PF_R); | |
297 | phdr->p_align = grub_host_to_target32 (image_target->voidp_sizeof); | |
298 | phdr->p_vaddr = 0; | |
299 | phdr->p_paddr = 0; | |
300 | phdr->p_filesz = grub_host_to_target32 (note_size); | |
301 | phdr->p_memsz = 0; | |
302 | phdr->p_offset = grub_host_to_target32 (header_size + program_size); | |
303 | } | |
304 | ||
305 | { | |
306 | char *str_start = (elf_img + sizeof (*ehdr) + phnum * sizeof (*phdr) | |
307 | + shnum * sizeof (*shdr)); | |
308 | char *ptr = str_start + 1; | |
309 | ||
310 | shdr++; | |
311 | ||
312 | shdr->sh_name = grub_host_to_target32 (0); | |
313 | shdr->sh_type = grub_host_to_target32 (SHT_STRTAB); | |
314 | shdr->sh_addr = grub_host_to_target_addr (0); | |
315 | shdr->sh_offset = grub_host_to_target_addr (str_start - elf_img); | |
316 | shdr->sh_size = grub_host_to_target32 (string_size); | |
317 | shdr->sh_link = grub_host_to_target32 (0); | |
318 | shdr->sh_info = grub_host_to_target32 (0); | |
319 | shdr->sh_addralign = grub_host_to_target32 (align); | |
320 | shdr->sh_entsize = grub_host_to_target32 (0); | |
321 | shdr++; | |
322 | ||
323 | memcpy (ptr, ".text", sizeof (".text")); | |
324 | ||
325 | shdr->sh_name = grub_host_to_target32 (ptr - str_start); | |
326 | ptr += sizeof (".text"); | |
327 | shdr->sh_type = grub_host_to_target32 (SHT_PROGBITS); | |
328 | shdr->sh_addr = grub_host_to_target_addr (target_addr); | |
329 | shdr->sh_offset = grub_host_to_target_addr (header_size); | |
330 | shdr->sh_size = grub_host_to_target32 (kernel_size); | |
331 | shdr->sh_link = grub_host_to_target32 (0); | |
332 | shdr->sh_info = grub_host_to_target32 (0); | |
333 | shdr->sh_addralign = grub_host_to_target32 (align); | |
334 | shdr->sh_entsize = grub_host_to_target32 (0); | |
335 | shdr++; | |
336 | ||
337 | memcpy (ptr, "mods", sizeof ("mods")); | |
338 | shdr->sh_name = grub_host_to_target32 (ptr - str_start); | |
339 | ptr += sizeof ("mods"); | |
340 | shdr->sh_type = grub_host_to_target32 (SHT_PROGBITS); | |
341 | shdr->sh_addr = grub_host_to_target_addr (target_addr + kernel_size); | |
342 | shdr->sh_offset = grub_host_to_target_addr (header_size + kernel_size); | |
343 | shdr->sh_size = grub_host_to_target32 (*core_size - kernel_size); | |
344 | shdr->sh_link = grub_host_to_target32 (0); | |
345 | shdr->sh_info = grub_host_to_target32 (0); | |
346 | shdr->sh_addralign = grub_host_to_target32 (image_target->voidp_sizeof); | |
347 | shdr->sh_entsize = grub_host_to_target32 (0); | |
348 | shdr++; | |
349 | ||
350 | if (image_target->id == IMAGE_XEN) | |
351 | { | |
352 | memcpy (ptr, ".xen", sizeof (".xen")); | |
353 | shdr->sh_name = grub_host_to_target32 (ptr - str_start); | |
354 | ptr += sizeof (".xen"); | |
355 | shdr->sh_type = grub_host_to_target32 (SHT_PROGBITS); | |
356 | shdr->sh_addr = grub_host_to_target_addr (target_addr + kernel_size); | |
357 | shdr->sh_offset = grub_host_to_target_addr (program_size + header_size); | |
358 | shdr->sh_size = grub_host_to_target32 (XEN_NOTE_SIZE); | |
359 | shdr->sh_link = grub_host_to_target32 (0); | |
360 | shdr->sh_info = grub_host_to_target32 (0); | |
361 | shdr->sh_addralign = grub_host_to_target32 (image_target->voidp_sizeof); | |
362 | shdr->sh_entsize = grub_host_to_target32 (0); | |
363 | shdr++; | |
364 | } | |
365 | } | |
366 | ||
367 | free (*core_img); | |
368 | *core_img = elf_img; | |
369 | *core_size = program_size + header_size + footer_size; | |
370 | } | |
371 | ||
0253aeb7 VS |
372 | /* Relocate symbols; note that this function overwrites the symbol table. |
373 | Return the address of a start symbol. */ | |
374 | static Elf_Addr | |
375 | SUFFIX (relocate_symbols) (Elf_Ehdr *e, Elf_Shdr *sections, | |
376 | Elf_Shdr *symtab_section, Elf_Addr *section_addresses, | |
377 | Elf_Half section_entsize, Elf_Half num_sections, | |
5452733f | 378 | void *jumpers, Elf_Addr jumpers_addr, |
ec16e026 | 379 | const struct grub_install_image_target_desc *image_target) |
0253aeb7 VS |
380 | { |
381 | Elf_Word symtab_size, sym_size, num_syms; | |
382 | Elf_Off symtab_offset; | |
383 | Elf_Addr start_address = 0; | |
384 | Elf_Sym *sym; | |
385 | Elf_Word i; | |
386 | Elf_Shdr *strtab_section; | |
387 | const char *strtab; | |
5452733f | 388 | grub_uint64_t *jptr = jumpers; |
0253aeb7 VS |
389 | |
390 | strtab_section | |
391 | = (Elf_Shdr *) ((char *) sections | |
392 | + (grub_target_to_host32 (symtab_section->sh_link) | |
393 | * section_entsize)); | |
962b15b4 | 394 | strtab = (char *) e + grub_target_to_host (strtab_section->sh_offset); |
0253aeb7 | 395 | |
962b15b4 VS |
396 | symtab_size = grub_target_to_host (symtab_section->sh_size); |
397 | sym_size = grub_target_to_host (symtab_section->sh_entsize); | |
398 | symtab_offset = grub_target_to_host (symtab_section->sh_offset); | |
0253aeb7 VS |
399 | num_syms = symtab_size / sym_size; |
400 | ||
401 | for (i = 0, sym = (Elf_Sym *) ((char *) e + symtab_offset); | |
402 | i < num_syms; | |
403 | i++, sym = (Elf_Sym *) ((char *) sym + sym_size)) | |
404 | { | |
495fc8c1 | 405 | Elf_Section cur_index; |
0253aeb7 VS |
406 | const char *name; |
407 | ||
408 | name = strtab + grub_target_to_host32 (sym->st_name); | |
409 | ||
495fc8c1 VS |
410 | cur_index = grub_target_to_host16 (sym->st_shndx); |
411 | if (cur_index == STN_ABS) | |
0253aeb7 VS |
412 | { |
413 | continue; | |
414 | } | |
7cb24eb5 | 415 | else if (cur_index == STN_UNDEF) |
0253aeb7 VS |
416 | { |
417 | if (sym->st_name) | |
418 | grub_util_error ("undefined symbol %s", name); | |
419 | else | |
420 | continue; | |
421 | } | |
495fc8c1 VS |
422 | else if (cur_index >= num_sections) |
423 | grub_util_error ("section %d does not exist", cur_index); | |
0253aeb7 | 424 | |
bea33583 | 425 | sym->st_value = (grub_target_to_host (sym->st_value) |
495fc8c1 | 426 | + section_addresses[cur_index]); |
5452733f VS |
427 | |
428 | if (image_target->elf_target == EM_IA_64 && ELF_ST_TYPE (sym->st_info) | |
429 | == STT_FUNC) | |
430 | { | |
d5e2a158 | 431 | *jptr = grub_host_to_target64 (sym->st_value); |
5452733f VS |
432 | sym->st_value = (char *) jptr - (char *) jumpers + jumpers_addr; |
433 | jptr++; | |
434 | *jptr = 0; | |
435 | jptr++; | |
436 | } | |
18e76955 VS |
437 | grub_util_info ("locating %s at 0x%" GRUB_HOST_PRIxLONG_LONG |
438 | " (0x%" GRUB_HOST_PRIxLONG_LONG ")", name, | |
495fc8c1 VS |
439 | (unsigned long long) sym->st_value, |
440 | (unsigned long long) section_addresses[cur_index]); | |
0253aeb7 VS |
441 | |
442 | if (! start_address) | |
443 | if (strcmp (name, "_start") == 0 || strcmp (name, "start") == 0) | |
444 | start_address = sym->st_value; | |
445 | } | |
446 | ||
447 | return start_address; | |
448 | } | |
449 | ||
450 | /* Return the address of a symbol at the index I in the section S. */ | |
451 | static Elf_Addr | |
452 | SUFFIX (get_symbol_address) (Elf_Ehdr *e, Elf_Shdr *s, Elf_Word i, | |
ec16e026 | 453 | const struct grub_install_image_target_desc *image_target) |
0253aeb7 VS |
454 | { |
455 | Elf_Sym *sym; | |
456 | ||
457 | sym = (Elf_Sym *) ((char *) e | |
d5e2a158 VS |
458 | + grub_target_to_host (s->sh_offset) |
459 | + i * grub_target_to_host (s->sh_entsize)); | |
0253aeb7 VS |
460 | return sym->st_value; |
461 | } | |
462 | ||
463 | /* Return the address of a modified value. */ | |
464 | static Elf_Addr * | |
465 | SUFFIX (get_target_address) (Elf_Ehdr *e, Elf_Shdr *s, Elf_Addr offset, | |
ec16e026 | 466 | const struct grub_install_image_target_desc *image_target) |
0253aeb7 | 467 | { |
d5e2a158 | 468 | return (Elf_Addr *) ((char *) e + grub_target_to_host (s->sh_offset) + offset); |
0253aeb7 VS |
469 | } |
470 | ||
495fc8c1 | 471 | #ifdef MKIMAGE_ELF64 |
5452733f VS |
472 | static Elf_Addr |
473 | SUFFIX (count_funcs) (Elf_Ehdr *e, Elf_Shdr *symtab_section, | |
ec16e026 | 474 | const struct grub_install_image_target_desc *image_target) |
5452733f VS |
475 | { |
476 | Elf_Word symtab_size, sym_size, num_syms; | |
477 | Elf_Off symtab_offset; | |
5452733f VS |
478 | Elf_Sym *sym; |
479 | Elf_Word i; | |
480 | int ret = 0; | |
481 | ||
482 | symtab_size = grub_target_to_host (symtab_section->sh_size); | |
483 | sym_size = grub_target_to_host (symtab_section->sh_entsize); | |
484 | symtab_offset = grub_target_to_host (symtab_section->sh_offset); | |
485 | num_syms = symtab_size / sym_size; | |
486 | ||
487 | for (i = 0, sym = (Elf_Sym *) ((char *) e + symtab_offset); | |
488 | i < num_syms; | |
489 | i++, sym = (Elf_Sym *) ((char *) sym + sym_size)) | |
490 | if (ELF_ST_TYPE (sym->st_info) == STT_FUNC) | |
491 | ret++; | |
492 | ||
493 | return ret; | |
494 | } | |
495fc8c1 | 495 | #endif |
5452733f | 496 | |
8c534b85 VS |
497 | #ifdef MKIMAGE_ELF32 |
498 | /* Deal with relocation information. This function relocates addresses | |
499 | within the virtual address space starting from 0. So only relative | |
500 | addresses can be fully resolved. Absolute addresses must be relocated | |
501 | again by a PE32 relocator when loaded. */ | |
502 | static grub_size_t | |
503 | arm_get_trampoline_size (Elf_Ehdr *e, | |
504 | Elf_Shdr *sections, | |
505 | Elf_Half section_entsize, | |
506 | Elf_Half num_sections, | |
507 | const struct grub_install_image_target_desc *image_target) | |
508 | { | |
509 | Elf_Half i; | |
510 | Elf_Shdr *s; | |
511 | grub_size_t ret = 0; | |
512 | ||
513 | for (i = 0, s = sections; | |
514 | i < num_sections; | |
515 | i++, s = (Elf_Shdr *) ((char *) s + section_entsize)) | |
516 | if ((s->sh_type == grub_host_to_target32 (SHT_REL)) || | |
517 | (s->sh_type == grub_host_to_target32 (SHT_RELA))) | |
518 | { | |
519 | Elf_Rela *r; | |
520 | Elf_Word rtab_size, r_size, num_rs; | |
521 | Elf_Off rtab_offset; | |
522 | Elf_Shdr *symtab_section; | |
523 | Elf_Word j; | |
524 | ||
525 | symtab_section = (Elf_Shdr *) ((char *) sections | |
526 | + (grub_target_to_host32 (s->sh_link) | |
527 | * section_entsize)); | |
528 | ||
529 | rtab_size = grub_target_to_host (s->sh_size); | |
530 | r_size = grub_target_to_host (s->sh_entsize); | |
531 | rtab_offset = grub_target_to_host (s->sh_offset); | |
532 | num_rs = rtab_size / r_size; | |
533 | ||
534 | for (j = 0, r = (Elf_Rela *) ((char *) e + rtab_offset); | |
535 | j < num_rs; | |
536 | j++, r = (Elf_Rela *) ((char *) r + r_size)) | |
537 | { | |
538 | Elf_Addr info; | |
539 | Elf_Addr sym_addr; | |
540 | ||
541 | info = grub_target_to_host (r->r_info); | |
542 | sym_addr = SUFFIX (get_symbol_address) (e, symtab_section, | |
543 | ELF_R_SYM (info), image_target); | |
544 | ||
545 | sym_addr += (s->sh_type == grub_target_to_host32 (SHT_RELA)) ? | |
546 | grub_target_to_host (r->r_addend) : 0; | |
547 | ||
548 | switch (ELF_R_TYPE (info)) | |
549 | { | |
550 | case R_ARM_ABS32: | |
551 | case R_ARM_V4BX: | |
552 | break; | |
553 | case R_ARM_THM_CALL: | |
554 | case R_ARM_THM_JUMP24: | |
555 | case R_ARM_THM_JUMP19: | |
556 | if (!(sym_addr & 1)) | |
557 | ret += 8; | |
558 | break; | |
559 | ||
560 | case R_ARM_CALL: | |
561 | case R_ARM_JUMP24: | |
562 | if (sym_addr & 1) | |
563 | ret += 16; | |
564 | break; | |
565 | ||
566 | default: | |
d6c2782a VS |
567 | grub_util_error (_("relocation 0x%x is not implemented yet"), |
568 | (unsigned int) ELF_R_TYPE (info)); | |
8c534b85 VS |
569 | break; |
570 | } | |
571 | } | |
572 | } | |
573 | return ret; | |
574 | } | |
575 | #endif | |
576 | ||
0253aeb7 VS |
577 | /* Deal with relocation information. This function relocates addresses |
578 | within the virtual address space starting from 0. So only relative | |
579 | addresses can be fully resolved. Absolute addresses must be relocated | |
580 | again by a PE32 relocator when loaded. */ | |
581 | static void | |
582 | SUFFIX (relocate_addresses) (Elf_Ehdr *e, Elf_Shdr *sections, | |
583 | Elf_Addr *section_addresses, | |
584 | Elf_Half section_entsize, Elf_Half num_sections, | |
5452733f VS |
585 | const char *strtab, |
586 | char *pe_target, Elf_Addr tramp_off, | |
587 | Elf_Addr got_off, | |
ec16e026 | 588 | const struct grub_install_image_target_desc *image_target) |
0253aeb7 VS |
589 | { |
590 | Elf_Half i; | |
591 | Elf_Shdr *s; | |
495fc8c1 | 592 | #ifdef MKIMAGE_ELF64 |
d5e2a158 | 593 | struct grub_ia64_trampoline *tr = (void *) (pe_target + tramp_off); |
5452733f | 594 | grub_uint64_t *gpptr = (void *) (pe_target + got_off); |
d5e2a158 | 595 | #define MASK19 ((1 << 19) - 1) |
8c534b85 VS |
596 | #else |
597 | grub_uint32_t *tr = (void *) (pe_target + tramp_off); | |
495fc8c1 | 598 | #endif |
0253aeb7 VS |
599 | |
600 | for (i = 0, s = sections; | |
601 | i < num_sections; | |
602 | i++, s = (Elf_Shdr *) ((char *) s + section_entsize)) | |
603 | if ((s->sh_type == grub_host_to_target32 (SHT_REL)) || | |
604 | (s->sh_type == grub_host_to_target32 (SHT_RELA))) | |
605 | { | |
606 | Elf_Rela *r; | |
607 | Elf_Word rtab_size, r_size, num_rs; | |
608 | Elf_Off rtab_offset; | |
609 | Elf_Shdr *symtab_section; | |
610 | Elf_Word target_section_index; | |
611 | Elf_Addr target_section_addr; | |
612 | Elf_Shdr *target_section; | |
613 | Elf_Word j; | |
614 | ||
615 | symtab_section = (Elf_Shdr *) ((char *) sections | |
616 | + (grub_target_to_host32 (s->sh_link) | |
617 | * section_entsize)); | |
618 | target_section_index = grub_target_to_host32 (s->sh_info); | |
619 | target_section_addr = section_addresses[target_section_index]; | |
620 | target_section = (Elf_Shdr *) ((char *) sections | |
621 | + (target_section_index | |
622 | * section_entsize)); | |
623 | ||
624 | grub_util_info ("dealing with the relocation section %s for %s", | |
625 | strtab + grub_target_to_host32 (s->sh_name), | |
626 | strtab + grub_target_to_host32 (target_section->sh_name)); | |
627 | ||
d5e2a158 VS |
628 | rtab_size = grub_target_to_host (s->sh_size); |
629 | r_size = grub_target_to_host (s->sh_entsize); | |
630 | rtab_offset = grub_target_to_host (s->sh_offset); | |
0253aeb7 VS |
631 | num_rs = rtab_size / r_size; |
632 | ||
633 | for (j = 0, r = (Elf_Rela *) ((char *) e + rtab_offset); | |
634 | j < num_rs; | |
635 | j++, r = (Elf_Rela *) ((char *) r + r_size)) | |
636 | { | |
637 | Elf_Addr info; | |
638 | Elf_Addr offset; | |
639 | Elf_Addr sym_addr; | |
640 | Elf_Addr *target; | |
641 | Elf_Addr addend; | |
642 | ||
643 | offset = grub_target_to_host (r->r_offset); | |
644 | target = SUFFIX (get_target_address) (e, target_section, | |
645 | offset, image_target); | |
646 | info = grub_target_to_host (r->r_info); | |
647 | sym_addr = SUFFIX (get_symbol_address) (e, symtab_section, | |
648 | ELF_R_SYM (info), image_target); | |
649 | ||
650 | addend = (s->sh_type == grub_target_to_host32 (SHT_RELA)) ? | |
d5e2a158 | 651 | grub_target_to_host (r->r_addend) : 0; |
0253aeb7 | 652 | |
bea33583 | 653 | switch (image_target->elf_target) |
654 | { | |
655 | case EM_386: | |
0253aeb7 VS |
656 | switch (ELF_R_TYPE (info)) |
657 | { | |
658 | case R_386_NONE: | |
659 | break; | |
660 | ||
661 | case R_386_32: | |
662 | /* This is absolute. */ | |
663 | *target = grub_host_to_target32 (grub_target_to_host32 (*target) | |
664 | + addend + sym_addr); | |
18e76955 VS |
665 | grub_util_info ("relocating an R_386_32 entry to 0x%" |
666 | GRUB_HOST_PRIxLONG_LONG " at the offset 0x%" | |
667 | GRUB_HOST_PRIxLONG_LONG, | |
495fc8c1 VS |
668 | (unsigned long long) *target, |
669 | (unsigned long long) offset); | |
0253aeb7 VS |
670 | break; |
671 | ||
672 | case R_386_PC32: | |
673 | /* This is relative. */ | |
674 | *target = grub_host_to_target32 (grub_target_to_host32 (*target) | |
675 | + addend + sym_addr | |
676 | - target_section_addr - offset | |
677 | - image_target->vaddr_offset); | |
18e76955 VS |
678 | grub_util_info ("relocating an R_386_PC32 entry to 0x%" |
679 | GRUB_HOST_PRIxLONG_LONG " at the offset 0x%" | |
680 | GRUB_HOST_PRIxLONG_LONG, | |
495fc8c1 VS |
681 | (unsigned long long) *target, |
682 | (unsigned long long) offset); | |
0253aeb7 VS |
683 | break; |
684 | default: | |
d6c2782a VS |
685 | grub_util_error (_("relocation 0x%x is not implemented yet"), |
686 | (unsigned int) ELF_R_TYPE (info)); | |
0253aeb7 VS |
687 | break; |
688 | } | |
bea33583 | 689 | break; |
495fc8c1 | 690 | #ifdef MKIMAGE_ELF64 |
bea33583 | 691 | case EM_X86_64: |
0253aeb7 VS |
692 | switch (ELF_R_TYPE (info)) |
693 | { | |
694 | ||
695 | case R_X86_64_NONE: | |
696 | break; | |
697 | ||
698 | case R_X86_64_64: | |
699 | *target = grub_host_to_target64 (grub_target_to_host64 (*target) | |
700 | + addend + sym_addr); | |
18e76955 VS |
701 | grub_util_info ("relocating an R_X86_64_64 entry to 0x%" |
702 | GRUB_HOST_PRIxLONG_LONG " at the offset 0x%" | |
703 | GRUB_HOST_PRIxLONG_LONG, | |
495fc8c1 VS |
704 | (unsigned long long) *target, |
705 | (unsigned long long) offset); | |
0253aeb7 VS |
706 | break; |
707 | ||
708 | case R_X86_64_PC32: | |
709 | { | |
710 | grub_uint32_t *t32 = (grub_uint32_t *) target; | |
711 | *t32 = grub_host_to_target64 (grub_target_to_host32 (*t32) | |
712 | + addend + sym_addr | |
713 | - target_section_addr - offset | |
714 | - image_target->vaddr_offset); | |
18e76955 VS |
715 | grub_util_info ("relocating an R_X86_64_PC32 entry to 0x%x at the offset 0x%" |
716 | GRUB_HOST_PRIxLONG_LONG, | |
495fc8c1 | 717 | *t32, (unsigned long long) offset); |
0253aeb7 VS |
718 | break; |
719 | } | |
720 | ||
f4171ebd VS |
721 | case R_X86_64_PC64: |
722 | { | |
723 | *target = grub_host_to_target64 (grub_target_to_host64 (*target) | |
724 | + addend + sym_addr | |
725 | - target_section_addr - offset | |
726 | - image_target->vaddr_offset); | |
18e76955 VS |
727 | grub_util_info ("relocating an R_X86_64_PC64 entry to 0x%" |
728 | GRUB_HOST_PRIxLONG_LONG " at the offset 0x%" | |
729 | GRUB_HOST_PRIxLONG_LONG, | |
f4171ebd VS |
730 | (unsigned long long) *target, |
731 | (unsigned long long) offset); | |
732 | break; | |
733 | } | |
734 | ||
0253aeb7 VS |
735 | case R_X86_64_32: |
736 | case R_X86_64_32S: | |
737 | { | |
738 | grub_uint32_t *t32 = (grub_uint32_t *) target; | |
739 | *t32 = grub_host_to_target64 (grub_target_to_host32 (*t32) | |
740 | + addend + sym_addr); | |
18e76955 VS |
741 | grub_util_info ("relocating an R_X86_64_32(S) entry to 0x%x at the offset 0x%" |
742 | GRUB_HOST_PRIxLONG_LONG, | |
495fc8c1 | 743 | *t32, (unsigned long long) offset); |
0253aeb7 VS |
744 | break; |
745 | } | |
746 | ||
747 | default: | |
d6c2782a VS |
748 | grub_util_error (_("relocation 0x%x is not implemented yet"), |
749 | (unsigned int) ELF_R_TYPE (info)); | |
0253aeb7 VS |
750 | break; |
751 | } | |
bea33583 | 752 | break; |
753 | case EM_IA_64: | |
754 | switch (ELF_R_TYPE (info)) | |
755 | { | |
5452733f VS |
756 | case R_IA64_PCREL21B: |
757 | { | |
758 | grub_uint64_t noff; | |
d5e2a158 | 759 | grub_ia64_make_trampoline (tr, addend + sym_addr); |
5452733f | 760 | noff = ((char *) tr - (char *) pe_target |
a988e7aa | 761 | - target_section_addr - (offset & ~3)) >> 4; |
5452733f VS |
762 | tr++; |
763 | if (noff & ~MASK19) | |
0b6225bd | 764 | grub_util_error ("trampoline offset too big (%" |
18e76955 VS |
765 | GRUB_HOST_PRIxLONG_LONG ")", |
766 | (unsigned long long) noff); | |
d5e2a158 | 767 | grub_ia64_add_value_to_slot_20b ((grub_addr_t) target, noff); |
5452733f VS |
768 | } |
769 | break; | |
770 | ||
5452733f VS |
771 | case R_IA64_LTOFF22X: |
772 | case R_IA64_LTOFF22: | |
2679b724 VS |
773 | { |
774 | Elf_Sym *sym; | |
775 | ||
776 | sym = (Elf_Sym *) ((char *) e | |
d5e2a158 VS |
777 | + grub_target_to_host (symtab_section->sh_offset) |
778 | + ELF_R_SYM (info) * grub_target_to_host (symtab_section->sh_entsize)); | |
2679b724 VS |
779 | if (ELF_ST_TYPE (sym->st_info) == STT_FUNC) |
780 | sym_addr = grub_target_to_host64 (*(grub_uint64_t *) (pe_target | |
781 | + sym->st_value | |
782 | - image_target->vaddr_offset)); | |
783 | } | |
784 | case R_IA64_LTOFF_FPTR22: | |
5452733f | 785 | *gpptr = grub_host_to_target64 (addend + sym_addr); |
d5e2a158 VS |
786 | grub_ia64_add_value_to_slot_21 ((grub_addr_t) target, |
787 | (char *) gpptr - (char *) pe_target | |
788 | + image_target->vaddr_offset); | |
5452733f VS |
789 | gpptr++; |
790 | break; | |
791 | ||
792 | case R_IA64_GPREL22: | |
d5e2a158 VS |
793 | grub_ia64_add_value_to_slot_21 ((grub_addr_t) target, |
794 | addend + sym_addr); | |
5452733f VS |
795 | break; |
796 | case R_IA64_PCREL64LSB: | |
797 | *target = grub_host_to_target64 (grub_target_to_host64 (*target) | |
798 | + addend + sym_addr | |
799 | - target_section_addr - offset | |
800 | - image_target->vaddr_offset); | |
801 | break; | |
802 | ||
803 | case R_IA64_SEGREL64LSB: | |
804 | *target = grub_host_to_target64 (grub_target_to_host64 (*target) | |
805 | + addend + sym_addr - target_section_addr); | |
806 | break; | |
807 | case R_IA64_DIR64LSB: | |
808 | case R_IA64_FPTR64LSB: | |
809 | *target = grub_host_to_target64 (grub_target_to_host64 (*target) | |
810 | + addend + sym_addr); | |
811 | grub_util_info ("relocating a direct entry to 0x%" | |
18e76955 VS |
812 | GRUB_HOST_PRIxLONG_LONG " at the offset 0x%" |
813 | GRUB_HOST_PRIxLONG_LONG, | |
814 | (unsigned long long) | |
d5e2a158 VS |
815 | grub_target_to_host64 (*target), |
816 | (unsigned long long) offset); | |
5452733f VS |
817 | break; |
818 | ||
819 | /* We treat LTOFF22X as LTOFF22, so we can ignore LDXMOV. */ | |
820 | case R_IA64_LDXMOV: | |
821 | break; | |
822 | ||
bea33583 | 823 | default: |
d6c2782a VS |
824 | grub_util_error (_("relocation 0x%x is not implemented yet"), |
825 | (unsigned int) ELF_R_TYPE (info)); | |
bea33583 | 826 | break; |
827 | } | |
828 | break; | |
15a463d7 LL |
829 | case EM_AARCH64: |
830 | { | |
831 | sym_addr += addend; | |
832 | switch (ELF_R_TYPE (info)) | |
833 | { | |
834 | case R_AARCH64_ABS64: | |
835 | { | |
836 | *target = grub_host_to_target64 (grub_target_to_host64 (*target) + sym_addr); | |
837 | } | |
838 | break; | |
839 | case R_AARCH64_JUMP26: | |
840 | case R_AARCH64_CALL26: | |
841 | { | |
15a463d7 LL |
842 | sym_addr -= offset; |
843 | sym_addr -= SUFFIX (entry_point); | |
8c534b85 | 844 | if (!grub_arm_64_check_xxxx26_offset (sym_addr)) |
57a691b7 | 845 | grub_util_error ("%s", "CALL26 Relocation out of range"); |
8c534b85 VS |
846 | |
847 | grub_arm64_set_xxxx26_offset((grub_uint32_t *)target, | |
15a463d7 | 848 | sym_addr); |
15a463d7 LL |
849 | } |
850 | break; | |
851 | default: | |
d6c2782a VS |
852 | grub_util_error (_("relocation 0x%x is not implemented yet"), |
853 | (unsigned int) ELF_R_TYPE (info)); | |
15a463d7 LL |
854 | break; |
855 | } | |
856 | break; | |
857 | } | |
5452733f | 858 | #endif |
389b31cd LL |
859 | #if defined(MKIMAGE_ELF32) |
860 | case EM_ARM: | |
861 | { | |
862 | sym_addr += addend; | |
863 | sym_addr -= SUFFIX (entry_point); | |
864 | switch (ELF_R_TYPE (info)) | |
865 | { | |
866 | case R_ARM_ABS32: | |
867 | { | |
868 | grub_util_info (" ABS32:\toffset=%d\t(0x%08x)", | |
869 | (int) sym_addr, (int) sym_addr); | |
870 | /* Data will be naturally aligned */ | |
389b31cd LL |
871 | sym_addr += 0x400; |
872 | *target = grub_host_to_target32 (grub_target_to_host32 (*target) + sym_addr); | |
873 | } | |
874 | break; | |
8c534b85 VS |
875 | /* Happens when compiled with -march=armv4. |
876 | Since currently we need at least armv5, keep bx as-is. | |
877 | */ | |
878 | case R_ARM_V4BX: | |
879 | break; | |
389b31cd LL |
880 | case R_ARM_THM_CALL: |
881 | case R_ARM_THM_JUMP24: | |
8c534b85 | 882 | case R_ARM_THM_JUMP19: |
389b31cd | 883 | { |
28af3d86 | 884 | grub_err_t err; |
e7c418c5 VS |
885 | grub_util_info (" THM_JUMP24:\ttarget=0x%08lx\toffset=(0x%08x)", |
886 | (unsigned long) ((char *) target | |
887 | - (char *) e), | |
888 | sym_addr); | |
8c534b85 VS |
889 | if (!(sym_addr & 1)) |
890 | { | |
891 | grub_uint32_t tr_addr; | |
892 | grub_int32_t new_offset; | |
893 | tr_addr = (char *) tr - (char *) pe_target | |
894 | - target_section_addr; | |
895 | new_offset = sym_addr - tr_addr - 12; | |
896 | ||
897 | if (!grub_arm_jump24_check_offset (new_offset)) | |
898 | return grub_util_error ("jump24 relocation out of range"); | |
899 | ||
900 | tr[0] = grub_host_to_target32 (0x46c04778); /* bx pc; nop */ | |
901 | tr[1] = grub_host_to_target32 (((new_offset >> 2) & 0xffffff) | 0xea000000); /* b new_offset */ | |
902 | tr += 2; | |
903 | sym_addr = tr_addr | 1; | |
904 | } | |
389b31cd LL |
905 | sym_addr -= offset; |
906 | /* Thumb instructions can be 16-bit aligned */ | |
8c534b85 VS |
907 | if (ELF_R_TYPE (info) == R_ARM_THM_JUMP19) |
908 | err = grub_arm_reloc_thm_jump19 ((grub_uint16_t *) target, sym_addr); | |
909 | else | |
910 | err = grub_arm_reloc_thm_call ((grub_uint16_t *) target, | |
911 | sym_addr); | |
28af3d86 VS |
912 | if (err) |
913 | grub_util_error ("%s", grub_errmsg); | |
389b31cd LL |
914 | } |
915 | break; | |
8c534b85 VS |
916 | |
917 | case R_ARM_CALL: | |
918 | case R_ARM_JUMP24: | |
389b31cd | 919 | { |
28af3d86 | 920 | grub_err_t err; |
e7c418c5 | 921 | grub_util_info (" JUMP24:\ttarget=0x%08lx\toffset=(0x%08x)", (unsigned long) ((char *) target - (char *) e), sym_addr); |
8c534b85 VS |
922 | if (sym_addr & 1) |
923 | { | |
924 | grub_uint32_t tr_addr; | |
925 | grub_int32_t new_offset; | |
926 | tr_addr = (char *) tr - (char *) pe_target | |
927 | - target_section_addr; | |
928 | new_offset = sym_addr - tr_addr - 12; | |
929 | ||
930 | /* There is no immediate version of bx, only register one... */ | |
931 | tr[0] = grub_host_to_target32 (0xe59fc004); /* ldr ip, [pc, #4] */ | |
932 | tr[1] = grub_host_to_target32 (0xe08cc00f); /* add ip, ip, pc */ | |
933 | tr[2] = grub_host_to_target32 (0xe12fff1c); /* bx ip */ | |
934 | tr[3] = grub_host_to_target32 (new_offset | 1); | |
935 | tr += 4; | |
936 | sym_addr = tr_addr; | |
937 | } | |
389b31cd | 938 | sym_addr -= offset; |
8c534b85 VS |
939 | err = grub_arm_reloc_jump24 (target, |
940 | sym_addr); | |
28af3d86 VS |
941 | if (err) |
942 | grub_util_error ("%s", grub_errmsg); | |
389b31cd LL |
943 | } |
944 | break; | |
8c534b85 | 945 | |
389b31cd | 946 | default: |
d6c2782a VS |
947 | grub_util_error (_("relocation 0x%x is not implemented yet"), |
948 | (unsigned int) ELF_R_TYPE (info)); | |
389b31cd LL |
949 | break; |
950 | } | |
951 | break; | |
952 | } | |
953 | #endif /* MKIMAGE_ELF32 */ | |
bea33583 | 954 | default: |
955 | grub_util_error ("unknown architecture type %d", | |
956 | image_target->elf_target); | |
957 | } | |
0253aeb7 VS |
958 | } |
959 | } | |
960 | } | |
961 | ||
962 | /* Add a PE32's fixup entry for a relocation. Return the resulting address | |
963 | after having written to the file OUT. */ | |
964 | static Elf_Addr | |
965 | SUFFIX (add_fixup_entry) (struct fixup_block_list **cblock, grub_uint16_t type, | |
966 | Elf_Addr addr, int flush, Elf_Addr current_address, | |
ec16e026 | 967 | const struct grub_install_image_target_desc *image_target) |
0253aeb7 VS |
968 | { |
969 | struct grub_pe32_fixup_block *b; | |
970 | ||
971 | b = &((*cblock)->b); | |
972 | ||
973 | /* First, check if it is necessary to write out the current block. */ | |
974 | if ((*cblock)->state) | |
975 | { | |
976 | if (flush || addr < b->page_rva || b->page_rva + 0x1000 <= addr) | |
977 | { | |
978 | grub_uint32_t size; | |
979 | ||
980 | if (flush) | |
981 | { | |
982 | /* Add as much padding as necessary to align the address | |
983 | with a section boundary. */ | |
984 | Elf_Addr next_address; | |
985 | unsigned padding_size; | |
495fc8c1 | 986 | size_t cur_index; |
0253aeb7 VS |
987 | |
988 | next_address = current_address + b->block_size; | |
989 | padding_size = ((ALIGN_UP (next_address, image_target->section_align) | |
990 | - next_address) | |
991 | >> 1); | |
495fc8c1 | 992 | cur_index = ((b->block_size - sizeof (*b)) >> 1); |
0253aeb7 VS |
993 | grub_util_info ("adding %d padding fixup entries", padding_size); |
994 | while (padding_size--) | |
995 | { | |
495fc8c1 | 996 | b->entries[cur_index++] = 0; |
0253aeb7 VS |
997 | b->block_size += 2; |
998 | } | |
999 | } | |
5452733f | 1000 | else while (b->block_size & (8 - 1)) |
0253aeb7 VS |
1001 | { |
1002 | /* If not aligned with a 32-bit boundary, add | |
1003 | a padding entry. */ | |
495fc8c1 | 1004 | size_t cur_index; |
0253aeb7 VS |
1005 | |
1006 | grub_util_info ("adding a padding fixup entry"); | |
495fc8c1 VS |
1007 | cur_index = ((b->block_size - sizeof (*b)) >> 1); |
1008 | b->entries[cur_index] = 0; | |
0253aeb7 VS |
1009 | b->block_size += 2; |
1010 | } | |
1011 | ||
1012 | /* Flush it. */ | |
1013 | grub_util_info ("writing %d bytes of a fixup block starting at 0x%x", | |
1014 | b->block_size, b->page_rva); | |
1015 | size = b->block_size; | |
1016 | current_address += size; | |
1017 | b->page_rva = grub_host_to_target32 (b->page_rva); | |
1018 | b->block_size = grub_host_to_target32 (b->block_size); | |
1019 | (*cblock)->next = xmalloc (sizeof (**cblock) + 2 * 0x1000); | |
1020 | memset ((*cblock)->next, 0, sizeof (**cblock) + 2 * 0x1000); | |
1021 | *cblock = (*cblock)->next; | |
1022 | } | |
1023 | } | |
1024 | ||
1025 | b = &((*cblock)->b); | |
1026 | ||
1027 | if (! flush) | |
1028 | { | |
1029 | grub_uint16_t entry; | |
495fc8c1 | 1030 | size_t cur_index; |
0253aeb7 VS |
1031 | |
1032 | /* If not allocated yet, allocate a block with enough entries. */ | |
1033 | if (! (*cblock)->state) | |
1034 | { | |
1035 | (*cblock)->state = 1; | |
1036 | ||
1037 | /* The spec does not mention the requirement of a Page RVA. | |
1038 | Here, align the address with a 4K boundary for safety. */ | |
1039 | b->page_rva = (addr & ~(0x1000 - 1)); | |
1040 | b->block_size = sizeof (*b); | |
1041 | } | |
1042 | ||
1043 | /* Sanity check. */ | |
1044 | if (b->block_size >= sizeof (*b) + 2 * 0x1000) | |
1045 | grub_util_error ("too many fixup entries"); | |
1046 | ||
1047 | /* Add a new entry. */ | |
495fc8c1 | 1048 | cur_index = ((b->block_size - sizeof (*b)) >> 1); |
0253aeb7 | 1049 | entry = GRUB_PE32_FIXUP_ENTRY (type, addr - b->page_rva); |
495fc8c1 | 1050 | b->entries[cur_index] = grub_host_to_target16 (entry); |
0253aeb7 VS |
1051 | b->block_size += 2; |
1052 | } | |
1053 | ||
1054 | return current_address; | |
1055 | } | |
1056 | ||
1057 | /* Make a .reloc section. */ | |
1058 | static Elf_Addr | |
1059 | SUFFIX (make_reloc_section) (Elf_Ehdr *e, void **out, | |
1060 | Elf_Addr *section_addresses, Elf_Shdr *sections, | |
1061 | Elf_Half section_entsize, Elf_Half num_sections, | |
5452733f VS |
1062 | const char *strtab, |
1063 | Elf_Addr jumpers, grub_size_t njumpers, | |
ec16e026 | 1064 | const struct grub_install_image_target_desc *image_target) |
0253aeb7 | 1065 | { |
5452733f | 1066 | unsigned i; |
0253aeb7 VS |
1067 | Elf_Shdr *s; |
1068 | struct fixup_block_list *lst, *lst0; | |
1069 | Elf_Addr current_address = 0; | |
1070 | ||
1071 | lst = lst0 = xmalloc (sizeof (*lst) + 2 * 0x1000); | |
1072 | memset (lst, 0, sizeof (*lst) + 2 * 0x1000); | |
1073 | ||
5452733f | 1074 | for (i = 0, s = sections; i < num_sections; |
0253aeb7 | 1075 | i++, s = (Elf_Shdr *) ((char *) s + section_entsize)) |
d5e2a158 VS |
1076 | if ((grub_target_to_host32 (s->sh_type) == SHT_REL) || |
1077 | (grub_target_to_host32 (s->sh_type) == SHT_RELA)) | |
0253aeb7 VS |
1078 | { |
1079 | Elf_Rel *r; | |
1080 | Elf_Word rtab_size, r_size, num_rs; | |
1081 | Elf_Off rtab_offset; | |
1082 | Elf_Addr section_address; | |
1083 | Elf_Word j; | |
1084 | ||
1085 | grub_util_info ("translating the relocation section %s", | |
1086 | strtab + grub_le_to_cpu32 (s->sh_name)); | |
1087 | ||
d5e2a158 VS |
1088 | rtab_size = grub_target_to_host (s->sh_size); |
1089 | r_size = grub_target_to_host (s->sh_entsize); | |
1090 | rtab_offset = grub_target_to_host (s->sh_offset); | |
0253aeb7 VS |
1091 | num_rs = rtab_size / r_size; |
1092 | ||
1093 | section_address = section_addresses[grub_le_to_cpu32 (s->sh_info)]; | |
1094 | ||
1095 | for (j = 0, r = (Elf_Rel *) ((char *) e + rtab_offset); | |
1096 | j < num_rs; | |
1097 | j++, r = (Elf_Rel *) ((char *) r + r_size)) | |
1098 | { | |
1099 | Elf_Addr info; | |
1100 | Elf_Addr offset; | |
1101 | ||
d5e2a158 VS |
1102 | offset = grub_target_to_host (r->r_offset); |
1103 | info = grub_target_to_host (r->r_info); | |
0253aeb7 VS |
1104 | |
1105 | /* Necessary to relocate only absolute addresses. */ | |
bea33583 | 1106 | switch (image_target->elf_target) |
0253aeb7 | 1107 | { |
bea33583 | 1108 | case EM_386: |
0253aeb7 VS |
1109 | if (ELF_R_TYPE (info) == R_386_32) |
1110 | { | |
1111 | Elf_Addr addr; | |
1112 | ||
1113 | addr = section_address + offset; | |
18e76955 VS |
1114 | grub_util_info ("adding a relocation entry for 0x%" |
1115 | GRUB_HOST_PRIxLONG_LONG, | |
495fc8c1 | 1116 | (unsigned long long) addr); |
0253aeb7 VS |
1117 | current_address |
1118 | = SUFFIX (add_fixup_entry) (&lst, | |
1119 | GRUB_PE32_REL_BASED_HIGHLOW, | |
1120 | addr, 0, current_address, | |
1121 | image_target); | |
1122 | } | |
bea33583 | 1123 | break; |
1124 | case EM_X86_64: | |
0253aeb7 VS |
1125 | if ((ELF_R_TYPE (info) == R_X86_64_32) || |
1126 | (ELF_R_TYPE (info) == R_X86_64_32S)) | |
1127 | { | |
1128 | grub_util_error ("can\'t add fixup entry for R_X86_64_32(S)"); | |
1129 | } | |
1130 | else if (ELF_R_TYPE (info) == R_X86_64_64) | |
1131 | { | |
1132 | Elf_Addr addr; | |
1133 | ||
1134 | addr = section_address + offset; | |
18e76955 VS |
1135 | grub_util_info ("adding a relocation entry for 0x%" |
1136 | GRUB_HOST_PRIxLONG_LONG, | |
495fc8c1 | 1137 | (unsigned long long) addr); |
0253aeb7 VS |
1138 | current_address |
1139 | = SUFFIX (add_fixup_entry) (&lst, | |
1140 | GRUB_PE32_REL_BASED_DIR64, | |
1141 | addr, | |
1142 | 0, current_address, | |
1143 | image_target); | |
1144 | } | |
bea33583 | 1145 | break; |
5452733f VS |
1146 | case EM_IA_64: |
1147 | switch (ELF_R_TYPE (info)) | |
1148 | { | |
1149 | case R_IA64_PCREL64LSB: | |
1150 | case R_IA64_LDXMOV: | |
1151 | case R_IA64_PCREL21B: | |
1152 | case R_IA64_LTOFF_FPTR22: | |
1153 | case R_IA64_LTOFF22X: | |
1154 | case R_IA64_LTOFF22: | |
1155 | case R_IA64_GPREL22: | |
1156 | case R_IA64_SEGREL64LSB: | |
1157 | break; | |
1158 | ||
1159 | case R_IA64_FPTR64LSB: | |
1160 | case R_IA64_DIR64LSB: | |
1161 | #if 1 | |
1162 | { | |
1163 | Elf_Addr addr; | |
1164 | ||
1165 | addr = section_address + offset; | |
18e76955 VS |
1166 | grub_util_info ("adding a relocation entry for 0x%" |
1167 | GRUB_HOST_PRIxLONG_LONG, | |
495fc8c1 | 1168 | (unsigned long long) addr); |
5452733f VS |
1169 | current_address |
1170 | = SUFFIX (add_fixup_entry) (&lst, | |
1171 | GRUB_PE32_REL_BASED_DIR64, | |
1172 | addr, | |
1173 | 0, current_address, | |
1174 | image_target); | |
1175 | } | |
1176 | #endif | |
1177 | break; | |
1178 | default: | |
d6c2782a VS |
1179 | grub_util_error (_("relocation 0x%x is not implemented yet"), |
1180 | (unsigned int) ELF_R_TYPE (info)); | |
5452733f VS |
1181 | break; |
1182 | } | |
1183 | break; | |
15a463d7 LL |
1184 | case EM_AARCH64: |
1185 | switch (ELF_R_TYPE (info)) | |
1186 | { | |
1187 | case R_AARCH64_ABS64: | |
1188 | { | |
1189 | Elf_Addr addr; | |
1190 | ||
1191 | addr = section_address + offset; | |
1192 | current_address | |
1193 | = SUFFIX (add_fixup_entry) (&lst, | |
1194 | GRUB_PE32_REL_BASED_DIR64, | |
1195 | addr, 0, current_address, | |
1196 | image_target); | |
1197 | } | |
1198 | break; | |
1199 | /* Relative relocations do not require fixup entries. */ | |
1200 | case R_AARCH64_CALL26: | |
1201 | case R_AARCH64_JUMP26: | |
1202 | break; | |
1203 | default: | |
57a691b7 | 1204 | grub_util_error (_("relocation 0x%x is not implemented yet"), |
d6c2782a | 1205 | (unsigned int) ELF_R_TYPE (info)); |
15a463d7 LL |
1206 | break; |
1207 | } | |
1208 | break; | |
1209 | break; | |
389b31cd LL |
1210 | #if defined(MKIMAGE_ELF32) |
1211 | case EM_ARM: | |
1212 | switch (ELF_R_TYPE (info)) | |
1213 | { | |
8c534b85 | 1214 | case R_ARM_V4BX: |
389b31cd LL |
1215 | /* Relative relocations do not require fixup entries. */ |
1216 | case R_ARM_JUMP24: | |
1217 | case R_ARM_THM_CALL: | |
1218 | case R_ARM_THM_JUMP19: | |
1219 | case R_ARM_THM_JUMP24: | |
8c534b85 | 1220 | case R_ARM_CALL: |
389b31cd LL |
1221 | { |
1222 | Elf_Addr addr; | |
1223 | ||
1224 | addr = section_address + offset; | |
1225 | grub_util_info (" %s: not adding fixup: 0x%08x : 0x%08x", __FUNCTION__, (unsigned int) addr, (unsigned int) current_address); | |
1226 | } | |
1227 | break; | |
1228 | /* Create fixup entry for PE/COFF loader */ | |
1229 | case R_ARM_ABS32: | |
854f383c | 1230 | { |
389b31cd LL |
1231 | Elf_Addr addr; |
1232 | ||
1233 | addr = section_address + offset; | |
389b31cd LL |
1234 | current_address |
1235 | = SUFFIX (add_fixup_entry) (&lst, | |
1236 | GRUB_PE32_REL_BASED_HIGHLOW, | |
1237 | addr, 0, current_address, | |
1238 | image_target); | |
1239 | } | |
1240 | break; | |
1241 | default: | |
57a691b7 VS |
1242 | grub_util_error (_("relocation 0x%x is not implemented yet"), |
1243 | (unsigned int) ELF_R_TYPE (info)); | |
389b31cd LL |
1244 | break; |
1245 | } | |
1246 | break; | |
1247 | #endif /* defined(MKIMAGE_ELF32) */ | |
bea33583 | 1248 | default: |
1249 | grub_util_error ("unknown machine type 0x%x", image_target->elf_target); | |
0253aeb7 VS |
1250 | } |
1251 | } | |
1252 | } | |
1253 | ||
5452733f VS |
1254 | if (image_target->elf_target == EM_IA_64) |
1255 | for (i = 0; i < njumpers; i++) | |
1256 | current_address = SUFFIX (add_fixup_entry) (&lst, | |
1257 | GRUB_PE32_REL_BASED_DIR64, | |
1258 | jumpers + 8 * i, | |
1259 | 0, current_address, | |
1260 | image_target); | |
1261 | ||
0253aeb7 VS |
1262 | current_address = SUFFIX (add_fixup_entry) (&lst, 0, 0, 1, current_address, image_target); |
1263 | ||
1264 | { | |
1265 | grub_uint8_t *ptr; | |
1266 | ptr = *out = xmalloc (current_address); | |
1267 | for (lst = lst0; lst; lst = lst->next) | |
1268 | if (lst->state) | |
1269 | { | |
1270 | memcpy (ptr, &lst->b, grub_target_to_host32 (lst->b.block_size)); | |
1271 | ptr += grub_target_to_host32 (lst->b.block_size); | |
1272 | } | |
495fc8c1 | 1273 | assert ((current_address + (grub_uint8_t *) *out) == ptr); |
0253aeb7 VS |
1274 | } |
1275 | ||
663f6eb1 VS |
1276 | for (lst = lst0; lst; ) |
1277 | { | |
1278 | struct fixup_block_list *next; | |
1279 | next = lst->next; | |
1280 | free (lst); | |
1281 | lst = next; | |
1282 | } | |
1283 | ||
0253aeb7 VS |
1284 | return current_address; |
1285 | } | |
1286 | ||
1287 | /* Determine if this section is a text section. Return false if this | |
1288 | section is not allocated. */ | |
1289 | static int | |
ec16e026 | 1290 | SUFFIX (is_text_section) (Elf_Shdr *s, const struct grub_install_image_target_desc *image_target) |
0253aeb7 VS |
1291 | { |
1292 | if (image_target->id != IMAGE_EFI | |
1293 | && grub_target_to_host32 (s->sh_type) != SHT_PROGBITS) | |
1294 | return 0; | |
962b15b4 | 1295 | return ((grub_target_to_host (s->sh_flags) & (SHF_EXECINSTR | SHF_ALLOC)) |
0253aeb7 VS |
1296 | == (SHF_EXECINSTR | SHF_ALLOC)); |
1297 | } | |
1298 | ||
1299 | /* Determine if this section is a data section. This assumes that | |
1300 | BSS is also a data section, since the converter initializes BSS | |
1301 | when producing PE32 to avoid a bug in EFI implementations. */ | |
1302 | static int | |
ec16e026 | 1303 | SUFFIX (is_data_section) (Elf_Shdr *s, const struct grub_install_image_target_desc *image_target) |
0253aeb7 VS |
1304 | { |
1305 | if (image_target->id != IMAGE_EFI | |
1306 | && grub_target_to_host32 (s->sh_type) != SHT_PROGBITS) | |
1307 | return 0; | |
962b15b4 | 1308 | return ((grub_target_to_host (s->sh_flags) & (SHF_EXECINSTR | SHF_ALLOC)) |
0253aeb7 VS |
1309 | == SHF_ALLOC); |
1310 | } | |
1311 | ||
1312 | /* Return if the ELF header is valid. */ | |
1313 | static int | |
ec16e026 | 1314 | SUFFIX (check_elf_header) (Elf_Ehdr *e, size_t size, const struct grub_install_image_target_desc *image_target) |
0253aeb7 VS |
1315 | { |
1316 | if (size < sizeof (*e) | |
1317 | || e->e_ident[EI_MAG0] != ELFMAG0 | |
1318 | || e->e_ident[EI_MAG1] != ELFMAG1 | |
1319 | || e->e_ident[EI_MAG2] != ELFMAG2 | |
1320 | || e->e_ident[EI_MAG3] != ELFMAG3 | |
1321 | || e->e_ident[EI_VERSION] != EV_CURRENT | |
1322 | || e->e_ident[EI_CLASS] != ELFCLASSXX | |
1323 | || e->e_version != grub_host_to_target32 (EV_CURRENT)) | |
1324 | return 0; | |
1325 | ||
1326 | return 1; | |
1327 | } | |
1328 | ||
1329 | /* Locate section addresses by merging code sections and data sections | |
1330 | into .text and .data, respectively. Return the array of section | |
1331 | addresses. */ | |
1332 | static Elf_Addr * | |
a0c9d5c6 VS |
1333 | SUFFIX (locate_sections) (const char *kernel_path, |
1334 | Elf_Shdr *sections, Elf_Half section_entsize, | |
0253aeb7 | 1335 | Elf_Half num_sections, const char *strtab, |
f7676b70 VS |
1336 | size_t *exec_size, size_t *kernel_sz, |
1337 | size_t *all_align, | |
ec16e026 | 1338 | const struct grub_install_image_target_desc *image_target) |
0253aeb7 VS |
1339 | { |
1340 | int i; | |
1341 | Elf_Addr current_address; | |
1342 | Elf_Addr *section_addresses; | |
1343 | Elf_Shdr *s; | |
1344 | ||
debaf23f VS |
1345 | *all_align = 1; |
1346 | ||
0253aeb7 VS |
1347 | section_addresses = xmalloc (sizeof (*section_addresses) * num_sections); |
1348 | memset (section_addresses, 0, sizeof (*section_addresses) * num_sections); | |
1349 | ||
1350 | current_address = 0; | |
1351 | ||
debaf23f VS |
1352 | for (i = 0, s = sections; |
1353 | i < num_sections; | |
1354 | i++, s = (Elf_Shdr *) ((char *) s + section_entsize)) | |
1355 | if ((grub_target_to_host (s->sh_flags) & SHF_ALLOC) | |
1356 | && grub_host_to_target32 (s->sh_addralign) > *all_align) | |
1357 | *all_align = grub_host_to_target32 (s->sh_addralign); | |
1358 | ||
1359 | ||
0253aeb7 VS |
1360 | /* .text */ |
1361 | for (i = 0, s = sections; | |
1362 | i < num_sections; | |
1363 | i++, s = (Elf_Shdr *) ((char *) s + section_entsize)) | |
1364 | if (SUFFIX (is_text_section) (s, image_target)) | |
1365 | { | |
fb53b340 | 1366 | Elf_Word align = grub_host_to_target_addr (s->sh_addralign); |
0253aeb7 | 1367 | const char *name = strtab + grub_host_to_target32 (s->sh_name); |
0253aeb7 VS |
1368 | if (align) |
1369 | current_address = ALIGN_UP (current_address + image_target->vaddr_offset, | |
1370 | align) - image_target->vaddr_offset; | |
18e76955 VS |
1371 | grub_util_info ("locating the section %s at 0x%" |
1372 | GRUB_HOST_PRIxLONG_LONG, | |
495fc8c1 | 1373 | name, (unsigned long long) current_address); |
9d742bd4 | 1374 | if (image_target->id != IMAGE_EFI) |
a0c9d5c6 VS |
1375 | { |
1376 | current_address = grub_host_to_target_addr (s->sh_addr) | |
1377 | - image_target->link_addr; | |
1378 | if (grub_host_to_target_addr (s->sh_addr) | |
1379 | != image_target->link_addr) | |
f4dab3d1 VS |
1380 | { |
1381 | char *msg | |
496a6b30 | 1382 | = grub_xasprintf (_("`%s' is miscompiled: its start address is 0x%llx" |
f4dab3d1 VS |
1383 | " instead of 0x%llx: ld.gold bug?"), |
1384 | kernel_path, | |
1385 | (unsigned long long) grub_host_to_target_addr (s->sh_addr), | |
1386 | (unsigned long long) image_target->link_addr); | |
1387 | grub_util_error ("%s", msg); | |
1388 | } | |
a0c9d5c6 | 1389 | } |
0253aeb7 | 1390 | section_addresses[i] = current_address; |
962b15b4 | 1391 | current_address += grub_host_to_target_addr (s->sh_size); |
0253aeb7 VS |
1392 | } |
1393 | ||
1394 | current_address = ALIGN_UP (current_address + image_target->vaddr_offset, | |
1395 | image_target->section_align) | |
1396 | - image_target->vaddr_offset; | |
1397 | *exec_size = current_address; | |
1398 | ||
1399 | /* .data */ | |
1400 | for (i = 0, s = sections; | |
1401 | i < num_sections; | |
1402 | i++, s = (Elf_Shdr *) ((char *) s + section_entsize)) | |
1403 | if (SUFFIX (is_data_section) (s, image_target)) | |
1404 | { | |
fb53b340 | 1405 | Elf_Word align = grub_host_to_target_addr (s->sh_addralign); |
0253aeb7 VS |
1406 | const char *name = strtab + grub_host_to_target32 (s->sh_name); |
1407 | ||
1408 | if (align) | |
1409 | current_address = ALIGN_UP (current_address + image_target->vaddr_offset, | |
1410 | align) | |
1411 | - image_target->vaddr_offset; | |
1412 | ||
18e76955 VS |
1413 | grub_util_info ("locating the section %s at 0x%" |
1414 | GRUB_HOST_PRIxLONG_LONG, | |
495fc8c1 | 1415 | name, (unsigned long long) current_address); |
9d742bd4 VS |
1416 | if (image_target->id != IMAGE_EFI) |
1417 | current_address = grub_host_to_target_addr (s->sh_addr) | |
1418 | - image_target->link_addr; | |
0253aeb7 | 1419 | section_addresses[i] = current_address; |
962b15b4 | 1420 | current_address += grub_host_to_target_addr (s->sh_size); |
0253aeb7 VS |
1421 | } |
1422 | ||
1423 | current_address = ALIGN_UP (current_address + image_target->vaddr_offset, | |
1424 | image_target->section_align) - image_target->vaddr_offset; | |
1425 | *kernel_sz = current_address; | |
1426 | return section_addresses; | |
1427 | } | |
1428 | ||
1429 | static char * | |
f7676b70 VS |
1430 | SUFFIX (load_image) (const char *kernel_path, size_t *exec_size, |
1431 | size_t *kernel_sz, size_t *bss_size, | |
1432 | size_t total_module_size, grub_uint64_t *start, | |
1433 | void **reloc_section, size_t *reloc_size, | |
1434 | size_t *align, | |
ec16e026 | 1435 | const struct grub_install_image_target_desc *image_target) |
0253aeb7 VS |
1436 | { |
1437 | char *kernel_img, *out_img; | |
1438 | const char *strtab; | |
1439 | Elf_Ehdr *e; | |
1440 | Elf_Shdr *sections; | |
1441 | Elf_Addr *section_addresses; | |
1442 | Elf_Addr *section_vaddresses; | |
1443 | int i; | |
1444 | Elf_Shdr *s; | |
1445 | Elf_Half num_sections; | |
1446 | Elf_Off section_offset; | |
1447 | Elf_Half section_entsize; | |
1448 | grub_size_t kernel_size; | |
8c534b85 | 1449 | grub_size_t ia64jmp_off = 0, tramp_off = 0, ia64_got_off = 0; |
5452733f | 1450 | unsigned ia64jmpnum = 0; |
0b6225bd | 1451 | Elf_Shdr *symtab_section = 0; |
5452733f | 1452 | grub_size_t got = 0; |
0253aeb7 VS |
1453 | |
1454 | *start = 0; | |
1455 | ||
1456 | kernel_size = grub_util_get_image_size (kernel_path); | |
1457 | kernel_img = xmalloc (kernel_size); | |
1458 | grub_util_load_image (kernel_path, kernel_img); | |
1459 | ||
1460 | e = (Elf_Ehdr *) kernel_img; | |
1461 | if (! SUFFIX (check_elf_header) (e, kernel_size, image_target)) | |
1462 | grub_util_error ("invalid ELF header"); | |
1463 | ||
962b15b4 | 1464 | section_offset = grub_target_to_host (e->e_shoff); |
0253aeb7 VS |
1465 | section_entsize = grub_target_to_host16 (e->e_shentsize); |
1466 | num_sections = grub_target_to_host16 (e->e_shnum); | |
1467 | ||
1468 | if (kernel_size < section_offset + section_entsize * num_sections) | |
d61386e2 | 1469 | grub_util_error (_("premature end of file %s"), kernel_path); |
0253aeb7 VS |
1470 | |
1471 | sections = (Elf_Shdr *) (kernel_img + section_offset); | |
1472 | ||
1473 | /* Relocate sections then symbols in the virtual address space. */ | |
1474 | s = (Elf_Shdr *) ((char *) sections | |
1475 | + grub_host_to_target16 (e->e_shstrndx) * section_entsize); | |
fb53b340 | 1476 | strtab = (char *) e + grub_host_to_target_addr (s->sh_offset); |
0253aeb7 | 1477 | |
a0c9d5c6 VS |
1478 | section_addresses = SUFFIX (locate_sections) (kernel_path, |
1479 | sections, section_entsize, | |
0253aeb7 | 1480 | num_sections, strtab, |
debaf23f VS |
1481 | exec_size, kernel_sz, align, |
1482 | image_target); | |
0253aeb7 | 1483 | |
debaf23f | 1484 | section_vaddresses = xmalloc (sizeof (*section_addresses) * num_sections); |
0253aeb7 | 1485 | |
debaf23f VS |
1486 | for (i = 0; i < num_sections; i++) |
1487 | section_vaddresses[i] = section_addresses[i] + image_target->vaddr_offset; | |
0253aeb7 | 1488 | |
debaf23f VS |
1489 | if (image_target->id != IMAGE_EFI) |
1490 | { | |
1491 | Elf_Addr current_address = *kernel_sz; | |
0253aeb7 | 1492 | |
debaf23f VS |
1493 | for (i = 0, s = sections; |
1494 | i < num_sections; | |
1495 | i++, s = (Elf_Shdr *) ((char *) s + section_entsize)) | |
1496 | if (grub_target_to_host32 (s->sh_type) == SHT_NOBITS) | |
1497 | { | |
495fc8c1 | 1498 | Elf_Word sec_align = grub_host_to_target_addr (s->sh_addralign); |
debaf23f | 1499 | const char *name = strtab + grub_host_to_target32 (s->sh_name); |
0253aeb7 | 1500 | |
495fc8c1 | 1501 | if (sec_align) |
debaf23f | 1502 | current_address = ALIGN_UP (current_address |
495fc8c1 VS |
1503 | + image_target->vaddr_offset, |
1504 | sec_align) | |
debaf23f | 1505 | - image_target->vaddr_offset; |
0253aeb7 | 1506 | |
18e76955 VS |
1507 | grub_util_info ("locating the section %s at 0x%" |
1508 | GRUB_HOST_PRIxLONG_LONG, | |
495fc8c1 | 1509 | name, (unsigned long long) current_address); |
9d742bd4 VS |
1510 | if (image_target->id != IMAGE_EFI) |
1511 | current_address = grub_host_to_target_addr (s->sh_addr) | |
1512 | - image_target->link_addr; | |
1513 | ||
debaf23f VS |
1514 | section_vaddresses[i] = current_address |
1515 | + image_target->vaddr_offset; | |
1516 | current_address += grub_host_to_target_addr (s->sh_size); | |
1517 | } | |
1518 | current_address = ALIGN_UP (current_address + image_target->vaddr_offset, | |
1519 | image_target->section_align) | |
1520 | - image_target->vaddr_offset; | |
1521 | *bss_size = current_address - *kernel_sz; | |
1522 | } | |
1523 | else | |
1524 | *bss_size = 0; | |
0253aeb7 | 1525 | |
e5ed2f69 VS |
1526 | if (image_target->id == IMAGE_SPARC64_AOUT |
1527 | || image_target->id == IMAGE_SPARC64_RAW | |
1528 | || image_target->id == IMAGE_SPARC64_CDCORE) | |
1529 | *kernel_sz = ALIGN_UP (*kernel_sz, image_target->mod_align); | |
1530 | ||
debaf23f VS |
1531 | if (image_target->id == IMAGE_EFI) |
1532 | { | |
0253aeb7 VS |
1533 | symtab_section = NULL; |
1534 | for (i = 0, s = sections; | |
1535 | i < num_sections; | |
1536 | i++, s = (Elf_Shdr *) ((char *) s + section_entsize)) | |
1537 | if (s->sh_type == grub_host_to_target32 (SHT_SYMTAB)) | |
1538 | { | |
1539 | symtab_section = s; | |
1540 | break; | |
1541 | } | |
7aaed664 AB |
1542 | if (! symtab_section) |
1543 | grub_util_error ("%s", _("no symbol table")); | |
0253aeb7 | 1544 | |
8c534b85 VS |
1545 | #ifdef MKIMAGE_ELF32 |
1546 | if (image_target->elf_target == EM_ARM) | |
1547 | { | |
1548 | grub_size_t tramp; | |
1549 | ||
1550 | *kernel_sz = ALIGN_UP (*kernel_sz, 16); | |
1551 | ||
1552 | tramp = arm_get_trampoline_size (e, sections, section_entsize, | |
1553 | num_sections, image_target); | |
1554 | ||
1555 | tramp_off = *kernel_sz; | |
1556 | *kernel_sz += ALIGN_UP (tramp, 16); | |
1557 | } | |
1558 | #endif | |
1559 | ||
5452733f VS |
1560 | #ifdef MKIMAGE_ELF64 |
1561 | if (image_target->elf_target == EM_IA_64) | |
1562 | { | |
1563 | grub_size_t tramp; | |
1564 | ||
1565 | *kernel_sz = ALIGN_UP (*kernel_sz, 16); | |
1566 | ||
1567 | grub_ia64_dl_get_tramp_got_size (e, &tramp, &got); | |
5452733f | 1568 | |
8c534b85 | 1569 | tramp_off = *kernel_sz; |
5452733f VS |
1570 | *kernel_sz += ALIGN_UP (tramp, 16); |
1571 | ||
1572 | ia64jmp_off = *kernel_sz; | |
1573 | ia64jmpnum = SUFFIX (count_funcs) (e, symtab_section, | |
1574 | image_target); | |
1575 | *kernel_sz += 16 * ia64jmpnum; | |
1576 | ||
1577 | ia64_got_off = *kernel_sz; | |
49060520 | 1578 | *kernel_sz += ALIGN_UP (got, 16); |
5452733f VS |
1579 | } |
1580 | #endif | |
1581 | ||
5452733f VS |
1582 | } |
1583 | else | |
1584 | { | |
1585 | *reloc_size = 0; | |
1586 | *reloc_section = NULL; | |
1587 | } | |
1588 | ||
1589 | out_img = xmalloc (*kernel_sz + total_module_size); | |
0253aeb7 | 1590 | |
5452733f VS |
1591 | if (image_target->id == IMAGE_EFI) |
1592 | { | |
0253aeb7 VS |
1593 | *start = SUFFIX (relocate_symbols) (e, sections, symtab_section, |
1594 | section_vaddresses, section_entsize, | |
5452733f VS |
1595 | num_sections, |
1596 | (char *) out_img + ia64jmp_off, | |
1597 | ia64jmp_off | |
1598 | + image_target->vaddr_offset, | |
1599 | image_target); | |
0253aeb7 VS |
1600 | if (*start == 0) |
1601 | grub_util_error ("start symbol is not defined"); | |
15a463d7 | 1602 | |
389b31cd LL |
1603 | SUFFIX (entry_point) = (Elf_Addr) *start; |
1604 | ||
0253aeb7 | 1605 | /* Resolve addresses in the virtual address space. */ |
5452733f VS |
1606 | SUFFIX (relocate_addresses) (e, sections, section_addresses, |
1607 | section_entsize, | |
1608 | num_sections, strtab, | |
8c534b85 | 1609 | out_img, tramp_off, ia64_got_off, |
5452733f | 1610 | image_target); |
15a463d7 | 1611 | |
0253aeb7 VS |
1612 | *reloc_size = SUFFIX (make_reloc_section) (e, reloc_section, |
1613 | section_vaddresses, sections, | |
1614 | section_entsize, num_sections, | |
5452733f VS |
1615 | strtab, ia64jmp_off |
1616 | + image_target->vaddr_offset, | |
a846dd4b | 1617 | 2 * ia64jmpnum + (got / 8), |
5452733f | 1618 | image_target); |
0253aeb7 VS |
1619 | } |
1620 | ||
0253aeb7 VS |
1621 | for (i = 0, s = sections; |
1622 | i < num_sections; | |
1623 | i++, s = (Elf_Shdr *) ((char *) s + section_entsize)) | |
1624 | if (SUFFIX (is_data_section) (s, image_target) | |
1625 | || SUFFIX (is_text_section) (s, image_target)) | |
1626 | { | |
1627 | if (grub_target_to_host32 (s->sh_type) == SHT_NOBITS) | |
1628 | memset (out_img + section_addresses[i], 0, | |
962b15b4 | 1629 | grub_host_to_target_addr (s->sh_size)); |
0253aeb7 VS |
1630 | else |
1631 | memcpy (out_img + section_addresses[i], | |
962b15b4 VS |
1632 | kernel_img + grub_host_to_target_addr (s->sh_offset), |
1633 | grub_host_to_target_addr (s->sh_size)); | |
0253aeb7 VS |
1634 | } |
1635 | free (kernel_img); | |
1636 | ||
663f6eb1 VS |
1637 | free (section_vaddresses); |
1638 | free (section_addresses); | |
1639 | ||
0253aeb7 VS |
1640 | return out_img; |
1641 | } | |
1642 | ||
1643 | ||
1644 | #undef SUFFIX | |
1645 | #undef ELFCLASSXX | |
1646 | #undef Elf_Ehdr | |
1647 | #undef Elf_Phdr | |
9612ebc0 | 1648 | #undef Elf_Nhdr |
0253aeb7 VS |
1649 | #undef Elf_Shdr |
1650 | #undef Elf_Addr | |
1651 | #undef Elf_Sym | |
1652 | #undef Elf_Off | |
1653 | #undef Elf_Rela | |
1654 | #undef Elf_Rel | |
1655 | #undef ELF_R_TYPE | |
1656 | #undef ELF_R_SYM | |
c36e5cd1 VS |
1657 | #undef Elf_Word |
1658 | #undef Elf_Half | |
1659 | #undef Elf_Section | |
1660 | #undef ELF_ST_TYPE | |
9612ebc0 | 1661 | #undef XEN_NOTE_SIZE |