/* 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.
*
* 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)
#elif defined(MKIMAGE_ELF64)
#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
struct image_target_desc *image_target)
{
Elf_Word i;
const char *strtab;
* section_entsize));
i < num_syms;
{
const char *name;
{
continue;
}
{
else
continue;
}
else if (index >= num_sections)
+ section_addresses[index]);
== STT_FUNC)
{
jptr++;
*jptr = 0;
jptr++;
}
if (! start_address)
}
return start_address;
}
/* Return the address of a symbol at the index I in the section S. */
static Elf_Addr
struct image_target_desc *image_target)
{
+ grub_target_to_host32 (s->sh_offset)
+ i * grub_target_to_host32 (s->sh_entsize));
}
/* Return the address of a modified value. */
static Elf_Addr *
struct image_target_desc *image_target)
{
}
static Elf_Addr
struct image_target_desc *image_target)
{
Elf_Word i;
int ret = 0;
i < num_syms;
ret++;
return ret;
}
#ifdef MKIMAGE_ELF64
struct unaligned_uint32
{
} __attribute__ ((packed));
static void
{
struct unaligned_uint32 *p;
switch (addr & 3)
{
case 0:
break;
case 1:
break;
case 2:
break;
}
}
static grub_uint32_t
{
low = (a & 0x00007f);
return (c & 0x7f) | ((c << 7) & 0x7fc000) | ((c >> 7) & 0x0003e00); //0x003e00
}
static void
{
struct unaligned_uint32 *p;
switch (addr & 3)
{
case 0:
p->val = ((add_value_to_slot_21_real (((p->val >> 2) & MASKF21), value) & MASKF21) << 2) | (p->val & ~(MASKF21 << 2));
break;
case 1:
p->val = ((add_value_to_slot_21_real (((p->val >> 3) & MASKF21), value) & MASKF21) << 3) | (p->val & ~(MASKF21 << 3));
break;
case 2:
p->val = ((add_value_to_slot_21_real (((p->val >> 4) & MASKF21), value) & MASKF21) << 4) | (p->val & ~(MASKF21 << 4));
break;
}
}
struct ia64_kernel_trampoline
{
/* nop.m */
/* movl r15 = addr*/
};
{
/* [MLX] nop.m 0x0 */
0x05, 0x00, 0x00, 0x00, 0x01
};
{
/* [MMI] add r15=r15,r1;; */
0x0b, 0x78, 0x3c, 0x02, 0x00, 0x20,
/* ld8 r16=[r15],8 */
0x00, 0x41, 0x3c, 0x30, 0x28, 0xc0,
/* mov r14=r1;; */
0x01, 0x08, 0x00, 0x84,
/* [MIB] ld8 r1=[r15] */
0x11, 0x08, 0x00, 0x1e, 0x18, 0x10,
/* mov b6=r16 */
0x60, 0x80, 0x04, 0x80, 0x03, 0x00,
/* br.few b6;; */
0x60, 0x00, 0x80, 0x00
};
static void
{
}
#endif
/* 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
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))
{
Elf_Rela *r;
Elf_Word j;
+ (grub_target_to_host32 (s->sh_link)
* section_entsize));
* section_entsize));
grub_util_info ("dealing with the relocation section %s for %s",
for (j = 0, r = (Elf_Rela *) ((char *) e + rtab_offset);
j < num_rs;
{
r->r_addend : 0;
switch (image_target->elf_target)
{
case EM_386:
switch (ELF_R_TYPE (info))
{
case R_386_NONE:
break;
case R_386_32:
/* This is absolute. */
grub_util_info ("relocating an R_386_32 entry to 0x%x at the offset 0x%x",
break;
case R_386_PC32:
/* This is relative. */
- image_target->vaddr_offset);
grub_util_info ("relocating an R_386_PC32 entry to 0x%x at the offset 0x%x",
break;
default:
grub_util_error ("unknown relocation type 0x%x",
ELF_R_TYPE (info));
break;
}
break;
case EM_X86_64:
switch (ELF_R_TYPE (info))
{
case R_X86_64_NONE:
break;
case R_X86_64_64:
grub_util_info ("relocating an R_X86_64_64 entry to 0x%llx at the offset 0x%llx",
break;
case R_X86_64_PC32:
{
- image_target->vaddr_offset);
grub_util_info ("relocating an R_X86_64_PC32 entry to 0x%x at the offset 0x%llx",
break;
}
case R_X86_64_32:
case R_X86_64_32S:
{
grub_util_info ("relocating an R_X86_64_32(S) entry to 0x%x at the offset 0x%llx",
break;
}
default:
grub_util_error ("unknown relocation type %d",
ELF_R_TYPE (info));
break;
}
break;
#ifdef MKIMAGE_ELF64
case EM_IA_64:
switch (ELF_R_TYPE (info))
{
case R_IA64_PCREL21B:
{
tr++;
grub_util_error ("trampoline offset too big (%lx)",
noff);
}
break;
case R_IA64_LTOFF22X:
case R_IA64_LTOFF22:
{
- image_target->vaddr_offset));
}
case R_IA64_LTOFF_FPTR22:
+ image_target->vaddr_offset);
gpptr++;
break;
case R_IA64_GPREL22:
break;
case R_IA64_PCREL64LSB:
- image_target->vaddr_offset);
break;
case R_IA64_SEGREL64LSB:
break;
case R_IA64_DIR64LSB:
case R_IA64_FPTR64LSB:
grub_util_info ("relocating a direct entry to 0x%"
PRIxGRUB_UINT64_T " at the offset 0x%x",
break;
/* We treat LTOFF22X as LTOFF22, so we can ignore LDXMOV. */
case R_IA64_LDXMOV:
break;
default:
grub_util_error ("unknown relocation type 0x%x",
ELF_R_TYPE (info));
break;
}
break;
#endif
default:
grub_util_error ("unknown architecture type %d",
}
}
}
}
/* Add a PE32's fixup entry for a relocation. Return the resulting address
after having written to the file OUT. */
static Elf_Addr
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 (flush)
{
/* Add as much padding as necessary to align the address
with a section boundary. */
unsigned padding_size;
- next_address)
>> 1);
while (padding_size--)
{
b->block_size += 2;
}
}
{
/* If not aligned with a 32-bit boundary, add
a padding entry. */
grub_util_info ("adding a padding fixup entry");
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 = &((*cblock)->b);
if (! flush)
{
/* If not allocated yet, allocate a block with enough entries. */
{
/* The spec does not mention the requirement of a Page RVA.
Here, align the address with a 4K boundary for safety. */
b->block_size = sizeof (*b);
}
/* Sanity check. */
grub_util_error ("too many fixup entries");
/* Add a new entry. */
b->block_size += 2;
}
return current_address;
}
/* Make a .reloc section. */
static Elf_Addr
const char *strtab,
struct image_target_desc *image_target)
{
unsigned i;
Elf_Shdr *s;
for (i = 0, s = sections; i < num_sections;
i++, s = (Elf_Shdr *) ((char *) s + section_entsize))
{
Elf_Rel *r;
Elf_Word j;
grub_util_info ("translating the relocation section %s",
for (j = 0, r = (Elf_Rel *) ((char *) e + rtab_offset);
j < num_rs;
{
/* Necessary to relocate only absolute addresses. */
switch (image_target->elf_target)
{
case EM_386:
{
addr, 0, current_address,
}
break;
case EM_X86_64:
{
grub_util_error ("can\'t add fixup entry for R_X86_64_32(S)");
}
{
addr,
0, current_address,
}
break;
case EM_IA_64:
switch (ELF_R_TYPE (info))
{
case R_IA64_PCREL64LSB:
case R_IA64_LDXMOV:
case R_IA64_PCREL21B:
case R_IA64_LTOFF_FPTR22:
case R_IA64_LTOFF22X:
case R_IA64_LTOFF22:
case R_IA64_GPREL22:
case R_IA64_SEGREL64LSB:
break;
case R_IA64_FPTR64LSB:
case R_IA64_DIR64LSB:
#if 1
{
addr,
0, current_address,
}
#endif
break;
default:
grub_util_error ("unknown relocation type 0x%x",
ELF_R_TYPE (info));
break;
}
break;
default:
}
}
}
for (i = 0; i < njumpers; i++)
jumpers + 8 * i,
0, current_address,
{
{
}
{
}
}
return current_address;
}
/* Determine if this section is a text section. Return false if this
section is not allocated. */
static int
{
return 0;
== (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
{
return 0;
== SHF_ALLOC);
}
/* Return if the ELF header is valid. */
static int
{
if (size < sizeof (*e)
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 *
struct image_target_desc *image_target)
{
int i;
Elf_Shdr *s;
*all_align = 1;
current_address = 0;
for (i = 0, s = sections;
i < num_sections;
i++, s = (Elf_Shdr *) ((char *) s + section_entsize))
/* .text */
for (i = 0, s = sections;
i < num_sections;
i++, s = (Elf_Shdr *) ((char *) s + section_entsize))
{
if (align)
grub_util_info ("locating the section %s at 0x%x",
}
/* .data */
for (i = 0, s = sections;
i < num_sections;
i++, s = (Elf_Shdr *) ((char *) s + section_entsize))
{
if (align)
grub_util_info ("locating the section %s at 0x%x",
}
return section_addresses;
}
static char *
struct image_target_desc *image_target)
{
const char *strtab;
Elf_Ehdr *e;
int i;
Elf_Shdr *s;
unsigned ia64jmpnum = 0;
*start = 0;
e = (Elf_Ehdr *) kernel_img;
grub_util_error ("invalid ELF header");
grub_util_error ("invalid ELF format");
/* Relocate sections then symbols in the virtual address space. */
for (i = 0; i < num_sections; i++)
{
for (i = 0, s = sections;
i < num_sections;
i++, s = (Elf_Shdr *) ((char *) s + section_entsize))
{
if (align)
grub_util_info ("locating the section %s at 0x%x",
}
}
else
*bss_size = 0;
{
for (i = 0, s = sections;
i < num_sections;
i++, s = (Elf_Shdr *) ((char *) s + section_entsize))
{
symtab_section = s;
break;
}
#ifdef MKIMAGE_ELF64
{
tramp *= sizeof (struct ia64_kernel_trampoline);
ia64jmp_off = *kernel_sz;
}
#endif
if (! symtab_section)
grub_util_error ("no symbol table");
}
else
{
*reloc_size = 0;
*reloc_section = NULL;
}
{
(char *) out_img + ia64jmp_off,
if (*start == 0)
grub_util_error ("start symbol is not defined");
/* Resolve addresses in the virtual address space. */
}
for (i = 0, s = sections;
i < num_sections;
i++, s = (Elf_Shdr *) ((char *) s + section_entsize))
{
grub_host_to_target_addr (s->sh_size));
else
grub_host_to_target_addr (s->sh_size));
}
free (kernel_img);
return out_img;
}