diff --git a/ChangeLog b/ChangeLog index 6d4b5b210..b94054a5a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2013-11-21 Vladimir Serbinenko + + Unify GOT/trampoline handling between PPC, MIPS and IA64 as they + do essentially the same thing, do it in similar way. + 2013-11-21 Colin Watson * util/grub-mkrescue.c (main): If a source directory is not diff --git a/grub-core/kern/dl.c b/grub-core/kern/dl.c index ccbd5bac4..1d6841479 100644 --- a/grub-core/kern/dl.c +++ b/grub-core/kern/dl.c @@ -229,9 +229,10 @@ grub_dl_load_segments (grub_dl_t mod, const Elf_Ehdr *e) unsigned i; Elf_Shdr *s; grub_size_t tsize = 0, talign = 1; -#if defined (__ia64__) || defined (__powerpc__) +#if defined (__ia64__) || defined (__powerpc__) || defined (__mips__) grub_size_t tramp; grub_size_t got; + grub_err_t err; #endif char *ptr; @@ -244,10 +245,10 @@ grub_dl_load_segments (grub_dl_t mod, const Elf_Ehdr *e) talign = s->sh_addralign; } -#if defined (__ia64__) || defined (__powerpc__) - grub_arch_dl_get_tramp_got_size (e, &tramp, &got); - tramp *= GRUB_ARCH_DL_TRAMP_SIZE; - got *= sizeof (grub_uint64_t); +#if defined (__ia64__) || defined (__powerpc__) || defined (__mips__) + err = grub_arch_dl_get_tramp_got_size (e, &tramp, &got); + if (err) + return err; tsize += ALIGN_UP (tramp, GRUB_ARCH_DL_TRAMP_ALIGN); if (talign < GRUB_ARCH_DL_TRAMP_ALIGN) talign = GRUB_ARCH_DL_TRAMP_ALIGN; @@ -313,7 +314,7 @@ grub_dl_load_segments (grub_dl_t mod, const Elf_Ehdr *e) mod->segment = seg; } } -#if defined (__ia64__) || defined (__powerpc__) +#if defined (__ia64__) || defined (__powerpc__) || defined (__mips__) ptr = (char *) ALIGN_UP ((grub_addr_t) ptr, GRUB_ARCH_DL_TRAMP_ALIGN); mod->tramp = ptr; ptr += tramp; diff --git a/grub-core/kern/emu/full.c b/grub-core/kern/emu/full.c index 97d59fd32..8034c637b 100644 --- a/grub-core/kern/emu/full.c +++ b/grub-core/kern/emu/full.c @@ -46,7 +46,7 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr) return GRUB_ERR_BAD_MODULE; } -#if defined (__ia64__) || defined (__powerpc__) +#if defined (__ia64__) || defined (__powerpc__) || defined (__mips__) void grub_arch_dl_get_tramp_got_size (const void *ehdr __attribute__ ((unused)), grub_size_t *tramp, grub_size_t *got) { diff --git a/grub-core/kern/ia64/dl_helper.c b/grub-core/kern/ia64/dl_helper.c index e2209ca0a..515e323ad 100644 --- a/grub-core/kern/ia64/dl_helper.c +++ b/grub-core/kern/ia64/dl_helper.c @@ -170,7 +170,7 @@ grub_ia64_make_trampoline (struct grub_ia64_trampoline *tr, grub_uint64_t addr) grub_memcpy (tr->jump, jump, sizeof (tr->jump)); } -void +grub_err_t grub_ia64_dl_get_tramp_got_size (const void *ehdr, grub_size_t *tramp, grub_size_t *got) { @@ -187,7 +187,7 @@ grub_ia64_dl_get_tramp_got_size (const void *ehdr, grub_size_t *tramp, break; if (i == grub_le_to_cpu16 (e->e_shnum)) - return; + return GRUB_ERR_NONE; for (i = 0, s = (Elf64_Shdr *) ((char *) e + grub_le_to_cpu64 (e->e_shoff)); i < grub_le_to_cpu16 (e->e_shnum); @@ -211,7 +211,9 @@ grub_ia64_dl_get_tramp_got_size (const void *ehdr, grub_size_t *tramp, break; } } - *tramp = cntt; - *got = cntg; + *tramp = cntt * sizeof (struct grub_ia64_trampoline); + *got = cntg * sizeof (grub_uint64_t); + + return GRUB_ERR_NONE; } diff --git a/grub-core/kern/mips/dl.c b/grub-core/kern/mips/dl.c index ee65c9d18..36ca9a225 100644 --- a/grub-core/kern/mips/dl.c +++ b/grub-core/kern/mips/dl.c @@ -51,6 +51,58 @@ grub_arch_dl_check_header (void *ehdr) #pragma GCC diagnostic ignored "-Wcast-align" +grub_err_t +grub_arch_dl_get_tramp_got_size (const void *ehdr, grub_size_t *tramp, + grub_size_t *got) +{ + const Elf_Ehdr *e = ehdr; + const Elf_Shdr *s; + unsigned i; + /* FIXME: suboptimal. */ + grub_size_t gp_size = 0; + + *tramp = 0; + *got = 0; + + /* Find a symbol table. */ + for (i = 0, s = (const Elf_Shdr *) ((const char *) e + e->e_shoff); + i < e->e_shnum; + i++, s = (const Elf_Shdr *) ((const char *) s + e->e_shentsize)) + if (s->sh_type == SHT_SYMTAB) + break; + + if (i == e->e_shnum) + return grub_error (GRUB_ERR_BAD_MODULE, N_("no symbol table")); + + for (i = 0, s = (const Elf_Shdr *) ((const char *) e + e->e_shoff); + i < e->e_shnum; + i++, s = (const Elf_Shdr *) ((const char *) s + e->e_shentsize)) + if (s->sh_type == SHT_REL) + { + const Elf_Rel *rel, *max; + + for (rel = (const Elf_Rel *) ((const char *) e + s->sh_offset), + max = rel + s->sh_size / s->sh_entsize; + rel < max; + rel++) + switch (ELF_R_TYPE (rel->r_info)) + { + case R_MIPS_GOT16: + case R_MIPS_CALL16: + case R_MIPS_GPREL32: + gp_size += 4; + break; + } + } + + if (gp_size > 0x08000) + return grub_error (GRUB_ERR_OUT_OF_RANGE, "__gnu_local_gp is too big\n"); + + *got = gp_size; + + return GRUB_ERR_NONE; +} + /* Relocate symbols. */ grub_err_t grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr) @@ -59,7 +111,6 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr) Elf_Shdr *s; Elf_Word entsize; unsigned i; - grub_size_t gp_size = 0; /* FIXME: suboptimal. */ grub_uint32_t *gp, *gpptr; grub_uint32_t gp0; @@ -88,43 +139,7 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr) gp0 = ((grub_uint32_t *)((char *) e + s->sh_offset))[5]; - for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff); - i < e->e_shnum; - i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize)) - if (s->sh_type == SHT_REL) - { - grub_dl_segment_t seg; - - /* Find the target segment. */ - for (seg = mod->segment; seg; seg = seg->next) - if (seg->section == s->sh_info) - break; - - if (seg) - { - Elf_Rel *rel, *max; - - for (rel = (Elf_Rel *) ((char *) e + s->sh_offset), - max = rel + s->sh_size / s->sh_entsize; - rel < max; - rel++) - switch (ELF_R_TYPE (rel->r_info)) - { - case R_MIPS_GOT16: - case R_MIPS_CALL16: - case R_MIPS_GPREL32: - gp_size += 4; - break; - } - } - } - - if (gp_size > 0x08000) - return grub_error (GRUB_ERR_OUT_OF_RANGE, "__gnu_local_gp is too big\n"); - - gpptr = gp = grub_malloc (gp_size); - if (!gp) - return grub_errno; + gpptr = gp = mod->got; for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff); i < e->e_shnum; @@ -234,7 +249,6 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr) break; default: { - grub_free (gp); return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, N_("relocation 0x%x is not implemented yet"), ELF_R_TYPE (rel->r_info)); diff --git a/grub-core/kern/powerpc/dl.c b/grub-core/kern/powerpc/dl.c index 8f77b6547..e87423438 100644 --- a/grub-core/kern/powerpc/dl.c +++ b/grub-core/kern/powerpc/dl.c @@ -38,9 +38,26 @@ grub_arch_dl_check_header (void *ehdr) return GRUB_ERR_NONE; } +/* For low-endian reverse lis and addr_high as well as ori and addr_low. */ +struct trampoline +{ + grub_uint32_t lis; + grub_uint32_t ori; + grub_uint32_t mtctr; + grub_uint32_t bctr; +}; + +static const struct trampoline trampoline_template = + { + 0x3d800000, + 0x618c0000, + 0x7d8903a6, + 0x4e800420, + }; + #pragma GCC diagnostic ignored "-Wcast-align" -void +grub_err_t grub_arch_dl_get_tramp_got_size (const void *ehdr, grub_size_t *tramp, grub_size_t *got) { @@ -59,7 +76,7 @@ grub_arch_dl_get_tramp_got_size (const void *ehdr, grub_size_t *tramp, break; if (i == e->e_shnum) - return; + return GRUB_ERR_NONE; for (i = 0, s = (const Elf_Shdr *) ((const char *) e + e->e_shoff); i < e->e_shnum; @@ -77,26 +94,11 @@ grub_arch_dl_get_tramp_got_size (const void *ehdr, grub_size_t *tramp, } - return; + *tramp *= sizeof (struct trampoline); + + return GRUB_ERR_NONE; } -/* For low-endian reverse lis and addr_high as well as ori and addr_low. */ -struct trampoline -{ - grub_uint32_t lis; - grub_uint32_t ori; - grub_uint32_t mtctr; - grub_uint32_t bctr; -}; - -static const struct trampoline trampoline_template = - { - 0x3d800000, - 0x618c0000, - 0x7d8903a6, - 0x4e800420, - }; - /* Relocate symbols. */ grub_err_t grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr) @@ -167,8 +169,6 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr) if (delta << 6 >> 6 != delta) { - COMPILE_TIME_ASSERT (sizeof (struct trampoline) - == GRUB_ARCH_DL_TRAMP_SIZE); grub_memcpy (tptr, &trampoline_template, sizeof (*tptr)); delta = (grub_uint8_t *) tptr - (grub_uint8_t *) addr; diff --git a/include/grub/dl.h b/include/grub/dl.h index 3dc88c75c..11fc77568 100644 --- a/include/grub/dl.h +++ b/include/grub/dl.h @@ -179,7 +179,7 @@ struct grub_dl Elf_Sym *symtab; void (*init) (struct grub_dl *mod); void (*fini) (void); -#if defined (__ia64__) || defined (__powerpc__) +#if defined (__ia64__) || defined (__powerpc__) || defined (__mips__) void *got; void *tramp; #endif @@ -243,23 +243,21 @@ void grub_arch_dl_init_linker (void); #define GRUB_IA64_DL_TRAMP_SIZE 48 #define GRUB_IA64_DL_GOT_ALIGN 16 -void +grub_err_t grub_ia64_dl_get_tramp_got_size (const void *ehdr, grub_size_t *tramp, grub_size_t *got); #if defined (__ia64__) #define GRUB_ARCH_DL_TRAMP_ALIGN GRUB_IA64_DL_TRAMP_ALIGN #define GRUB_ARCH_DL_GOT_ALIGN GRUB_IA64_DL_GOT_ALIGN -#define GRUB_ARCH_DL_TRAMP_SIZE GRUB_IA64_DL_TRAMP_SIZE #define grub_arch_dl_get_tramp_got_size grub_ia64_dl_get_tramp_got_size #else -void +grub_err_t grub_arch_dl_get_tramp_got_size (const void *ehdr, grub_size_t *tramp, grub_size_t *got); #endif -#ifdef __powerpc__ -#define GRUB_ARCH_DL_TRAMP_SIZE 16 +#if defined (__powerpc__) || defined (__mips__) #define GRUB_ARCH_DL_TRAMP_ALIGN 4 #define GRUB_ARCH_DL_GOT_ALIGN 4 #endif diff --git a/util/grub-mkimagexx.c b/util/grub-mkimagexx.c index 36a683d6f..4f619b925 100644 --- a/util/grub-mkimagexx.c +++ b/util/grub-mkimagexx.c @@ -1321,7 +1321,6 @@ SUFFIX (load_image) (const char *kernel_path, size_t *exec_size, *kernel_sz = ALIGN_UP (*kernel_sz, 16); grub_ia64_dl_get_tramp_got_size (e, &tramp, &got); - tramp *= sizeof (struct grub_ia64_trampoline); ia64_toff = *kernel_sz; *kernel_sz += ALIGN_UP (tramp, 16); @@ -1332,7 +1331,7 @@ SUFFIX (load_image) (const char *kernel_path, size_t *exec_size, *kernel_sz += 16 * ia64jmpnum; ia64_got_off = *kernel_sz; - *kernel_sz += ALIGN_UP (got * sizeof (grub_uint64_t), 16); + *kernel_sz += ALIGN_UP (got, 16); } #endif