util/grub-mkrawimage.c working on EFI

This commit is contained in:
Vladimir 'phcoder' Serbinenko 2010-04-25 19:38:12 +02:00
parent d31bc9964c
commit e310b81f6c

View file

@ -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;