From 7a148da6dd17cf98001b452ddf4e2c7612dd4e99 Mon Sep 17 00:00:00 2001 From: Vladimir Serbinenko Date: Tue, 10 Dec 2013 00:01:27 +0100 Subject: [PATCH] Implement sparc64 trampolines (needed for sparc64-emu). --- ChangeLog | 4 +++ grub-core/kern/dl.c | 6 ++-- grub-core/kern/emu/full.c | 2 +- grub-core/kern/sparc64/dl.c | 66 +++++++++++++++++++++++++++++++++++++ include/grub/dl.h | 4 +-- 5 files changed, 76 insertions(+), 6 deletions(-) diff --git a/ChangeLog b/ChangeLog index 2b9f300bb..36d1d941d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2013-12-09 Vladimir Serbinenko + + Implement sparc64 trampolines (needed for sparc64-emu). + 2013-12-09 Vladimir Serbinenko * grub-core/kern/sparc64/dl.c (grub_arch_dl_relocate_symbols): Check diff --git a/grub-core/kern/dl.c b/grub-core/kern/dl.c index 90589f75b..98f858a38 100644 --- a/grub-core/kern/dl.c +++ b/grub-core/kern/dl.c @@ -225,7 +225,7 @@ 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 (__i386__) && !defined (__x86_64__) && !defined (__sparc__) +#if !defined (__i386__) && !defined (__x86_64__) grub_size_t tramp; grub_size_t got; grub_err_t err; @@ -241,7 +241,7 @@ grub_dl_load_segments (grub_dl_t mod, const Elf_Ehdr *e) talign = s->sh_addralign; } -#if !defined (__i386__) && !defined (__x86_64__) && !defined (__sparc__) +#if !defined (__i386__) && !defined (__x86_64__) err = grub_arch_dl_get_tramp_got_size (e, &tramp, &got); if (err) return err; @@ -304,7 +304,7 @@ grub_dl_load_segments (grub_dl_t mod, const Elf_Ehdr *e) mod->segment = seg; } } -#if !defined (__i386__) && !defined (__x86_64__) && !defined (__sparc__) +#if !defined (__i386__) && !defined (__x86_64__) ptr = (char *) ALIGN_UP ((grub_addr_t) ptr, GRUB_ARCH_DL_TRAMP_ALIGN); mod->tramp = ptr; mod->trampptr = ptr; diff --git a/grub-core/kern/emu/full.c b/grub-core/kern/emu/full.c index 03888d8ec..e8d63b1f5 100644 --- a/grub-core/kern/emu/full.c +++ b/grub-core/kern/emu/full.c @@ -49,7 +49,7 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr, return GRUB_ERR_BAD_MODULE; } -#if !defined (__i386__) && !defined (__x86_64__) && !defined (__sparc__) +#if !defined (__i386__) && !defined (__x86_64__) grub_err_t 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/sparc64/dl.c b/grub-core/kern/sparc64/dl.c index c6cac6838..d25c15e10 100644 --- a/grub-core/kern/sparc64/dl.c +++ b/grub-core/kern/sparc64/dl.c @@ -40,6 +40,61 @@ grub_arch_dl_check_header (void *ehdr) #pragma GCC diagnostic ignored "-Wcast-align" +struct trampoline +{ + grub_uint8_t code[0x28]; + grub_uint64_t addr; +}; + +static const grub_uint8_t trampoline_code[0x28] = +{ + /* 0: */ 0x82, 0x10, 0x00, 0x0f, /* mov %o7, %g1 */ + /* 4: */ 0x40, 0x00, 0x00, 0x02, /* call 0xc */ + /* 8: */ 0x01, 0x00, 0x00, 0x00, /* nop */ + /* c: */ 0x9e, 0x1b, 0xc0, 0x01, /* xor %o7, %g1, %o7 */ + /* 10: */ 0x82, 0x18, 0x40, 0x0f, /* xor %g1, %o7, %g1 */ + /* 14: */ 0x9e, 0x1b, 0xc0, 0x01, /* xor %o7, %g1, %o7 */ + /* 18: */ 0xc2, 0x58, 0x60, 0x24, /* ldx [ %g1 + 0x24 ], %g1 */ + /* 1c: */ 0x81, 0xc0, 0x40, 0x00, /* jmp %g1 */ + /* 20: */ 0x01, 0x00, 0x00, 0x00, /* nop */ + /* 24: */ 0x01, 0x00, 0x00, 0x00, /* nop */ +}; + +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; + + *tramp = 0; + *got = 0; + + for (i = 0, s = (const Elf_Shdr *) ((grub_addr_t) e + e->e_shoff); + i < e->e_shnum; + i++, s = (const Elf_Shdr *) ((grub_addr_t) s + e->e_shentsize)) + if (s->sh_type == SHT_REL || s->sh_type == SHT_RELA) + { + const Elf_Rel *rel, *max; + + for (rel = (const Elf_Rel *) ((grub_addr_t) e + s->sh_offset), + max = rel + s->sh_size / s->sh_entsize; + rel < max; + rel = (const Elf_Rel *) ((grub_addr_t) rel + s->sh_entsize)) + switch (ELF_R_TYPE (rel->r_info)) + { + case R_SPARC_WDISP30: + { + *tramp += sizeof (struct trampoline); + break; + } + } + } + + return GRUB_ERR_NONE; +} + /* Relocate symbols. */ grub_err_t grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr, @@ -74,6 +129,17 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr, *addr = value; break; case R_SPARC_WDISP30: /* 7 V-disp30 */ + if (((value - (Elf_Addr) addr) & 0xFFFFFFFF00000000) && + (((value - (Elf_Addr) addr) & 0xFFFFFFFF00000000) + != 0xFFFFFFFF00000000)) + { + struct trampoline *tp = mod->trampptr; + mod->trampptr = tp + 1; + grub_memcpy (tp->code, trampoline_code, sizeof (tp->code)); + tp->addr = value; + value = (Elf_Addr) tp; + } + if (((value - (Elf_Addr) addr) & 0xFFFFFFFF00000000) && (((value - (Elf_Addr) addr) & 0xFFFFFFFF00000000) != 0xFFFFFFFF00000000)) diff --git a/include/grub/dl.h b/include/grub/dl.h index 58b636f16..d29a899f5 100644 --- a/include/grub/dl.h +++ b/include/grub/dl.h @@ -180,7 +180,7 @@ struct grub_dl grub_size_t symsize; void (*init) (struct grub_dl *mod); void (*fini) (void); -#if !defined (__i386__) && !defined (__x86_64__) && !defined (__sparc__) +#if !defined (__i386__) && !defined (__x86_64__) void *got; void *gotptr; void *tramp; @@ -278,7 +278,7 @@ grub_arch_dl_get_tramp_got_size (const void *ehdr, grub_size_t *tramp, #define GRUB_ARCH_DL_GOT_ALIGN 4 #endif -#if defined (__aarch64__) +#if defined (__aarch64__) || defined (__sparc__) #define GRUB_ARCH_DL_TRAMP_ALIGN 8 #define GRUB_ARCH_DL_GOT_ALIGN 8 #endif