Revamp relocation handling.

Move more code to common dl.c. Add missing veneers for arm and arm64.
	Decreases kernel size by 70 bytes on i386-pc (40-50 compressed)
This commit is contained in:
Vladimir Serbinenko 2013-12-06 09:18:55 +01:00
parent a846dd4b3a
commit 8c534b85f1
18 changed files with 1097 additions and 987 deletions

View file

@ -40,101 +40,69 @@ grub_arch_dl_check_header (void *ehdr)
/* Relocate symbols. */
grub_err_t
grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr)
grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr,
Elf_Shdr *s, grub_dl_segment_t seg)
{
Elf64_Ehdr *e = ehdr;
Elf64_Shdr *s;
Elf64_Word entsize;
unsigned i;
Elf64_Rela *rel, *max;
/* Find a symbol table. */
for (i = 0, s = (Elf64_Shdr *) ((char *) e + e->e_shoff);
i < e->e_shnum;
i++, s = (Elf64_Shdr *) ((char *) s + e->e_shentsize))
if (s->sh_type == SHT_SYMTAB)
break;
for (rel = (Elf64_Rela *) ((char *) ehdr + s->sh_offset),
max = (Elf64_Rela *) ((char *) rel + s->sh_size);
rel < max;
rel = (Elf64_Rela *) ((char *) rel + s->sh_entsize))
{
Elf64_Word *addr32;
Elf64_Xword *addr64;
Elf64_Sym *sym;
if (i == e->e_shnum)
return grub_error (GRUB_ERR_BAD_MODULE, N_("no symbol table"));
if (seg->size < rel->r_offset)
return grub_error (GRUB_ERR_BAD_MODULE,
"reloc offset is out of the segment");
entsize = s->sh_entsize;
addr32 = (Elf64_Word *) ((char *) seg->addr + rel->r_offset);
addr64 = (Elf64_Xword *) addr32;
sym = (Elf64_Sym *) ((char *) mod->symtab
+ mod->symsize * ELF_R_SYM (rel->r_info));
for (i = 0, s = (Elf64_Shdr *) ((char *) e + e->e_shoff);
i < e->e_shnum;
i++, s = (Elf64_Shdr *) ((char *) s + e->e_shentsize))
if (s->sh_type == SHT_RELA)
{
grub_dl_segment_t seg;
switch (ELF_R_TYPE (rel->r_info))
{
case R_X86_64_64:
*addr64 += rel->r_addend + sym->st_value;
break;
/* Find the target segment. */
for (seg = mod->segment; seg; seg = seg->next)
if (seg->section == s->sh_info)
break;
if (seg)
case R_X86_64_PC32:
{
Elf64_Rela *rel, *max;
for (rel = (Elf64_Rela *) ((char *) e + s->sh_offset),
max = rel + s->sh_size / s->sh_entsize;
rel < max;
rel++)
{
Elf64_Word *addr32;
Elf64_Xword *addr64;
Elf64_Sym *sym;
if (seg->size < rel->r_offset)
return grub_error (GRUB_ERR_BAD_MODULE,
"reloc offset is out of the segment");
addr32 = (Elf64_Word *) ((char *) seg->addr + rel->r_offset);
addr64 = (Elf64_Xword *) addr32;
sym = (Elf64_Sym *) ((char *) mod->symtab
+ entsize * ELF_R_SYM (rel->r_info));
switch (ELF_R_TYPE (rel->r_info))
{
case R_X86_64_64:
*addr64 += rel->r_addend + sym->st_value;
break;
case R_X86_64_PC32:
{
grub_int64_t value;
value = ((grub_int32_t) *addr32) + rel->r_addend + sym->st_value -
(Elf64_Xword) seg->addr - rel->r_offset;
if (value != (grub_int32_t) value)
return grub_error (GRUB_ERR_BAD_MODULE, "relocation out of range");
*addr32 = value;
}
break;
case R_X86_64_32:
{
grub_uint64_t value = *addr32 + rel->r_addend + sym->st_value;
if (value != (grub_uint32_t) value)
return grub_error (GRUB_ERR_BAD_MODULE, "relocation out of range");
*addr32 = value;
}
break;
case R_X86_64_32S:
{
grub_int64_t value = ((grub_int32_t) *addr32) + rel->r_addend + sym->st_value;
if (value != (grub_int32_t) value)
return grub_error (GRUB_ERR_BAD_MODULE, "relocation out of range");
*addr32 = value;
}
break;
default:
return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
N_("relocation 0x%x is not implemented yet"),
ELF_R_TYPE (rel->r_info));
}
}
grub_int64_t value;
value = ((grub_int32_t) *addr32) + rel->r_addend + sym->st_value -
(Elf64_Xword) seg->addr - rel->r_offset;
if (value != (grub_int32_t) value)
return grub_error (GRUB_ERR_BAD_MODULE, "relocation out of range");
*addr32 = value;
}
}
break;
case R_X86_64_32:
{
grub_uint64_t value = *addr32 + rel->r_addend + sym->st_value;
if (value != (grub_uint32_t) value)
return grub_error (GRUB_ERR_BAD_MODULE, "relocation out of range");
*addr32 = value;
}
break;
case R_X86_64_32S:
{
grub_int64_t value = ((grub_int32_t) *addr32) + rel->r_addend + sym->st_value;
if (value != (grub_int32_t) value)
return grub_error (GRUB_ERR_BAD_MODULE, "relocation out of range");
*addr32 = value;
}
break;
default:
return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
N_("relocation 0x%x is not implemented yet"),
ELF_R_TYPE (rel->r_info));
}
}
return GRUB_ERR_NONE;
}