Unified grub-mkimage achieved

This commit is contained in:
Vladimir 'phcoder' Serbinenko 2010-04-26 13:11:43 +02:00
parent 94ac790645
commit 0253aeb7a1
7 changed files with 836 additions and 714 deletions

View file

@ -18,7 +18,6 @@ endif
bin_UTILITIES += grub-mkimage bin_UTILITIES += grub-mkimage
grub_mkimage_SOURCES = gnulib/progname.c util/grub-mkimage.c util/misc.c \ grub_mkimage_SOURCES = gnulib/progname.c util/grub-mkimage.c util/misc.c \
util/resolve.c lib/LzmaEnc.c lib/LzFind.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 util/grub-mkimage.c_DEPENDENCIES = Makefile
# For grub-probe. # For grub-probe.

View file

@ -1,7 +1,5 @@
# -*- makefile -*- # -*- makefile -*-
GRUB_KERNEL_MACHINE_LINK_ADDR = 0x8200
COMMON_CFLAGS = -mrtd -mregparm=3 COMMON_CFLAGS = -mrtd -mregparm=3
# Images. # Images.
@ -58,7 +56,7 @@ kernel_img_HEADERS += machine/biosdisk.h machine/vga.h machine/vbe.h \
machine/pxe.h i386/pit.h machine/pxe.h i386/pit.h
kernel_img_CFLAGS = $(COMMON_CFLAGS) $(TARGET_IMG_CFLAGS) kernel_img_CFLAGS = $(COMMON_CFLAGS) $(TARGET_IMG_CFLAGS)
kernel_img_ASFLAGS = $(COMMON_ASFLAGS) 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. # Utilities.
sbin_UTILITIES = grub-setup sbin_UTILITIES = grub-setup

View file

@ -199,15 +199,8 @@ struct grub_pe64_optional_header
struct grub_pe32_data_directory reserved_entry; struct grub_pe32_data_directory reserved_entry;
}; };
#if GRUB_TARGET_SIZEOF_VOID_P == 4
#define GRUB_PE32_PE32_MAGIC 0x10b #define GRUB_PE32_PE32_MAGIC 0x10b
#define GRUB_PE32_PE64_MAGIC 0x20b
#else
#define GRUB_PE32_PE32_MAGIC 0x20b
#endif
#define GRUB_PE32_SUBSYSTEM_EFI_APPLICATION 10 #define GRUB_PE32_SUBSYSTEM_EFI_APPLICATION 10

View file

@ -42,19 +42,33 @@ struct grub_module_header
/* "gmim" (GRUB Module Info Magic). */ /* "gmim" (GRUB Module Info Magic). */
#define GRUB_MODULE_MAGIC 0x676d696d #define GRUB_MODULE_MAGIC 0x676d696d
struct grub_module_info struct grub_module_info32
{ {
/* Magic number so we know we have modules present. */ /* Magic number so we know we have modules present. */
grub_uint32_t magic; grub_uint32_t magic;
#if GRUB_TARGET_SIZEOF_VOID_P == 8
grub_uint32_t padding;
#endif
/* The offset of the modules. */ /* The offset of the modules. */
grub_target_off_t offset; grub_uint32_t offset;
/* The size of all modules plus this header. */ /* The size of all modules plus this header. */
grub_target_size_t size; grub_uint32_t size;
}; };
struct grub_module_info64
{
/* Magic number so we know we have modules present. */
grub_uint32_t magic;
grub_uint32_t padding;
/* The offset of the modules. */
grub_uint64_t offset;
/* The size of all modules plus this header. */
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 grub_addr_t grub_arch_modules_addr (void);
extern void EXPORT_FUNC(grub_module_iterate) (int (*hook) (struct grub_module_header *)); extern void EXPORT_FUNC(grub_module_iterate) (int (*hook) (struct grub_module_header *));

View file

@ -46,6 +46,8 @@
/* The segment where the kernel is loaded. */ /* The segment where the kernel is loaded. */
#define GRUB_BOOT_I386_PC_KERNEL_SEG 0x800 #define GRUB_BOOT_I386_PC_KERNEL_SEG 0x800
#define GRUB_KERNEL_I386_PC_LINK_ADDR 0x8200
/* The upper memory area (starting at 640 kiB). */ /* The upper memory area (starting at 640 kiB). */
#define GRUB_MEMORY_I386_PC_UPPER 0xa0000 #define GRUB_MEMORY_I386_PC_UPPER 0xa0000
#define GRUB_MEMORY_I386_QEMU_UPPER GRUB_MEMORY_I386_PC_UPPER #define GRUB_MEMORY_I386_QEMU_UPPER GRUB_MEMORY_I386_PC_UPPER

View file

@ -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_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_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_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))) #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); return grub_le_to_cpu64 (in);
} }
static inline grub_uint32_t static inline grub_uint64_t
grub_target_to_host_real (struct image_target_desc *image_target, grub_uint32_t in) grub_host_to_target64_real (struct image_target_desc *image_target, grub_uint64_t in)
{ {
if (image_target->voidp_sizeof == 8) if (image_target->bigendian)
return grub_target_to_host64_real (image_target, in); return grub_cpu_to_be64 (in);
else else
return grub_target_to_host32_real (image_target, in); return grub_cpu_to_le64 (in);
} }
static inline grub_uint32_t 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); 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_NAME "PowerPC"
#define GRUB_IEEE1275_NOTE_TYPE 0x1275 #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; *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
{ {
struct fixup_block_list *next; struct fixup_block_list *next;
@ -505,457 +302,13 @@ struct fixup_block_list
struct grub_pe32_fixup_block b; struct grub_pe32_fixup_block b;
}; };
/* Add a PE32's fixup entry for a relocation. Return the resulting address #define MKIMAGE_ELF32 1
after having written to the file OUT. */ #include "grub-mkimagexx.c"
Elf_Addr #undef MKIMAGE_ELF32
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); #define MKIMAGE_ELF64 1
#include "grub-mkimagexx.c"
/* First, check if it is necessary to write out the current block. */ #undef MKIMAGE_ELF64
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;
#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;
}
static void static void
generate_image (const char *dir, char *prefix, FILE *out, char *mods[], 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; char *kernel_path;
size_t offset; size_t offset;
struct grub_util_path_list *path_list, *p, *next; struct grub_util_path_list *path_list, *p, *next;
struct grub_module_info *modinfo;
grub_size_t bss_size; grub_size_t bss_size;
Elf_Addr start_address; grub_uint64_t start_address;
void *rel_section; void *rel_section;
grub_size_t reloc_size; grub_size_t reloc_size;
path_list = grub_util_resolve_dependencies (dir, "moddep.lst", mods); path_list = grub_util_resolve_dependencies (dir, "moddep.lst", mods);
kernel_path = grub_util_get_path (dir, "kernel.img"); 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) 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); 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, if (image_target->voidp_sizeof == 4)
total_module_size, &start_address, &rel_section, kernel_img = load_image32 (kernel_path, &exec_size, &kernel_size, &bss_size,
&reloc_size, image_target); 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) if (image_target->prefix + strlen (prefix) + 1 > image_target->data_end)
grub_util_error (_("prefix is too long")); grub_util_error (_("prefix is too long"));
strcpy (kernel_img + image_target->prefix, prefix); strcpy (kernel_img + image_target->prefix, prefix);
/* Fill in the grub_module_info structure. */ if (image_target->voidp_sizeof == 8)
modinfo = (struct grub_module_info *) (kernel_img + kernel_size); {
memset (modinfo, 0, sizeof (struct grub_module_info)); /* Fill in the grub_module_info structure. */
modinfo->magic = grub_host_to_target32 (GRUB_MODULE_MAGIC); struct grub_module_info64 *modinfo;
modinfo->offset = grub_host_to_target_addr (sizeof (struct grub_module_info)); modinfo = (struct grub_module_info64 *) (kernel_img + kernel_size);
modinfo->size = grub_host_to_target_addr (total_module_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) for (p = path_list; p; p = p->next)
{ {
struct grub_module_header *header; 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; char *boot_path, *boot_img;
size_t boot_size; 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_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); GRUB_MEMORY_I386_PC_UPPER);
num = ((core_size + GRUB_DISK_SECTOR_SIZE - 1) >> GRUB_DISK_SECTOR_BITS); 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 o = header + GRUB_PE32_MSDOS_STUB_SIZE + GRUB_PE32_SIGNATURE_SIZE
+ sizeof (struct grub_pe32_coff_header); + 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->code_size = grub_host_to_target32 (exec_size);
o->data_size = grub_cpu_to_le32 (reloc_addr - exec_size); o->data_size = grub_cpu_to_le32 (reloc_addr - exec_size);
o->bss_size = grub_cpu_to_le32 (bss_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_type = grub_host_to_target32 (PT_NOTE);
phdr[1].p_flags = grub_host_to_target32 (PF_R); 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_vaddr = 0;
phdr[1].p_paddr = 0; phdr[1].p_paddr = 0;
phdr[1].p_filesz = grub_host_to_target32 (note_size); phdr[1].p_filesz = grub_host_to_target32 (note_size);

741
util/grub-mkimagexx.c Normal file
View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#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