From: Vladimir 'phcoder' Serbinenko Date: Mon, 26 Apr 2010 11:11:43 +0000 (+0200) Subject: Unified grub-mkimage achieved X-Git-Tag: 1.99~901^2~8^2~5 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=0253aeb7a1ae7c4bc03f4fa4f4c34ff73f4d9ff4;p=thirdparty%2Fgrub.git Unified grub-mkimage achieved --- diff --git a/conf/common.rmk b/conf/common.rmk index bab7effb9..98dca34f9 100644 --- a/conf/common.rmk +++ b/conf/common.rmk @@ -18,7 +18,6 @@ endif bin_UTILITIES += grub-mkimage grub_mkimage_SOURCES = gnulib/progname.c util/grub-mkimage.c util/misc.c \ util/resolve.c lib/LzmaEnc.c lib/LzFind.c -grub_mkimage_CFLAGS = -DGRUB_KERNEL_MACHINE_LINK_ADDR=$(GRUB_KERNEL_MACHINE_LINK_ADDR) util/grub-mkimage.c_DEPENDENCIES = Makefile # For grub-probe. diff --git a/conf/i386-pc.rmk b/conf/i386-pc.rmk index 1e0a3ff58..f0da94b42 100644 --- a/conf/i386-pc.rmk +++ b/conf/i386-pc.rmk @@ -1,7 +1,5 @@ # -*- makefile -*- -GRUB_KERNEL_MACHINE_LINK_ADDR = 0x8200 - COMMON_CFLAGS = -mrtd -mregparm=3 # Images. @@ -58,7 +56,7 @@ kernel_img_HEADERS += machine/biosdisk.h machine/vga.h machine/vbe.h \ machine/pxe.h i386/pit.h kernel_img_CFLAGS = $(COMMON_CFLAGS) $(TARGET_IMG_CFLAGS) kernel_img_ASFLAGS = $(COMMON_ASFLAGS) -kernel_img_LDFLAGS += $(COMMON_LDFLAGS) $(TARGET_IMG_LDFLAGS)$(GRUB_KERNEL_MACHINE_LINK_ADDR) $(COMMON_CFLAGS) +kernel_img_LDFLAGS += $(COMMON_LDFLAGS) $(TARGET_IMG_LDFLAGS)0x8200 $(COMMON_CFLAGS) # Utilities. sbin_UTILITIES = grub-setup diff --git a/include/grub/efi/pe32.h b/include/grub/efi/pe32.h index 9d9edc220..90f9c9f5c 100644 --- a/include/grub/efi/pe32.h +++ b/include/grub/efi/pe32.h @@ -199,15 +199,8 @@ struct grub_pe64_optional_header struct grub_pe32_data_directory reserved_entry; }; -#if GRUB_TARGET_SIZEOF_VOID_P == 4 - #define GRUB_PE32_PE32_MAGIC 0x10b - -#else - -#define GRUB_PE32_PE32_MAGIC 0x20b - -#endif +#define GRUB_PE32_PE64_MAGIC 0x20b #define GRUB_PE32_SUBSYSTEM_EFI_APPLICATION 10 diff --git a/include/grub/kernel.h b/include/grub/kernel.h index bf52ffcb4..f7740b2e3 100644 --- a/include/grub/kernel.h +++ b/include/grub/kernel.h @@ -42,19 +42,33 @@ struct grub_module_header /* "gmim" (GRUB Module Info Magic). */ #define GRUB_MODULE_MAGIC 0x676d696d -struct grub_module_info +struct grub_module_info32 +{ + /* Magic number so we know we have modules present. */ + grub_uint32_t magic; + /* The offset of the modules. */ + grub_uint32_t offset; + /* The size of all modules plus this header. */ + grub_uint32_t size; +}; + +struct grub_module_info64 { /* Magic number so we know we have modules present. */ grub_uint32_t magic; -#if GRUB_TARGET_SIZEOF_VOID_P == 8 grub_uint32_t padding; -#endif /* The offset of the modules. */ - grub_target_off_t offset; + grub_uint64_t offset; /* The size of all modules plus this header. */ - grub_target_size_t size; + grub_uint64_t size; }; +#if GRUB_TARGET_SIZEOF_VOID_P == 8 +#define grub_module_info grub_module_info64 +#else +#define grub_module_info grub_module_info32 +#endif + extern grub_addr_t grub_arch_modules_addr (void); extern void EXPORT_FUNC(grub_module_iterate) (int (*hook) (struct grub_module_header *)); diff --git a/include/grub/offsets.h b/include/grub/offsets.h index a0c2f61f9..2ba6351ca 100644 --- a/include/grub/offsets.h +++ b/include/grub/offsets.h @@ -46,6 +46,8 @@ /* The segment where the kernel is loaded. */ #define GRUB_BOOT_I386_PC_KERNEL_SEG 0x800 +#define GRUB_KERNEL_I386_PC_LINK_ADDR 0x8200 + /* The upper memory area (starting at 640 kiB). */ #define GRUB_MEMORY_I386_PC_UPPER 0xa0000 #define GRUB_MEMORY_I386_QEMU_UPPER GRUB_MEMORY_I386_PC_UPPER diff --git a/util/grub-mkimage.c b/util/grub-mkimage.c index 08003a07f..c9bafdd8b 100644 --- a/util/grub-mkimage.c +++ b/util/grub-mkimage.c @@ -131,6 +131,9 @@ struct image_target_desc image_targets[] = #define grub_target_to_host32(x) (grub_target_to_host32_real (image_target, (x))) #define grub_host_to_target32(x) (grub_host_to_target32_real (image_target, (x))) +#define grub_target_to_host64(x) (grub_target_to_host64_real (image_target, (x))) +#define grub_host_to_target64(x) (grub_host_to_target64_real (image_target, (x))) +#define grub_host_to_target_addr(x) (grub_host_to_target_addr_real (image_target, (x))) #define grub_target_to_host16(x) (grub_target_to_host16_real (image_target, (x))) #define grub_host_to_target16(x) (grub_host_to_target16_real (image_target, (x))) @@ -152,13 +155,13 @@ grub_target_to_host64_real (struct image_target_desc *image_target, grub_uint64_ return grub_le_to_cpu64 (in); } -static inline grub_uint32_t -grub_target_to_host_real (struct image_target_desc *image_target, grub_uint32_t in) +static inline grub_uint64_t +grub_host_to_target64_real (struct image_target_desc *image_target, grub_uint64_t in) { - if (image_target->voidp_sizeof == 8) - return grub_target_to_host64_real (image_target, in); + if (image_target->bigendian) + return grub_cpu_to_be64 (in); else - return grub_target_to_host32_real (image_target, in); + return grub_cpu_to_le64 (in); } static inline grub_uint32_t @@ -188,6 +191,24 @@ grub_host_to_target16_real (struct image_target_desc *image_target, grub_uint16_ return grub_cpu_to_le16 (in); } +static inline grub_uint32_t +grub_host_to_target_addr_real (struct image_target_desc *image_target, grub_uint32_t in) +{ + if (image_target->voidp_sizeof == 8) + return grub_host_to_target64_real (image_target, in); + else + return grub_host_to_target32_real (image_target, in); +} + +static inline grub_uint32_t +grub_target_to_host_real (struct image_target_desc *image_target, grub_uint32_t in) +{ + if (image_target->voidp_sizeof == 8) + return grub_target_to_host64_real (image_target, in); + else + return grub_target_to_host32_real (image_target, in); +} + #define GRUB_IEEE1275_NOTE_NAME "PowerPC" #define GRUB_IEEE1275_NOTE_TYPE 0x1275 @@ -274,230 +295,6 @@ compress_kernel (struct image_target_desc *image_target, char *kernel_img, *core_size = kernel_size; } - -/* Relocate symbols; note that this function overwrites the symbol table. - Return the address of a start symbol. */ -static Elf_Addr -relocate_symbols (Elf_Ehdr *e, Elf_Shdr *sections, - Elf_Shdr *symtab_section, Elf_Addr *section_addresses, - Elf_Half section_entsize, Elf_Half num_sections, - struct image_target_desc *image_target) -{ - Elf_Word symtab_size, sym_size, num_syms; - Elf_Off symtab_offset; - Elf_Addr start_address = 0; - Elf_Sym *sym; - Elf_Word i; - Elf_Shdr *strtab_section; - const char *strtab; - - strtab_section - = (Elf_Shdr *) ((char *) sections - + (grub_target_to_host32 (symtab_section->sh_link) - * section_entsize)); - strtab = (char *) e + grub_target_to_host32 (strtab_section->sh_offset); - - symtab_size = grub_target_to_host32 (symtab_section->sh_size); - sym_size = grub_target_to_host32 (symtab_section->sh_entsize); - symtab_offset = grub_target_to_host32 (symtab_section->sh_offset); - num_syms = symtab_size / sym_size; - - for (i = 0, sym = (Elf_Sym *) ((char *) e + symtab_offset); - i < num_syms; - i++, sym = (Elf_Sym *) ((char *) sym + sym_size)) - { - Elf_Section index; - const char *name; - - name = strtab + grub_target_to_host32 (sym->st_name); - - index = grub_target_to_host16 (sym->st_shndx); - if (index == STN_ABS) - { - continue; - } - else if ((index == STN_UNDEF)) - { - if (sym->st_name) - grub_util_error ("undefined symbol %s", name); - else - continue; - } - else if (index >= num_sections) - grub_util_error ("section %d does not exist", index); - - sym->st_value = (grub_target_to_host32 (sym->st_value) - + section_addresses[index]); - grub_util_info ("locating %s at 0x%x", name, sym->st_value); - - if (! start_address) - if (strcmp (name, "_start") == 0 || strcmp (name, "start") == 0) - start_address = sym->st_value; - } - - return start_address; -} - -/* Return the address of a symbol at the index I in the section S. */ -static Elf_Addr -get_symbol_address (Elf_Ehdr *e, Elf_Shdr *s, Elf_Word i, - struct image_target_desc *image_target) -{ - Elf_Sym *sym; - - sym = (Elf_Sym *) ((char *) e - + grub_target_to_host32 (s->sh_offset) - + i * grub_target_to_host32 (s->sh_entsize)); - return sym->st_value; -} - -/* Return the address of a modified value. */ -static Elf_Addr * -get_target_address (Elf_Ehdr *e, Elf_Shdr *s, Elf_Addr offset, - struct image_target_desc *image_target) -{ - return (Elf_Addr *) ((char *) e + grub_target_to_host32 (s->sh_offset) + offset); -} - -/* Deal with relocation information. This function relocates addresses - within the virtual address space starting from 0. So only relative - addresses can be fully resolved. Absolute addresses must be relocated - again by a PE32 relocator when loaded. */ -static void -relocate_addresses (Elf_Ehdr *e, Elf_Shdr *sections, - Elf_Addr *section_addresses, - Elf_Half section_entsize, Elf_Half num_sections, - const char *strtab, struct image_target_desc *image_target) -{ - Elf_Half i; - Elf_Shdr *s; - - for (i = 0, s = sections; - i < num_sections; - i++, s = (Elf_Shdr *) ((char *) s + section_entsize)) - if ((s->sh_type == grub_host_to_target32 (SHT_REL)) || - (s->sh_type == grub_host_to_target32 (SHT_RELA))) - { - Elf_Rela *r; - Elf_Word rtab_size, r_size, num_rs; - Elf_Off rtab_offset; - Elf_Shdr *symtab_section; - Elf_Word target_section_index; - Elf_Addr target_section_addr; - Elf_Shdr *target_section; - Elf_Word j; - - symtab_section = (Elf_Shdr *) ((char *) sections - + (grub_target_to_host32 (s->sh_link) - * section_entsize)); - target_section_index = grub_target_to_host32 (s->sh_info); - target_section_addr = section_addresses[target_section_index]; - target_section = (Elf_Shdr *) ((char *) sections - + (target_section_index - * section_entsize)); - - grub_util_info ("dealing with the relocation section %s for %s", - strtab + grub_target_to_host32 (s->sh_name), - strtab + grub_target_to_host32 (target_section->sh_name)); - - rtab_size = grub_target_to_host32 (s->sh_size); - r_size = grub_target_to_host32 (s->sh_entsize); - rtab_offset = grub_target_to_host32 (s->sh_offset); - num_rs = rtab_size / r_size; - - for (j = 0, r = (Elf_Rela *) ((char *) e + rtab_offset); - j < num_rs; - j++, r = (Elf_Rela *) ((char *) r + r_size)) - { - Elf_Addr info; - Elf_Addr offset; - Elf_Addr sym_addr; - Elf_Addr *target; - Elf_Addr addend; - - offset = grub_target_to_host (r->r_offset); - target = get_target_address (e, target_section, offset, image_target); - info = grub_target_to_host (r->r_info); - sym_addr = get_symbol_address (e, symtab_section, - ELF_R_SYM (info), image_target); - - addend = (s->sh_type == grub_target_to_host32 (SHT_RELA)) ? - r->r_addend : 0; - - if (image_target->voidp_sizeof == 4) - switch (ELF_R_TYPE (info)) - { - case R_386_NONE: - break; - - case R_386_32: - /* This is absolute. */ - *target = grub_host_to_target32 (grub_target_to_host32 (*target) - + addend + sym_addr); - grub_util_info ("relocating an R_386_32 entry to 0x%x at the offset 0x%x", - *target, offset); - break; - - case R_386_PC32: - /* This is relative. */ - *target = grub_host_to_target32 (grub_target_to_host32 (*target) - + addend + sym_addr - - target_section_addr - offset - - image_target->vaddr_offset); - grub_util_info ("relocating an R_386_PC32 entry to 0x%x at the offset 0x%x", - *target, offset); - break; - default: - grub_util_error ("unknown relocation type %d", - ELF_R_TYPE (info)); - break; - } - else - switch (ELF_R_TYPE (info)) - { - - case R_X86_64_NONE: - break; - - case R_X86_64_64: - *target = grub_host_to_target64 (grub_target_to_host64 (*target) - + addend + sym_addr); - grub_util_info ("relocating an R_X86_64_64 entry to 0x%llx at the offset 0x%llx", - *target, offset); - break; - - case R_X86_64_PC32: - { - grub_uint32_t *t32 = (grub_uint32_t *) target; - *t32 = grub_host_to_target64 (grub_target_to_host32 (*t32) - + addend + sym_addr - - target_section_addr - offset - - image_target->vaddr_offset); - grub_util_info ("relocating an R_X86_64_PC32 entry to 0x%x at the offset 0x%llx", - *t32, offset); - break; - } - - case R_X86_64_32: - case R_X86_64_32S: - { - grub_uint32_t *t32 = (grub_uint32_t *) target; - *t32 = grub_host_to_target64 (grub_target_to_host32 (*t32) - + addend + sym_addr); - grub_util_info ("relocating an R_X86_64_32(S) entry to 0x%x at the offset 0x%llx", - *t32, offset); - break; - } - - default: - grub_util_error ("unknown relocation type %d", - ELF_R_TYPE (info)); - break; - } - } - } -} - struct fixup_block_list { struct fixup_block_list *next; @@ -505,457 +302,13 @@ struct fixup_block_list struct grub_pe32_fixup_block b; }; -/* Add a PE32's fixup entry for a relocation. Return the resulting address - after having written to the file OUT. */ -Elf_Addr -add_fixup_entry (struct fixup_block_list **cblock, grub_uint16_t type, - Elf_Addr addr, int flush, Elf_Addr current_address, - struct image_target_desc *image_target) -{ - struct grub_pe32_fixup_block *b; - - b = &((*cblock)->b); - - /* First, check if it is necessary to write out the current block. */ - if ((*cblock)->state) - { - if (flush || addr < b->page_rva || b->page_rva + 0x1000 <= addr) - { - grub_uint32_t size; - - if (flush) - { - /* Add as much padding as necessary to align the address - with a section boundary. */ - Elf_Addr next_address; - unsigned padding_size; - size_t index; - - next_address = current_address + b->block_size; - padding_size = ((ALIGN_UP (next_address, image_target->section_align) - - next_address) - >> 1); - index = ((b->block_size - sizeof (*b)) >> 1); - grub_util_info ("adding %d padding fixup entries", padding_size); - while (padding_size--) - { - b->entries[index++] = 0; - b->block_size += 2; - } - } - else if (b->block_size & (8 - 1)) - { - /* If not aligned with a 32-bit boundary, add - a padding entry. */ - size_t index; - - grub_util_info ("adding a padding fixup entry"); - index = ((b->block_size - sizeof (*b)) >> 1); - b->entries[index] = 0; - b->block_size += 2; - } - - /* Flush it. */ - grub_util_info ("writing %d bytes of a fixup block starting at 0x%x", - b->block_size, b->page_rva); - size = b->block_size; - current_address += size; - b->page_rva = grub_host_to_target32 (b->page_rva); - b->block_size = grub_host_to_target32 (b->block_size); - (*cblock)->next = xmalloc (sizeof (**cblock) + 2 * 0x1000); - memset ((*cblock)->next, 0, sizeof (**cblock) + 2 * 0x1000); - *cblock = (*cblock)->next; - } - } - - b = &((*cblock)->b); - - if (! flush) - { - grub_uint16_t entry; - size_t index; - - /* If not allocated yet, allocate a block with enough entries. */ - if (! (*cblock)->state) - { - (*cblock)->state = 1; - - /* The spec does not mention the requirement of a Page RVA. - Here, align the address with a 4K boundary for safety. */ - b->page_rva = (addr & ~(0x1000 - 1)); - b->block_size = sizeof (*b); - } - - /* Sanity check. */ - if (b->block_size >= sizeof (*b) + 2 * 0x1000) - grub_util_error ("too many fixup entries"); - - /* Add a new entry. */ - index = ((b->block_size - sizeof (*b)) >> 1); - entry = GRUB_PE32_FIXUP_ENTRY (type, addr - b->page_rva); - b->entries[index] = grub_host_to_target16 (entry); - b->block_size += 2; - } - - return current_address; -} - -/* Make a .reloc section. */ -static Elf_Addr -make_reloc_section (Elf_Ehdr *e, void **out, - Elf_Addr *section_addresses, Elf_Shdr *sections, - Elf_Half section_entsize, Elf_Half num_sections, - const char *strtab, struct image_target_desc *image_target) -{ - Elf_Half i; - Elf_Shdr *s; - struct fixup_block_list *lst, *lst0; - Elf_Addr current_address = 0; - - lst = lst0 = xmalloc (sizeof (*lst) + 2 * 0x1000); - memset (lst, 0, sizeof (*lst) + 2 * 0x1000); - - for (i = 0, s = sections; - i < num_sections; - i++, s = (Elf_Shdr *) ((char *) s + section_entsize)) - if ((s->sh_type == grub_cpu_to_le32 (SHT_REL)) || - (s->sh_type == grub_cpu_to_le32 (SHT_RELA))) - { - Elf_Rel *r; - Elf_Word rtab_size, r_size, num_rs; - Elf_Off rtab_offset; - Elf_Addr section_address; - Elf_Word j; - - grub_util_info ("translating the relocation section %s", - strtab + grub_le_to_cpu32 (s->sh_name)); - - rtab_size = grub_le_to_cpu32 (s->sh_size); - r_size = grub_le_to_cpu32 (s->sh_entsize); - rtab_offset = grub_le_to_cpu32 (s->sh_offset); - num_rs = rtab_size / r_size; - - section_address = section_addresses[grub_le_to_cpu32 (s->sh_info)]; - - for (j = 0, r = (Elf_Rel *) ((char *) e + rtab_offset); - j < num_rs; - j++, r = (Elf_Rel *) ((char *) r + r_size)) - { - Elf_Addr info; - Elf_Addr offset; - - offset = grub_le_to_cpu32 (r->r_offset); - info = grub_le_to_cpu32 (r->r_info); - - /* Necessary to relocate only absolute addresses. */ - if (image_target->voidp_sizeof == 4) - { - if (ELF_R_TYPE (info) == R_386_32) - { - Elf_Addr addr; - - addr = section_address + offset; - grub_util_info ("adding a relocation entry for 0x%x", addr); - current_address = add_fixup_entry (&lst, - GRUB_PE32_REL_BASED_HIGHLOW, - addr, 0, current_address, - image_target); - } - } - else - { - if ((ELF_R_TYPE (info) == R_X86_64_32) || - (ELF_R_TYPE (info) == R_X86_64_32S)) - { - grub_util_error ("can\'t add fixup entry for R_X86_64_32(S)"); - } - else if (ELF_R_TYPE (info) == R_X86_64_64) - { - Elf_Addr addr; - - addr = section_address + offset; - grub_util_info ("adding a relocation entry for 0x%llx", addr); - current_address = add_fixup_entry (&lst, - GRUB_PE32_REL_BASED_DIR64, - addr, - 0, current_address, - image_target); - } - } - } - } - - current_address = add_fixup_entry (&lst, 0, 0, 1, current_address, image_target); - - { - grub_uint8_t *ptr; - ptr = *out = xmalloc (current_address); - for (lst = lst0; lst; lst = lst->next) - if (lst->state) - { - memcpy (ptr, &lst->b, grub_target_to_host32 (lst->b.block_size)); - ptr += grub_target_to_host32 (lst->b.block_size); - } - if (current_address + *out != ptr) - { - grub_util_error ("Bug detected %d != %d\n", ptr - (grub_uint8_t *) *out, - current_address); - } - } - - return current_address; -} - -/* Determine if this section is a text section. Return false if this - section is not allocated. */ -static int -is_text_section (Elf_Shdr *s, struct image_target_desc *image_target) -{ - if (image_target->id != IMAGE_EFI - && grub_target_to_host32 (s->sh_type) != SHT_PROGBITS) - return 0; - return ((grub_target_to_host32 (s->sh_flags) & (SHF_EXECINSTR | SHF_ALLOC)) - == (SHF_EXECINSTR | SHF_ALLOC)); -} - -/* Determine if this section is a data section. This assumes that - BSS is also a data section, since the converter initializes BSS - when producing PE32 to avoid a bug in EFI implementations. */ -static int -is_data_section (Elf_Shdr *s, struct image_target_desc *image_target) -{ - if (image_target->id != IMAGE_EFI - && grub_target_to_host32 (s->sh_type) != SHT_PROGBITS) - return 0; - return ((grub_target_to_host32 (s->sh_flags) & (SHF_EXECINSTR | SHF_ALLOC)) - == SHF_ALLOC); -} - -/* Locate section addresses by merging code sections and data sections - into .text and .data, respectively. Return the array of section - addresses. */ -static Elf_Addr * -locate_sections (Elf_Shdr *sections, Elf_Half section_entsize, - Elf_Half num_sections, const char *strtab, - grub_size_t *exec_size, grub_size_t *kernel_sz, - struct image_target_desc *image_target) -{ - int i; - Elf_Addr current_address; - Elf_Addr *section_addresses; - Elf_Shdr *s; - - section_addresses = xmalloc (sizeof (*section_addresses) * num_sections); - memset (section_addresses, 0, sizeof (*section_addresses) * num_sections); - - current_address = 0; - - /* .text */ - for (i = 0, s = sections; - i < num_sections; - i++, s = (Elf_Shdr *) ((char *) s + section_entsize)) - if (is_text_section (s, image_target)) - { - Elf_Word align = grub_host_to_target32 (s->sh_addralign); - const char *name = strtab + grub_host_to_target32 (s->sh_name); - - if (align) - current_address = ALIGN_UP (current_address + image_target->vaddr_offset, - align) - image_target->vaddr_offset; - - grub_util_info ("locating the section %s at 0x%x", - name, current_address); - section_addresses[i] = current_address; - current_address += grub_host_to_target32 (s->sh_size); - } - - current_address = ALIGN_UP (current_address + image_target->vaddr_offset, - image_target->section_align) - - image_target->vaddr_offset; - *exec_size = current_address; - - /* .data */ - for (i = 0, s = sections; - i < num_sections; - i++, s = (Elf_Shdr *) ((char *) s + section_entsize)) - if (is_data_section (s, image_target)) - { - Elf_Word align = grub_host_to_target32 (s->sh_addralign); - const char *name = strtab + grub_host_to_target32 (s->sh_name); - - if (align) - current_address = ALIGN_UP (current_address + image_target->vaddr_offset, - align) - - image_target->vaddr_offset; - - grub_util_info ("locating the section %s at 0x%x", - name, current_address); - section_addresses[i] = current_address; - current_address += grub_host_to_target32 (s->sh_size); - } - - current_address = ALIGN_UP (current_address + image_target->vaddr_offset, - image_target->section_align) - image_target->vaddr_offset; - *kernel_sz = current_address; - return section_addresses; -} - -/* Return if the ELF header is valid. */ -static int -check_elf_header (Elf_Ehdr *e, size_t size, struct image_target_desc *image_target) -{ - if (size < sizeof (*e) - || e->e_ident[EI_MAG0] != ELFMAG0 - || e->e_ident[EI_MAG1] != ELFMAG1 - || e->e_ident[EI_MAG2] != ELFMAG2 - || e->e_ident[EI_MAG3] != ELFMAG3 - || e->e_ident[EI_VERSION] != EV_CURRENT - || e->e_version != grub_host_to_target32 (EV_CURRENT)) - return 0; - - return 1; -} - -static char * -load_image (const char *kernel_path, grub_size_t *exec_size, - grub_size_t *kernel_sz, grub_size_t *bss_size, - grub_size_t total_module_size, Elf_Addr *start, - void **reloc_section, grub_size_t *reloc_size, - struct image_target_desc *image_target) -{ - char *kernel_img, *out_img; - const char *strtab; - Elf_Ehdr *e; - Elf_Shdr *sections; - Elf_Addr *section_addresses; - Elf_Addr *section_vaddresses; - int i; - Elf_Shdr *s; - Elf_Half num_sections; - Elf_Off section_offset; - Elf_Half section_entsize; - grub_size_t kernel_size; - Elf_Shdr *symtab_section; - - *start = 0; - - kernel_size = grub_util_get_image_size (kernel_path); - kernel_img = xmalloc (kernel_size); - grub_util_load_image (kernel_path, kernel_img); - - e = (Elf_Ehdr *) kernel_img; - if (! check_elf_header (e, kernel_size, image_target)) - grub_util_error ("invalid ELF header"); - - section_offset = grub_target_to_host32 (e->e_shoff); - section_entsize = grub_target_to_host16 (e->e_shentsize); - num_sections = grub_target_to_host16 (e->e_shnum); - - if (kernel_size < section_offset + section_entsize * num_sections) - grub_util_error ("invalid ELF format"); - - sections = (Elf_Shdr *) (kernel_img + section_offset); - - /* Relocate sections then symbols in the virtual address space. */ - s = (Elf_Shdr *) ((char *) sections - + grub_host_to_target16 (e->e_shstrndx) * section_entsize); - strtab = (char *) e + grub_host_to_target32 (s->sh_offset); - - section_addresses = locate_sections (sections, section_entsize, - num_sections, strtab, - exec_size, kernel_sz, image_target); - - if (image_target->id == IMAGE_EFI) - { - section_vaddresses = xmalloc (sizeof (*section_addresses) * num_sections); - - for (i = 0; i < num_sections; i++) - section_vaddresses[i] = section_addresses[i] + image_target->vaddr_offset; +#define MKIMAGE_ELF32 1 +#include "grub-mkimagexx.c" +#undef MKIMAGE_ELF32 -#if 0 - { - Elf_Addr current_address = *kernel_sz; - - for (i = 0, s = sections; - i < num_sections; - i++, s = (Elf_Shdr *) ((char *) s + section_entsize)) - if (grub_target_to_host32 (s->sh_type) == SHT_NOBITS) - { - Elf_Word align = grub_host_to_target32 (s->sh_addralign); - const char *name = strtab + grub_host_to_target32 (s->sh_name); - - if (align) - current_address = ALIGN_UP (current_address + VADDR_OFFSET, align) - - VADDR_OFFSET; - - grub_util_info ("locating the section %s at 0x%x", - name, current_address); - section_vaddresses[i] = current_address + VADDR_OFFSET; - current_address += grub_host_to_target32 (s->sh_size); - } - current_address = ALIGN_UP (current_address + VADDR_OFFSET, SECTION_ALIGN) - - VADDR_OFFSET; - *bss_size = current_address - *kernel_sz; - } -#else - *bss_size = 0; -#endif - - symtab_section = NULL; - for (i = 0, s = sections; - i < num_sections; - i++, s = (Elf_Shdr *) ((char *) s + section_entsize)) - if (s->sh_type == grub_host_to_target32 (SHT_SYMTAB)) - { - symtab_section = s; - break; - } - - if (! symtab_section) - grub_util_error ("no symbol table"); - - *start = relocate_symbols (e, sections, symtab_section, - section_vaddresses, section_entsize, - num_sections, image_target); - if (*start == 0) - grub_util_error ("start symbol is not defined"); - - /* Resolve addresses in the virtual address space. */ - relocate_addresses (e, sections, section_addresses, section_entsize, - num_sections, strtab, image_target); - - *reloc_size = make_reloc_section (e, reloc_section, - section_vaddresses, sections, - section_entsize, num_sections, - strtab, image_target); - - } - else - { - *bss_size = 0; - *reloc_size = 0; - *reloc_section = NULL; - } - - out_img = xmalloc (*kernel_sz + total_module_size); - - for (i = 0, s = sections; - i < num_sections; - i++, s = (Elf_Shdr *) ((char *) s + section_entsize)) - if (is_data_section (s, image_target) || is_text_section (s, image_target)) - { - if (grub_target_to_host32 (s->sh_type) == SHT_NOBITS) - memset (out_img + section_addresses[i], 0, - grub_host_to_target32 (s->sh_size)); - else - memcpy (out_img + section_addresses[i], - kernel_img + grub_host_to_target32 (s->sh_offset), - grub_host_to_target32 (s->sh_size)); - } - free (kernel_img); - - return out_img; -} +#define MKIMAGE_ELF64 1 +#include "grub-mkimagexx.c" +#undef MKIMAGE_ELF64 static void generate_image (const char *dir, char *prefix, FILE *out, char *mods[], @@ -968,16 +321,18 @@ generate_image (const char *dir, char *prefix, FILE *out, char *mods[], char *kernel_path; size_t offset; struct grub_util_path_list *path_list, *p, *next; - struct grub_module_info *modinfo; grub_size_t bss_size; - Elf_Addr start_address; + grub_uint64_t start_address; void *rel_section; grub_size_t reloc_size; path_list = grub_util_resolve_dependencies (dir, "moddep.lst", mods); kernel_path = grub_util_get_path (dir, "kernel.img"); - total_module_size = sizeof (struct grub_module_info); + if (image_target->voidp_sizeof == 8) + total_module_size = sizeof (struct grub_module_info64); + else + total_module_size = sizeof (struct grub_module_info32); if (memdisk_path) { @@ -1006,22 +361,42 @@ generate_image (const char *dir, char *prefix, FILE *out, char *mods[], grub_util_info ("the total module size is 0x%x", total_module_size); - kernel_img = load_image (kernel_path, &exec_size, &kernel_size, &bss_size, - total_module_size, &start_address, &rel_section, - &reloc_size, image_target); + if (image_target->voidp_sizeof == 4) + kernel_img = load_image32 (kernel_path, &exec_size, &kernel_size, &bss_size, + total_module_size, &start_address, &rel_section, + &reloc_size, image_target); + else + kernel_img = load_image64 (kernel_path, &exec_size, &kernel_size, &bss_size, + total_module_size, &start_address, &rel_section, + &reloc_size, image_target); if (image_target->prefix + strlen (prefix) + 1 > image_target->data_end) grub_util_error (_("prefix is too long")); strcpy (kernel_img + image_target->prefix, prefix); - /* Fill in the grub_module_info structure. */ - modinfo = (struct grub_module_info *) (kernel_img + kernel_size); - memset (modinfo, 0, sizeof (struct grub_module_info)); - modinfo->magic = grub_host_to_target32 (GRUB_MODULE_MAGIC); - modinfo->offset = grub_host_to_target_addr (sizeof (struct grub_module_info)); - modinfo->size = grub_host_to_target_addr (total_module_size); + if (image_target->voidp_sizeof == 8) + { + /* Fill in the grub_module_info structure. */ + struct grub_module_info64 *modinfo; + modinfo = (struct grub_module_info64 *) (kernel_img + kernel_size); + memset (modinfo, 0, sizeof (struct grub_module_info64)); + modinfo->magic = grub_host_to_target32 (GRUB_MODULE_MAGIC); + modinfo->offset = grub_host_to_target_addr (sizeof (struct grub_module_info64)); + modinfo->size = grub_host_to_target_addr (total_module_size); + offset = kernel_size + sizeof (struct grub_module_info64); + } + else + { + /* Fill in the grub_module_info structure. */ + struct grub_module_info32 *modinfo; + modinfo = (struct grub_module_info32 *) (kernel_img + kernel_size); + memset (modinfo, 0, sizeof (struct grub_module_info32)); + modinfo->magic = grub_host_to_target32 (GRUB_MODULE_MAGIC); + modinfo->offset = grub_host_to_target_addr (sizeof (struct grub_module_info32)); + modinfo->size = grub_host_to_target_addr (total_module_size); + offset = kernel_size + sizeof (struct grub_module_info32); + } - offset = kernel_size + sizeof (struct grub_module_info); for (p = path_list; p; p = p->next) { struct grub_module_header *header; @@ -1119,9 +494,9 @@ generate_image (const char *dir, char *prefix, FILE *out, char *mods[], char *boot_path, *boot_img; size_t boot_size; - if (GRUB_KERNEL_MACHINE_LINK_ADDR + core_size > GRUB_MEMORY_I386_PC_UPPER) + if (GRUB_KERNEL_I386_PC_LINK_ADDR + core_size > GRUB_MEMORY_I386_PC_UPPER) grub_util_error (_("core image is too big (%p > %p)"), - GRUB_KERNEL_MACHINE_LINK_ADDR + core_size, + GRUB_KERNEL_I386_PC_LINK_ADDR + core_size, GRUB_MEMORY_I386_PC_UPPER); num = ((core_size + GRUB_DISK_SECTOR_SIZE - 1) >> GRUB_DISK_SECTOR_BITS); @@ -1258,7 +633,7 @@ generate_image (const char *dir, char *prefix, FILE *out, char *mods[], o = header + GRUB_PE32_MSDOS_STUB_SIZE + GRUB_PE32_SIGNATURE_SIZE + sizeof (struct grub_pe32_coff_header); - o->magic = grub_host_to_target16 (GRUB_PE32_PE32_MAGIC); + o->magic = grub_host_to_target16 (GRUB_PE32_PE64_MAGIC); o->code_size = grub_host_to_target32 (exec_size); o->data_size = grub_cpu_to_le32 (reloc_addr - exec_size); o->bss_size = grub_cpu_to_le32 (bss_size); @@ -1523,7 +898,7 @@ generate_image (const char *dir, char *prefix, FILE *out, char *mods[], phdr[1].p_type = grub_host_to_target32 (PT_NOTE); phdr[1].p_flags = grub_host_to_target32 (PF_R); - phdr[1].p_align = grub_host_to_target32 (GRUB_TARGET_SIZEOF_LONG); + phdr[1].p_align = grub_host_to_target32 (image_target->voidp_sizeof); phdr[1].p_vaddr = 0; phdr[1].p_paddr = 0; phdr[1].p_filesz = grub_host_to_target32 (note_size); diff --git a/util/grub-mkimagexx.c b/util/grub-mkimagexx.c new file mode 100644 index 000000000..eb0c7a514 --- /dev/null +++ b/util/grub-mkimagexx.c @@ -0,0 +1,741 @@ +/* grub-mkimage.c - make a bootable image */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2002,2003,2004,2005,2006,2007,2008,2009,2010 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#if defined(MKIMAGE_ELF32) +# define SUFFIX(x) x ## 32 +# define ELFCLASSXX ELFCLASS32 +# define Elf_Ehdr Elf32_Ehdr +# define Elf_Phdr Elf32_Phdr +# define Elf_Addr Elf32_Addr +# define Elf_Sym Elf32_Sym +# define Elf_Off Elf32_Off +# define Elf_Shdr Elf32_Shdr +# define Elf_Rela Elf32_Rela +# define Elf_Rel Elf32_Rel +# define ELF_R_SYM(val) ELF32_R_SYM(val) +# define ELF_R_TYPE(val) ELF32_R_TYPE(val) +#elif defined(MKIMAGE_ELF64) +# define SUFFIX(x) x ## 64 +# define ELFCLASSXX ELFCLASS64 +# define Elf_Ehdr Elf64_Ehdr +# define Elf_Phdr Elf64_Phdr +# define Elf_Addr Elf64_Addr +# define Elf_Sym Elf64_Sym +# define Elf_Off Elf64_Off +# define Elf_Shdr Elf64_Shdr +# define Elf_Rela Elf64_Rela +# define Elf_Rel Elf64_Rel +# define ELF_R_SYM(val) ELF64_R_SYM(val) +# define ELF_R_TYPE(val) ELF64_R_TYPE(val) +#else +#error "I'm confused" +#endif + +/* Relocate symbols; note that this function overwrites the symbol table. + Return the address of a start symbol. */ +static Elf_Addr +SUFFIX (relocate_symbols) (Elf_Ehdr *e, Elf_Shdr *sections, + Elf_Shdr *symtab_section, Elf_Addr *section_addresses, + Elf_Half section_entsize, Elf_Half num_sections, + struct image_target_desc *image_target) +{ + Elf_Word symtab_size, sym_size, num_syms; + Elf_Off symtab_offset; + Elf_Addr start_address = 0; + Elf_Sym *sym; + Elf_Word i; + Elf_Shdr *strtab_section; + const char *strtab; + + strtab_section + = (Elf_Shdr *) ((char *) sections + + (grub_target_to_host32 (symtab_section->sh_link) + * section_entsize)); + strtab = (char *) e + grub_target_to_host32 (strtab_section->sh_offset); + + symtab_size = grub_target_to_host32 (symtab_section->sh_size); + sym_size = grub_target_to_host32 (symtab_section->sh_entsize); + symtab_offset = grub_target_to_host32 (symtab_section->sh_offset); + num_syms = symtab_size / sym_size; + + for (i = 0, sym = (Elf_Sym *) ((char *) e + symtab_offset); + i < num_syms; + i++, sym = (Elf_Sym *) ((char *) sym + sym_size)) + { + Elf_Section index; + const char *name; + + name = strtab + grub_target_to_host32 (sym->st_name); + + index = grub_target_to_host16 (sym->st_shndx); + if (index == STN_ABS) + { + continue; + } + else if ((index == STN_UNDEF)) + { + if (sym->st_name) + grub_util_error ("undefined symbol %s", name); + else + continue; + } + else if (index >= num_sections) + grub_util_error ("section %d does not exist", index); + + sym->st_value = (grub_target_to_host32 (sym->st_value) + + section_addresses[index]); + grub_util_info ("locating %s at 0x%x", name, sym->st_value); + + if (! start_address) + if (strcmp (name, "_start") == 0 || strcmp (name, "start") == 0) + start_address = sym->st_value; + } + + return start_address; +} + +/* Return the address of a symbol at the index I in the section S. */ +static Elf_Addr +SUFFIX (get_symbol_address) (Elf_Ehdr *e, Elf_Shdr *s, Elf_Word i, + struct image_target_desc *image_target) +{ + Elf_Sym *sym; + + sym = (Elf_Sym *) ((char *) e + + grub_target_to_host32 (s->sh_offset) + + i * grub_target_to_host32 (s->sh_entsize)); + return sym->st_value; +} + +/* Return the address of a modified value. */ +static Elf_Addr * +SUFFIX (get_target_address) (Elf_Ehdr *e, Elf_Shdr *s, Elf_Addr offset, + struct image_target_desc *image_target) +{ + return (Elf_Addr *) ((char *) e + grub_target_to_host32 (s->sh_offset) + offset); +} + +/* Deal with relocation information. This function relocates addresses + within the virtual address space starting from 0. So only relative + addresses can be fully resolved. Absolute addresses must be relocated + again by a PE32 relocator when loaded. */ +static void +SUFFIX (relocate_addresses) (Elf_Ehdr *e, Elf_Shdr *sections, + Elf_Addr *section_addresses, + Elf_Half section_entsize, Elf_Half num_sections, + const char *strtab, struct image_target_desc *image_target) +{ + Elf_Half i; + Elf_Shdr *s; + + for (i = 0, s = sections; + i < num_sections; + i++, s = (Elf_Shdr *) ((char *) s + section_entsize)) + if ((s->sh_type == grub_host_to_target32 (SHT_REL)) || + (s->sh_type == grub_host_to_target32 (SHT_RELA))) + { + Elf_Rela *r; + Elf_Word rtab_size, r_size, num_rs; + Elf_Off rtab_offset; + Elf_Shdr *symtab_section; + Elf_Word target_section_index; + Elf_Addr target_section_addr; + Elf_Shdr *target_section; + Elf_Word j; + + symtab_section = (Elf_Shdr *) ((char *) sections + + (grub_target_to_host32 (s->sh_link) + * section_entsize)); + target_section_index = grub_target_to_host32 (s->sh_info); + target_section_addr = section_addresses[target_section_index]; + target_section = (Elf_Shdr *) ((char *) sections + + (target_section_index + * section_entsize)); + + grub_util_info ("dealing with the relocation section %s for %s", + strtab + grub_target_to_host32 (s->sh_name), + strtab + grub_target_to_host32 (target_section->sh_name)); + + rtab_size = grub_target_to_host32 (s->sh_size); + r_size = grub_target_to_host32 (s->sh_entsize); + rtab_offset = grub_target_to_host32 (s->sh_offset); + num_rs = rtab_size / r_size; + + for (j = 0, r = (Elf_Rela *) ((char *) e + rtab_offset); + j < num_rs; + j++, r = (Elf_Rela *) ((char *) r + r_size)) + { + Elf_Addr info; + Elf_Addr offset; + Elf_Addr sym_addr; + Elf_Addr *target; + Elf_Addr addend; + + offset = grub_target_to_host (r->r_offset); + target = SUFFIX (get_target_address) (e, target_section, + offset, image_target); + info = grub_target_to_host (r->r_info); + sym_addr = SUFFIX (get_symbol_address) (e, symtab_section, + ELF_R_SYM (info), image_target); + + addend = (s->sh_type == grub_target_to_host32 (SHT_RELA)) ? + r->r_addend : 0; + + if (image_target->voidp_sizeof == 4) + switch (ELF_R_TYPE (info)) + { + case R_386_NONE: + break; + + case R_386_32: + /* This is absolute. */ + *target = grub_host_to_target32 (grub_target_to_host32 (*target) + + addend + sym_addr); + grub_util_info ("relocating an R_386_32 entry to 0x%x at the offset 0x%x", + *target, offset); + break; + + case R_386_PC32: + /* This is relative. */ + *target = grub_host_to_target32 (grub_target_to_host32 (*target) + + addend + sym_addr + - target_section_addr - offset + - image_target->vaddr_offset); + grub_util_info ("relocating an R_386_PC32 entry to 0x%x at the offset 0x%x", + *target, offset); + break; + default: + grub_util_error ("unknown relocation type %d", + ELF_R_TYPE (info)); + break; + } + else + switch (ELF_R_TYPE (info)) + { + + case R_X86_64_NONE: + break; + + case R_X86_64_64: + *target = grub_host_to_target64 (grub_target_to_host64 (*target) + + addend + sym_addr); + grub_util_info ("relocating an R_X86_64_64 entry to 0x%llx at the offset 0x%llx", + *target, offset); + break; + + case R_X86_64_PC32: + { + grub_uint32_t *t32 = (grub_uint32_t *) target; + *t32 = grub_host_to_target64 (grub_target_to_host32 (*t32) + + addend + sym_addr + - target_section_addr - offset + - image_target->vaddr_offset); + grub_util_info ("relocating an R_X86_64_PC32 entry to 0x%x at the offset 0x%llx", + *t32, offset); + break; + } + + case R_X86_64_32: + case R_X86_64_32S: + { + grub_uint32_t *t32 = (grub_uint32_t *) target; + *t32 = grub_host_to_target64 (grub_target_to_host32 (*t32) + + addend + sym_addr); + grub_util_info ("relocating an R_X86_64_32(S) entry to 0x%x at the offset 0x%llx", + *t32, offset); + break; + } + + default: + grub_util_error ("unknown relocation type %d", + ELF_R_TYPE (info)); + break; + } + } + } +} + +/* Add a PE32's fixup entry for a relocation. Return the resulting address + after having written to the file OUT. */ +static Elf_Addr +SUFFIX (add_fixup_entry) (struct fixup_block_list **cblock, grub_uint16_t type, + Elf_Addr addr, int flush, Elf_Addr current_address, + struct image_target_desc *image_target) +{ + struct grub_pe32_fixup_block *b; + + b = &((*cblock)->b); + + /* First, check if it is necessary to write out the current block. */ + if ((*cblock)->state) + { + if (flush || addr < b->page_rva || b->page_rva + 0x1000 <= addr) + { + grub_uint32_t size; + + if (flush) + { + /* Add as much padding as necessary to align the address + with a section boundary. */ + Elf_Addr next_address; + unsigned padding_size; + size_t index; + + next_address = current_address + b->block_size; + padding_size = ((ALIGN_UP (next_address, image_target->section_align) + - next_address) + >> 1); + index = ((b->block_size - sizeof (*b)) >> 1); + grub_util_info ("adding %d padding fixup entries", padding_size); + while (padding_size--) + { + b->entries[index++] = 0; + b->block_size += 2; + } + } + else if (b->block_size & (8 - 1)) + { + /* If not aligned with a 32-bit boundary, add + a padding entry. */ + size_t index; + + grub_util_info ("adding a padding fixup entry"); + index = ((b->block_size - sizeof (*b)) >> 1); + b->entries[index] = 0; + b->block_size += 2; + } + + /* Flush it. */ + grub_util_info ("writing %d bytes of a fixup block starting at 0x%x", + b->block_size, b->page_rva); + size = b->block_size; + current_address += size; + b->page_rva = grub_host_to_target32 (b->page_rva); + b->block_size = grub_host_to_target32 (b->block_size); + (*cblock)->next = xmalloc (sizeof (**cblock) + 2 * 0x1000); + memset ((*cblock)->next, 0, sizeof (**cblock) + 2 * 0x1000); + *cblock = (*cblock)->next; + } + } + + b = &((*cblock)->b); + + if (! flush) + { + grub_uint16_t entry; + size_t index; + + /* If not allocated yet, allocate a block with enough entries. */ + if (! (*cblock)->state) + { + (*cblock)->state = 1; + + /* The spec does not mention the requirement of a Page RVA. + Here, align the address with a 4K boundary for safety. */ + b->page_rva = (addr & ~(0x1000 - 1)); + b->block_size = sizeof (*b); + } + + /* Sanity check. */ + if (b->block_size >= sizeof (*b) + 2 * 0x1000) + grub_util_error ("too many fixup entries"); + + /* Add a new entry. */ + index = ((b->block_size - sizeof (*b)) >> 1); + entry = GRUB_PE32_FIXUP_ENTRY (type, addr - b->page_rva); + b->entries[index] = grub_host_to_target16 (entry); + b->block_size += 2; + } + + return current_address; +} + +/* Make a .reloc section. */ +static Elf_Addr +SUFFIX (make_reloc_section) (Elf_Ehdr *e, void **out, + Elf_Addr *section_addresses, Elf_Shdr *sections, + Elf_Half section_entsize, Elf_Half num_sections, + const char *strtab, struct image_target_desc *image_target) +{ + Elf_Half i; + Elf_Shdr *s; + struct fixup_block_list *lst, *lst0; + Elf_Addr current_address = 0; + + lst = lst0 = xmalloc (sizeof (*lst) + 2 * 0x1000); + memset (lst, 0, sizeof (*lst) + 2 * 0x1000); + + for (i = 0, s = sections; + i < num_sections; + i++, s = (Elf_Shdr *) ((char *) s + section_entsize)) + if ((s->sh_type == grub_cpu_to_le32 (SHT_REL)) || + (s->sh_type == grub_cpu_to_le32 (SHT_RELA))) + { + Elf_Rel *r; + Elf_Word rtab_size, r_size, num_rs; + Elf_Off rtab_offset; + Elf_Addr section_address; + Elf_Word j; + + grub_util_info ("translating the relocation section %s", + strtab + grub_le_to_cpu32 (s->sh_name)); + + rtab_size = grub_le_to_cpu32 (s->sh_size); + r_size = grub_le_to_cpu32 (s->sh_entsize); + rtab_offset = grub_le_to_cpu32 (s->sh_offset); + num_rs = rtab_size / r_size; + + section_address = section_addresses[grub_le_to_cpu32 (s->sh_info)]; + + for (j = 0, r = (Elf_Rel *) ((char *) e + rtab_offset); + j < num_rs; + j++, r = (Elf_Rel *) ((char *) r + r_size)) + { + Elf_Addr info; + Elf_Addr offset; + + offset = grub_le_to_cpu32 (r->r_offset); + info = grub_le_to_cpu32 (r->r_info); + + /* Necessary to relocate only absolute addresses. */ + if (image_target->voidp_sizeof == 4) + { + if (ELF_R_TYPE (info) == R_386_32) + { + Elf_Addr addr; + + addr = section_address + offset; + grub_util_info ("adding a relocation entry for 0x%x", addr); + current_address + = SUFFIX (add_fixup_entry) (&lst, + GRUB_PE32_REL_BASED_HIGHLOW, + addr, 0, current_address, + image_target); + } + } + else + { + if ((ELF_R_TYPE (info) == R_X86_64_32) || + (ELF_R_TYPE (info) == R_X86_64_32S)) + { + grub_util_error ("can\'t add fixup entry for R_X86_64_32(S)"); + } + else if (ELF_R_TYPE (info) == R_X86_64_64) + { + Elf_Addr addr; + + addr = section_address + offset; + grub_util_info ("adding a relocation entry for 0x%llx", addr); + current_address + = SUFFIX (add_fixup_entry) (&lst, + GRUB_PE32_REL_BASED_DIR64, + addr, + 0, current_address, + image_target); + } + } + } + } + + current_address = SUFFIX (add_fixup_entry) (&lst, 0, 0, 1, current_address, image_target); + + { + grub_uint8_t *ptr; + ptr = *out = xmalloc (current_address); + for (lst = lst0; lst; lst = lst->next) + if (lst->state) + { + memcpy (ptr, &lst->b, grub_target_to_host32 (lst->b.block_size)); + ptr += grub_target_to_host32 (lst->b.block_size); + } + if (current_address + *out != ptr) + { + grub_util_error ("Bug detected %d != %d\n", ptr - (grub_uint8_t *) *out, + current_address); + } + } + + return current_address; +} + +/* Determine if this section is a text section. Return false if this + section is not allocated. */ +static int +SUFFIX (is_text_section) (Elf_Shdr *s, struct image_target_desc *image_target) +{ + if (image_target->id != IMAGE_EFI + && grub_target_to_host32 (s->sh_type) != SHT_PROGBITS) + return 0; + return ((grub_target_to_host32 (s->sh_flags) & (SHF_EXECINSTR | SHF_ALLOC)) + == (SHF_EXECINSTR | SHF_ALLOC)); +} + +/* Determine if this section is a data section. This assumes that + BSS is also a data section, since the converter initializes BSS + when producing PE32 to avoid a bug in EFI implementations. */ +static int +SUFFIX (is_data_section) (Elf_Shdr *s, struct image_target_desc *image_target) +{ + if (image_target->id != IMAGE_EFI + && grub_target_to_host32 (s->sh_type) != SHT_PROGBITS) + return 0; + return ((grub_target_to_host32 (s->sh_flags) & (SHF_EXECINSTR | SHF_ALLOC)) + == SHF_ALLOC); +} + +/* Return if the ELF header is valid. */ +static int +SUFFIX (check_elf_header) (Elf_Ehdr *e, size_t size, struct image_target_desc *image_target) +{ + if (size < sizeof (*e) + || e->e_ident[EI_MAG0] != ELFMAG0 + || e->e_ident[EI_MAG1] != ELFMAG1 + || e->e_ident[EI_MAG2] != ELFMAG2 + || e->e_ident[EI_MAG3] != ELFMAG3 + || e->e_ident[EI_VERSION] != EV_CURRENT + || e->e_ident[EI_CLASS] != ELFCLASSXX + || e->e_version != grub_host_to_target32 (EV_CURRENT)) + return 0; + + return 1; +} + +/* Locate section addresses by merging code sections and data sections + into .text and .data, respectively. Return the array of section + addresses. */ +static Elf_Addr * +SUFFIX (locate_sections) (Elf_Shdr *sections, Elf_Half section_entsize, + Elf_Half num_sections, const char *strtab, + grub_size_t *exec_size, grub_size_t *kernel_sz, + struct image_target_desc *image_target) +{ + int i; + Elf_Addr current_address; + Elf_Addr *section_addresses; + Elf_Shdr *s; + + section_addresses = xmalloc (sizeof (*section_addresses) * num_sections); + memset (section_addresses, 0, sizeof (*section_addresses) * num_sections); + + current_address = 0; + + /* .text */ + for (i = 0, s = sections; + i < num_sections; + i++, s = (Elf_Shdr *) ((char *) s + section_entsize)) + if (SUFFIX (is_text_section) (s, image_target)) + { + Elf_Word align = grub_host_to_target32 (s->sh_addralign); + const char *name = strtab + grub_host_to_target32 (s->sh_name); + + if (align) + current_address = ALIGN_UP (current_address + image_target->vaddr_offset, + align) - image_target->vaddr_offset; + + grub_util_info ("locating the section %s at 0x%x", + name, current_address); + section_addresses[i] = current_address; + current_address += grub_host_to_target32 (s->sh_size); + } + + current_address = ALIGN_UP (current_address + image_target->vaddr_offset, + image_target->section_align) + - image_target->vaddr_offset; + *exec_size = current_address; + + /* .data */ + for (i = 0, s = sections; + i < num_sections; + i++, s = (Elf_Shdr *) ((char *) s + section_entsize)) + if (SUFFIX (is_data_section) (s, image_target)) + { + Elf_Word align = grub_host_to_target32 (s->sh_addralign); + const char *name = strtab + grub_host_to_target32 (s->sh_name); + + if (align) + current_address = ALIGN_UP (current_address + image_target->vaddr_offset, + align) + - image_target->vaddr_offset; + + grub_util_info ("locating the section %s at 0x%x", + name, current_address); + section_addresses[i] = current_address; + current_address += grub_host_to_target32 (s->sh_size); + } + + current_address = ALIGN_UP (current_address + image_target->vaddr_offset, + image_target->section_align) - image_target->vaddr_offset; + *kernel_sz = current_address; + return section_addresses; +} + +static char * +SUFFIX (load_image) (const char *kernel_path, grub_size_t *exec_size, + grub_size_t *kernel_sz, grub_size_t *bss_size, + grub_size_t total_module_size, grub_uint64_t *start, + void **reloc_section, grub_size_t *reloc_size, + struct image_target_desc *image_target) +{ + char *kernel_img, *out_img; + const char *strtab; + Elf_Ehdr *e; + Elf_Shdr *sections; + Elf_Addr *section_addresses; + Elf_Addr *section_vaddresses; + int i; + Elf_Shdr *s; + Elf_Half num_sections; + Elf_Off section_offset; + Elf_Half section_entsize; + grub_size_t kernel_size; + Elf_Shdr *symtab_section; + + *start = 0; + + kernel_size = grub_util_get_image_size (kernel_path); + kernel_img = xmalloc (kernel_size); + grub_util_load_image (kernel_path, kernel_img); + + e = (Elf_Ehdr *) kernel_img; + if (! SUFFIX (check_elf_header) (e, kernel_size, image_target)) + grub_util_error ("invalid ELF header"); + + section_offset = grub_target_to_host32 (e->e_shoff); + section_entsize = grub_target_to_host16 (e->e_shentsize); + num_sections = grub_target_to_host16 (e->e_shnum); + + if (kernel_size < section_offset + section_entsize * num_sections) + grub_util_error ("invalid ELF format"); + + sections = (Elf_Shdr *) (kernel_img + section_offset); + + /* Relocate sections then symbols in the virtual address space. */ + s = (Elf_Shdr *) ((char *) sections + + grub_host_to_target16 (e->e_shstrndx) * section_entsize); + strtab = (char *) e + grub_host_to_target32 (s->sh_offset); + + section_addresses = SUFFIX (locate_sections) (sections, section_entsize, + num_sections, strtab, + exec_size, kernel_sz, image_target); + + if (image_target->id == IMAGE_EFI) + { + section_vaddresses = xmalloc (sizeof (*section_addresses) * num_sections); + + for (i = 0; i < num_sections; i++) + section_vaddresses[i] = section_addresses[i] + image_target->vaddr_offset; + +#if 0 + { + Elf_Addr current_address = *kernel_sz; + + for (i = 0, s = sections; + i < num_sections; + i++, s = (Elf_Shdr *) ((char *) s + section_entsize)) + if (grub_target_to_host32 (s->sh_type) == SHT_NOBITS) + { + Elf_Word align = grub_host_to_target32 (s->sh_addralign); + const char *name = strtab + grub_host_to_target32 (s->sh_name); + + if (align) + current_address = ALIGN_UP (current_address + VADDR_OFFSET, align) + - VADDR_OFFSET; + + grub_util_info ("locating the section %s at 0x%x", + name, current_address); + section_vaddresses[i] = current_address + VADDR_OFFSET; + current_address += grub_host_to_target32 (s->sh_size); + } + current_address = ALIGN_UP (current_address + VADDR_OFFSET, SECTION_ALIGN) + - VADDR_OFFSET; + *bss_size = current_address - *kernel_sz; + } +#else + *bss_size = 0; +#endif + + symtab_section = NULL; + for (i = 0, s = sections; + i < num_sections; + i++, s = (Elf_Shdr *) ((char *) s + section_entsize)) + if (s->sh_type == grub_host_to_target32 (SHT_SYMTAB)) + { + symtab_section = s; + break; + } + + if (! symtab_section) + grub_util_error ("no symbol table"); + + *start = SUFFIX (relocate_symbols) (e, sections, symtab_section, + section_vaddresses, section_entsize, + num_sections, image_target); + if (*start == 0) + grub_util_error ("start symbol is not defined"); + + /* Resolve addresses in the virtual address space. */ + SUFFIX (relocate_addresses) (e, sections, section_addresses, section_entsize, + num_sections, strtab, image_target); + + *reloc_size = SUFFIX (make_reloc_section) (e, reloc_section, + section_vaddresses, sections, + section_entsize, num_sections, + strtab, image_target); + } + else + { + *bss_size = 0; + *reloc_size = 0; + *reloc_section = NULL; + } + + out_img = xmalloc (*kernel_sz + total_module_size); + + for (i = 0, s = sections; + i < num_sections; + i++, s = (Elf_Shdr *) ((char *) s + section_entsize)) + if (SUFFIX (is_data_section) (s, image_target) + || SUFFIX (is_text_section) (s, image_target)) + { + if (grub_target_to_host32 (s->sh_type) == SHT_NOBITS) + memset (out_img + section_addresses[i], 0, + grub_host_to_target32 (s->sh_size)); + else + memcpy (out_img + section_addresses[i], + kernel_img + grub_host_to_target32 (s->sh_offset), + grub_host_to_target32 (s->sh_size)); + } + free (kernel_img); + + return out_img; +} + + +#undef SUFFIX +#undef ELFCLASSXX +#undef Elf_Ehdr +#undef Elf_Phdr +#undef Elf_Shdr +#undef Elf_Addr +#undef Elf_Sym +#undef Elf_Off +#undef Elf_Rela +#undef Elf_Rel +#undef ELF_R_TYPE +#undef ELF_R_SYM