util/grub-mkrawimage.c working on EFI
This commit is contained in:
parent
d31bc9964c
commit
e310b81f6c
1 changed files with 680 additions and 10 deletions
|
@ -30,6 +30,7 @@
|
||||||
#include <grub/util/misc.h>
|
#include <grub/util/misc.h>
|
||||||
#include <grub/util/resolve.h>
|
#include <grub/util/resolve.h>
|
||||||
#include <grub/misc.h>
|
#include <grub/misc.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
@ -45,7 +46,19 @@
|
||||||
|
|
||||||
#define ALIGN_ADDR(x) (ALIGN_UP((x), GRUB_TARGET_SIZEOF_VOID_P))
|
#define ALIGN_ADDR(x) (ALIGN_UP((x), GRUB_TARGET_SIZEOF_VOID_P))
|
||||||
|
|
||||||
|
#ifdef GRUB_MACHINE_EFI
|
||||||
|
#define SECTION_ALIGN GRUB_PE32_SECTION_ALIGNMENT
|
||||||
|
#define VADDR_OFFSET ALIGN_UP (sizeof (struct grub_pe32_header) + 5 * sizeof (struct grub_pe32_section_table), SECTION_ALIGN)
|
||||||
|
#else
|
||||||
#define SECTION_ALIGN 1
|
#define SECTION_ALIGN 1
|
||||||
|
#define VADDR_OFFSET 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if GRUB_TARGET_WORDSIZE == 32
|
||||||
|
# define grub_target_to_host(val) grub_target_to_host32(val)
|
||||||
|
#elif GRUB_TARGET_WORDSIZE == 64
|
||||||
|
# define grub_target_to_host(val) grub_target_to_host64(val)
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef ENABLE_LZMA
|
#ifdef ENABLE_LZMA
|
||||||
#include <grub/lib/LzmaEnc.h>
|
#include <grub/lib/LzmaEnc.h>
|
||||||
|
@ -100,13 +113,436 @@ compress_kernel (char *kernel_img, size_t kernel_size,
|
||||||
|
|
||||||
#endif /* No lzma compression */
|
#endif /* No lzma compression */
|
||||||
|
|
||||||
|
#ifdef GRUB_MACHINE_EFI
|
||||||
|
|
||||||
|
/* 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)
|
||||||
|
{
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
info = grub_target_to_host (r->r_info);
|
||||||
|
sym_addr = get_symbol_address (e, symtab_section,
|
||||||
|
ELF_R_SYM (info));
|
||||||
|
|
||||||
|
addend = (s->sh_type == grub_target_to_host32 (SHT_RELA)) ?
|
||||||
|
r->r_addend : 0;
|
||||||
|
|
||||||
|
switch (ELF_R_TYPE (info))
|
||||||
|
{
|
||||||
|
#if GRUB_TARGET_SIZEOF_VOID_P == 4
|
||||||
|
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
|
||||||
|
- VADDR_OFFSET);
|
||||||
|
grub_util_info ("relocating an R_386_PC32 entry to 0x%x at the offset 0x%x",
|
||||||
|
*target, offset);
|
||||||
|
break;
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
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
|
||||||
|
- 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
default:
|
||||||
|
grub_util_error ("unknown relocation type %d",
|
||||||
|
ELF_R_TYPE (info));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct fixup_block_list
|
||||||
|
{
|
||||||
|
struct fixup_block_list *next;
|
||||||
|
int state;
|
||||||
|
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 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, 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)
|
||||||
|
{
|
||||||
|
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 GRUB_TARGET_SIZEOF_VOID_P == 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);
|
||||||
|
}
|
||||||
|
#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);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
current_address = add_fixup_entry (&lst, 0, 0, 1, current_address);
|
||||||
|
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Determine if this section is a text section. Return false if this
|
/* Determine if this section is a text section. Return false if this
|
||||||
section is not allocated. */
|
section is not allocated. */
|
||||||
static int
|
static int
|
||||||
is_text_section (Elf_Shdr *s)
|
is_text_section (Elf_Shdr *s)
|
||||||
{
|
{
|
||||||
|
#ifndef GRUB_MACHINE_EFI
|
||||||
if (grub_target_to_host32 (s->sh_type) != SHT_PROGBITS)
|
if (grub_target_to_host32 (s->sh_type) != SHT_PROGBITS)
|
||||||
return 0;
|
return 0;
|
||||||
|
#endif
|
||||||
return ((grub_target_to_host32 (s->sh_flags) & (SHF_EXECINSTR | SHF_ALLOC))
|
return ((grub_target_to_host32 (s->sh_flags) & (SHF_EXECINSTR | SHF_ALLOC))
|
||||||
== (SHF_EXECINSTR | SHF_ALLOC));
|
== (SHF_EXECINSTR | SHF_ALLOC));
|
||||||
}
|
}
|
||||||
|
@ -117,8 +553,10 @@ is_text_section (Elf_Shdr *s)
|
||||||
static int
|
static int
|
||||||
is_data_section (Elf_Shdr *s)
|
is_data_section (Elf_Shdr *s)
|
||||||
{
|
{
|
||||||
|
#ifndef GRUB_MACHINE_EFI
|
||||||
if (grub_target_to_host32 (s->sh_type) != SHT_PROGBITS)
|
if (grub_target_to_host32 (s->sh_type) != SHT_PROGBITS)
|
||||||
return 0;
|
return 0;
|
||||||
|
#endif
|
||||||
return ((grub_target_to_host32 (s->sh_flags) & (SHF_EXECINSTR | SHF_ALLOC))
|
return ((grub_target_to_host32 (s->sh_flags) & (SHF_EXECINSTR | SHF_ALLOC))
|
||||||
== SHF_ALLOC);
|
== SHF_ALLOC);
|
||||||
}
|
}
|
||||||
|
@ -151,7 +589,8 @@ locate_sections (Elf_Shdr *sections, Elf_Half section_entsize,
|
||||||
const char *name = strtab + grub_host_to_target32 (s->sh_name);
|
const char *name = strtab + grub_host_to_target32 (s->sh_name);
|
||||||
|
|
||||||
if (align)
|
if (align)
|
||||||
current_address = ALIGN_UP (current_address, align);
|
current_address = ALIGN_UP (current_address + VADDR_OFFSET, align)
|
||||||
|
- VADDR_OFFSET;
|
||||||
|
|
||||||
grub_util_info ("locating the section %s at 0x%x",
|
grub_util_info ("locating the section %s at 0x%x",
|
||||||
name, current_address);
|
name, current_address);
|
||||||
|
@ -159,7 +598,8 @@ locate_sections (Elf_Shdr *sections, Elf_Half section_entsize,
|
||||||
current_address += grub_host_to_target32 (s->sh_size);
|
current_address += grub_host_to_target32 (s->sh_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
current_address = ALIGN_UP (current_address, SECTION_ALIGN);
|
current_address = ALIGN_UP (current_address + VADDR_OFFSET, SECTION_ALIGN)
|
||||||
|
- VADDR_OFFSET;
|
||||||
*exec_size = current_address;
|
*exec_size = current_address;
|
||||||
|
|
||||||
/* .data */
|
/* .data */
|
||||||
|
@ -172,7 +612,8 @@ locate_sections (Elf_Shdr *sections, Elf_Half section_entsize,
|
||||||
const char *name = strtab + grub_host_to_target32 (s->sh_name);
|
const char *name = strtab + grub_host_to_target32 (s->sh_name);
|
||||||
|
|
||||||
if (align)
|
if (align)
|
||||||
current_address = ALIGN_UP (current_address, align);
|
current_address = ALIGN_UP (current_address + VADDR_OFFSET, align)
|
||||||
|
- VADDR_OFFSET;
|
||||||
|
|
||||||
grub_util_info ("locating the section %s at 0x%x",
|
grub_util_info ("locating the section %s at 0x%x",
|
||||||
name, current_address);
|
name, current_address);
|
||||||
|
@ -180,7 +621,8 @@ locate_sections (Elf_Shdr *sections, Elf_Half section_entsize,
|
||||||
current_address += grub_host_to_target32 (s->sh_size);
|
current_address += grub_host_to_target32 (s->sh_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
current_address = ALIGN_UP (current_address, SECTION_ALIGN);
|
current_address = ALIGN_UP (current_address + VADDR_OFFSET, SECTION_ALIGN)
|
||||||
|
- VADDR_OFFSET;
|
||||||
*kernel_sz = current_address;
|
*kernel_sz = current_address;
|
||||||
return section_addresses;
|
return section_addresses;
|
||||||
}
|
}
|
||||||
|
@ -203,19 +645,25 @@ check_elf_header (Elf_Ehdr *e, size_t size)
|
||||||
|
|
||||||
static char *
|
static char *
|
||||||
load_image (const char *kernel_path, grub_size_t *exec_size,
|
load_image (const char *kernel_path, grub_size_t *exec_size,
|
||||||
grub_size_t *kernel_sz, grub_size_t total_module_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)
|
||||||
{
|
{
|
||||||
char *kernel_img, *out_img;
|
char *kernel_img, *out_img;
|
||||||
const char *strtab;
|
const char *strtab;
|
||||||
Elf_Ehdr *e;
|
Elf_Ehdr *e;
|
||||||
Elf_Shdr *sections;
|
Elf_Shdr *sections;
|
||||||
Elf_Addr *section_addresses;
|
Elf_Addr *section_addresses;
|
||||||
|
Elf_Addr *section_vaddresses;
|
||||||
int i;
|
int i;
|
||||||
Elf_Shdr *s;
|
Elf_Shdr *s;
|
||||||
Elf_Half num_sections;
|
Elf_Half num_sections;
|
||||||
Elf_Off section_offset;
|
Elf_Off section_offset;
|
||||||
Elf_Half section_entsize;
|
Elf_Half section_entsize;
|
||||||
grub_size_t kernel_size;
|
grub_size_t kernel_size;
|
||||||
|
Elf_Shdr *symtab_section;
|
||||||
|
|
||||||
|
*start = 0;
|
||||||
|
|
||||||
kernel_size = grub_util_get_image_size (kernel_path);
|
kernel_size = grub_util_get_image_size (kernel_path);
|
||||||
kernel_img = xmalloc (kernel_size);
|
kernel_img = xmalloc (kernel_size);
|
||||||
|
@ -242,15 +690,93 @@ load_image (const char *kernel_path, grub_size_t *exec_size,
|
||||||
section_addresses = locate_sections (sections, section_entsize,
|
section_addresses = locate_sections (sections, section_entsize,
|
||||||
num_sections, strtab,
|
num_sections, strtab,
|
||||||
exec_size, kernel_sz);
|
exec_size, kernel_sz);
|
||||||
|
|
||||||
|
#ifdef GRUB_MACHINE_EFI
|
||||||
|
{
|
||||||
|
section_vaddresses = xmalloc (sizeof (*section_addresses) * num_sections);
|
||||||
|
|
||||||
|
for (i = 0; i < num_sections; i++)
|
||||||
|
section_vaddresses[i] = section_addresses[i] + 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);
|
||||||
|
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);
|
||||||
|
|
||||||
|
*reloc_size = make_reloc_section (e, reloc_section,
|
||||||
|
section_vaddresses, sections,
|
||||||
|
section_entsize, num_sections,
|
||||||
|
strtab);
|
||||||
|
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
*bss_size = 0;
|
||||||
|
*reloc_size = 0;
|
||||||
|
*reloc_section = NULL;
|
||||||
|
#endif
|
||||||
|
|
||||||
out_img = xmalloc (*kernel_sz + total_module_size);
|
out_img = xmalloc (*kernel_sz + total_module_size);
|
||||||
|
|
||||||
for (i = 0, s = sections;
|
for (i = 0, s = sections;
|
||||||
i < num_sections;
|
i < num_sections;
|
||||||
i++, s = (Elf_Shdr *) ((char *) s + section_entsize))
|
i++, s = (Elf_Shdr *) ((char *) s + section_entsize))
|
||||||
if (is_data_section (s) || is_text_section (s))
|
if (is_data_section (s) || is_text_section (s))
|
||||||
memcpy (out_img + section_addresses[i],
|
{
|
||||||
kernel_img + grub_host_to_target32 (s->sh_offset),
|
if (grub_target_to_host32 (s->sh_type) == SHT_NOBITS)
|
||||||
grub_host_to_target32 (s->sh_size));
|
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);
|
free (kernel_img);
|
||||||
|
|
||||||
return out_img;
|
return out_img;
|
||||||
|
@ -274,6 +800,10 @@ generate_image (const char *dir, char *prefix, FILE *out, char *mods[],
|
||||||
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;
|
struct grub_module_info *modinfo;
|
||||||
|
grub_size_t bss_size;
|
||||||
|
Elf_Addr start_address;
|
||||||
|
void *rel_section;
|
||||||
|
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");
|
||||||
|
@ -307,8 +837,9 @@ 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,
|
kernel_img = load_image (kernel_path, &exec_size, &kernel_size, &bss_size,
|
||||||
total_module_size);
|
total_module_size, &start_address, &rel_section,
|
||||||
|
&reloc_size);
|
||||||
|
|
||||||
if (GRUB_KERNEL_MACHINE_PREFIX + strlen (prefix) + 1 > GRUB_KERNEL_MACHINE_DATA_END)
|
if (GRUB_KERNEL_MACHINE_PREFIX + strlen (prefix) + 1 > GRUB_KERNEL_MACHINE_DATA_END)
|
||||||
grub_util_error (_("prefix is too long"));
|
grub_util_error (_("prefix is too long"));
|
||||||
|
@ -456,6 +987,145 @@ generate_image (const char *dir, char *prefix, FILE *out, char *mods[],
|
||||||
free (boot_img);
|
free (boot_img);
|
||||||
free (boot_path);
|
free (boot_path);
|
||||||
}
|
}
|
||||||
|
#elif defined(GRUB_MACHINE_EFI)
|
||||||
|
{
|
||||||
|
void *pe_img;
|
||||||
|
size_t pe_size;
|
||||||
|
struct grub_pe32_header *header;
|
||||||
|
struct grub_pe32_coff_header *c;
|
||||||
|
struct grub_pe32_optional_header *o;
|
||||||
|
struct grub_pe32_section_table *text_section, *data_section;
|
||||||
|
struct grub_pe32_section_table *mods_section, *reloc_section;
|
||||||
|
static const grub_uint8_t stub[] = GRUB_PE32_MSDOS_STUB;
|
||||||
|
int header_size = ALIGN_UP (sizeof (struct grub_pe32_header)
|
||||||
|
+ 5 * sizeof (struct grub_pe32_section_table),
|
||||||
|
SECTION_ALIGN);
|
||||||
|
int reloc_addr = ALIGN_UP (header_size + core_size, SECTION_ALIGN);
|
||||||
|
|
||||||
|
pe_size = ALIGN_UP (reloc_addr + reloc_size, SECTION_ALIGN);
|
||||||
|
pe_img = xmalloc (reloc_addr + reloc_size);
|
||||||
|
memset (pe_img, 0, header_size);
|
||||||
|
memcpy (pe_img + header_size, core_img, core_size);
|
||||||
|
memcpy (pe_img + reloc_addr, rel_section, reloc_size);
|
||||||
|
header = pe_img;
|
||||||
|
|
||||||
|
/* The magic. */
|
||||||
|
memcpy (header->msdos_stub, stub, sizeof (header->msdos_stub));
|
||||||
|
memcpy (header->signature, "PE\0\0", sizeof (header->signature));
|
||||||
|
|
||||||
|
/* The COFF file header. */
|
||||||
|
c = &header->coff_header;
|
||||||
|
#if GRUB_TARGET_SIZEOF_VOID_P == 4
|
||||||
|
c->machine = grub_host_to_target16 (GRUB_PE32_MACHINE_I386);
|
||||||
|
#else
|
||||||
|
c->machine = grub_host_to_target16 (GRUB_PE32_MACHINE_X86_64);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
c->num_sections = grub_host_to_target16 (4);
|
||||||
|
c->time = grub_host_to_target32 (time (0));
|
||||||
|
c->optional_header_size = grub_host_to_target16 (sizeof (header->optional_header));
|
||||||
|
c->characteristics = grub_host_to_target16 (GRUB_PE32_EXECUTABLE_IMAGE
|
||||||
|
| GRUB_PE32_LINE_NUMS_STRIPPED
|
||||||
|
#if GRUB_TARGET_SIZEOF_VOID_P == 4
|
||||||
|
| GRUB_PE32_32BIT_MACHINE
|
||||||
|
#endif
|
||||||
|
| GRUB_PE32_LOCAL_SYMS_STRIPPED
|
||||||
|
| GRUB_PE32_DEBUG_STRIPPED);
|
||||||
|
|
||||||
|
/* The PE Optional header. */
|
||||||
|
o = &header->optional_header;
|
||||||
|
o->magic = grub_host_to_target16 (GRUB_PE32_PE32_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);
|
||||||
|
o->entry_addr = grub_cpu_to_le32 (start_address);
|
||||||
|
o->code_base = grub_cpu_to_le32 (header_size);
|
||||||
|
#if GRUB_TARGET_SIZEOF_VOID_P == 4
|
||||||
|
o->data_base = grub_host_to_target32 (header_size + exec_size);
|
||||||
|
#endif
|
||||||
|
o->image_base = 0;
|
||||||
|
o->section_alignment = grub_host_to_target32 (SECTION_ALIGN);
|
||||||
|
o->file_alignment = grub_host_to_target32 (SECTION_ALIGN);
|
||||||
|
o->image_size = grub_host_to_target32 (pe_size);
|
||||||
|
o->header_size = grub_host_to_target32 (header_size);
|
||||||
|
o->subsystem = grub_host_to_target16 (GRUB_PE32_SUBSYSTEM_EFI_APPLICATION);
|
||||||
|
|
||||||
|
/* Do these really matter? */
|
||||||
|
o->stack_reserve_size = grub_host_to_target32 (0x10000);
|
||||||
|
o->stack_commit_size = grub_host_to_target32 (0x10000);
|
||||||
|
o->heap_reserve_size = grub_host_to_target32 (0x10000);
|
||||||
|
o->heap_commit_size = grub_host_to_target32 (0x10000);
|
||||||
|
|
||||||
|
o->num_data_directories = grub_host_to_target32 (GRUB_PE32_NUM_DATA_DIRECTORIES);
|
||||||
|
|
||||||
|
o->base_relocation_table.rva = grub_host_to_target32 (reloc_addr);
|
||||||
|
o->base_relocation_table.size = grub_host_to_target32 (reloc_size);
|
||||||
|
|
||||||
|
/* The sections. */
|
||||||
|
text_section = (struct grub_pe32_section_table *) (header + 1);
|
||||||
|
strcpy (text_section->name, ".text");
|
||||||
|
text_section->virtual_size = grub_cpu_to_le32 (exec_size);
|
||||||
|
text_section->virtual_address = grub_cpu_to_le32 (header_size);
|
||||||
|
text_section->raw_data_size = grub_cpu_to_le32 (exec_size);
|
||||||
|
text_section->raw_data_offset = grub_cpu_to_le32 (header_size);
|
||||||
|
text_section->characteristics = grub_cpu_to_le32 (GRUB_PE32_SCN_CNT_CODE
|
||||||
|
| GRUB_PE32_SCN_MEM_EXECUTE
|
||||||
|
| GRUB_PE32_SCN_MEM_READ
|
||||||
|
| GRUB_PE32_SCN_ALIGN_64BYTES);
|
||||||
|
|
||||||
|
data_section = text_section + 1;
|
||||||
|
strcpy (data_section->name, ".data");
|
||||||
|
data_section->virtual_size = grub_cpu_to_le32 (kernel_size - exec_size);
|
||||||
|
data_section->virtual_address = grub_cpu_to_le32 (header_size + exec_size);
|
||||||
|
data_section->raw_data_size = grub_cpu_to_le32 (kernel_size - exec_size);
|
||||||
|
data_section->raw_data_offset = grub_cpu_to_le32 (header_size + exec_size);
|
||||||
|
data_section->characteristics
|
||||||
|
= grub_cpu_to_le32 (GRUB_PE32_SCN_CNT_INITIALIZED_DATA
|
||||||
|
| GRUB_PE32_SCN_MEM_READ
|
||||||
|
| GRUB_PE32_SCN_MEM_WRITE
|
||||||
|
| GRUB_PE32_SCN_ALIGN_64BYTES);
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
bss_section = data_section + 1;
|
||||||
|
strcpy (bss_section->name, ".bss");
|
||||||
|
bss_section->virtual_size = grub_cpu_to_le32 (bss_size);
|
||||||
|
bss_section->virtual_address = grub_cpu_to_le32 (header_size + kernel_size);
|
||||||
|
bss_section->raw_data_size = 0;
|
||||||
|
bss_section->raw_data_offset = 0;
|
||||||
|
bss_section->characteristics
|
||||||
|
= grub_cpu_to_le32 (GRUB_PE32_SCN_MEM_READ
|
||||||
|
| GRUB_PE32_SCN_MEM_WRITE
|
||||||
|
| GRUB_PE32_SCN_ALIGN_64BYTES
|
||||||
|
| GRUB_PE32_SCN_CNT_INITIALIZED_DATA
|
||||||
|
| 0x80);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
mods_section = data_section + 1;
|
||||||
|
strcpy (mods_section->name, "mods");
|
||||||
|
mods_section->virtual_size = grub_cpu_to_le32 (reloc_addr - kernel_size - header_size);
|
||||||
|
mods_section->virtual_address = grub_cpu_to_le32 (header_size + kernel_size + bss_size);
|
||||||
|
mods_section->raw_data_size = grub_cpu_to_le32 (reloc_addr - kernel_size - header_size);
|
||||||
|
mods_section->raw_data_offset = grub_cpu_to_le32 (header_size + kernel_size);
|
||||||
|
mods_section->characteristics
|
||||||
|
= grub_cpu_to_le32 (GRUB_PE32_SCN_CNT_INITIALIZED_DATA
|
||||||
|
| GRUB_PE32_SCN_MEM_READ
|
||||||
|
| GRUB_PE32_SCN_MEM_WRITE
|
||||||
|
| GRUB_PE32_SCN_ALIGN_64BYTES);
|
||||||
|
|
||||||
|
reloc_section = mods_section + 1;
|
||||||
|
strcpy (reloc_section->name, ".reloc");
|
||||||
|
reloc_section->virtual_size = grub_cpu_to_le32 (reloc_size);
|
||||||
|
reloc_section->virtual_address = grub_cpu_to_le32 (reloc_addr + bss_size);
|
||||||
|
reloc_section->raw_data_size = grub_cpu_to_le32 (reloc_size);
|
||||||
|
reloc_section->raw_data_offset = grub_cpu_to_le32 (reloc_addr);
|
||||||
|
reloc_section->characteristics
|
||||||
|
= grub_cpu_to_le32 (GRUB_PE32_SCN_CNT_INITIALIZED_DATA
|
||||||
|
| GRUB_PE32_SCN_MEM_DISCARDABLE
|
||||||
|
| GRUB_PE32_SCN_MEM_READ);
|
||||||
|
free (core_img);
|
||||||
|
core_img = pe_img;
|
||||||
|
core_size = pe_size;
|
||||||
|
}
|
||||||
#elif defined(GRUB_MACHINE_QEMU)
|
#elif defined(GRUB_MACHINE_QEMU)
|
||||||
{
|
{
|
||||||
char *rom_img;
|
char *rom_img;
|
||||||
|
|
Loading…
Reference in a new issue