]> git.ipfire.org Git - thirdparty/grub.git/commitdiff
Revamp relocation handling.
authorVladimir Serbinenko <phcoder@gmail.com>
Fri, 6 Dec 2013 08:18:55 +0000 (09:18 +0100)
committerVladimir Serbinenko <phcoder@gmail.com>
Fri, 6 Dec 2013 08:18:55 +0000 (09:18 +0100)
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)

18 files changed:
ChangeLog
grub-core/kern/arm/dl.c
grub-core/kern/arm/dl_helper.c
grub-core/kern/arm64/dl.c
grub-core/kern/arm64/dl_helper.c
grub-core/kern/dl.c
grub-core/kern/emu/full.c
grub-core/kern/i386/dl.c
grub-core/kern/ia64/dl.c
grub-core/kern/mips/dl.c
grub-core/kern/powerpc/dl.c
grub-core/kern/sparc64/dl.c
grub-core/kern/x86_64/dl.c
include/grub/arm/reloc.h
include/grub/arm64/reloc.h
include/grub/dl.h
util/grub-mkimagexx.c
util/mkimage.c

index 6fac2eacdb0dd073367672f91ac3a68ee663c89a..ee38108f70e8fc2ea55c8e932ceb51920403f161 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,10 @@
+2013-12-06  Vladimir Serbinenko  <phcoder@gmail.com>
+
+       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)
+
 2013-12-05  Vladimir Serbinenko  <phcoder@gmail.com>
 
        * util/grub-mkimagexx.c: Fix reloc section generation for ia64.
index 4563a52f997b1e40fa2d4ee6b10c1769770dce14..26da0ac22b1887f0e170ad010f7ef17e89d3616c 100644 (file)
 #include <grub/i18n.h>
 #include <grub/arm/reloc.h>
 
-/*************************************************
- * Runtime dynamic linker with helper functions. *
- *************************************************/
-static grub_err_t
-do_relocations (Elf_Shdr * relhdr, Elf_Ehdr * e, grub_dl_t mod)
+struct trampoline_arm
+{
+#define ARM_LOAD_IP 0xe59fc000
+#define ARM_BX 0xe12fff1c
+#define ARM_MOV_PC 0xe1a0f00c
+  grub_uint32_t load_ip;  /* ldr ip, [pc] */
+  grub_uint32_t bx; /* bx ip or mov pc, ip*/
+  grub_uint32_t addr;
+};
+
+static grub_uint16_t thumb_template[8] =
+  {
+    0x468c, /* mov     ip, r1 */
+    0x4903, /* ldr     r1, [pc, #12]   ; (10 <.text+0x10>) */
+    /* Exchange R1 and IP in limited Thumb instruction set.
+       IP gets negated but we compensate it by C code.  */
+                                     /* R1   IP */
+                                     /* -A   R1 */
+    0x4461, /* add     r1, ip */    /* R1-A R1 */
+    0x4249, /* negs    r1, r1 */    /* A-R1 R1 */
+    0x448c, /* add     ip, r1 */    /* A-R1 A  */
+    0x4249, /* negs    r1, r1 */    /* R1-A A  */
+    0x4461, /* add     r1, ip */    /* R1   A  */
+    0x4760  /* bx      ip */
+  };
+
+struct trampoline_thumb
 {
-  grub_dl_segment_t seg;
-  Elf_Rel *rel;
-  Elf_Sym *sym;
-  int i, entnum;
+  grub_uint16_t template[8];
+  grub_uint32_t neg_addr;
+};
+
+#pragma GCC diagnostic ignored "-Wcast-align"
 
-  entnum = relhdr->sh_size / sizeof (Elf_Rel);
+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;
 
-  /* Find the target segment for this relocation section. */
-  for (seg = mod->segment ; seg ; seg = seg->next)
-    if (seg->section == relhdr->sh_info)
-      break;
-  if (!seg)
-    return grub_error (GRUB_ERR_EOF, N_("relocation segment not found"));
+  *tramp = 0;
+  *got = 0;
 
-  rel = (Elf_Rel *) ((grub_addr_t) e + relhdr->sh_offset);
+  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)
+      {
+       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++)
+         switch (ELF_R_TYPE (rel->r_info))
+           {
+           case R_ARM_CALL:
+           case R_ARM_JUMP24:
+             {
+               *tramp += sizeof (struct trampoline_arm);
+               break;
+             }
+           case R_ARM_THM_CALL:
+           case R_ARM_THM_JUMP24:
+           case R_ARM_THM_JUMP19:
+             {
+               *tramp += sizeof (struct trampoline_thumb);
+               break;
+             }
+           }
+      }
+
+  grub_dprintf ("dl", "trampoline size %x\n", *tramp);
+
+  return GRUB_ERR_NONE;
+}
+
+/*************************************************
+ * Runtime dynamic linker with helper functions. *
+ *************************************************/
+grub_err_t
+grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr,
+                              Elf_Shdr *s, grub_dl_segment_t seg)
+{
+  Elf_Rel *rel, *max;
 
-  /* Step through all relocations */
-  for (i = 0, sym = mod->symtab; i < entnum; i++)
+  for (rel = (Elf_Rel *) ((char *) ehdr + s->sh_offset),
+        max = (Elf_Rel *) ((char *) rel + s->sh_size);
+       rel < max;
+       rel = (Elf_Rel *) ((char *) rel + s->sh_entsize))
     {
       Elf_Addr *target, sym_addr;
-      int relsym, reltype;
       grub_err_t retval;
+      Elf_Sym *sym;
 
-      if (seg->size < rel[i].r_offset)
+      if (seg->size < rel->r_offset)
        return grub_error (GRUB_ERR_BAD_MODULE,
                           "reloc offset is out of the segment");
-      relsym = ELF_R_SYM (rel[i].r_info);
-      reltype = ELF_R_TYPE (rel[i].r_info);
-      target = (void *) ((grub_addr_t) seg->addr + rel[i].r_offset);
+      target = (void *) ((char *) seg->addr + rel->r_offset);
+      sym = (Elf_Sym *) ((char *) mod->symtab
+                        + mod->symsize * ELF_R_SYM (rel->r_info));
 
-      sym_addr = sym[relsym].st_value;
+      sym_addr = sym->st_value;
 
-      switch (reltype)
+      switch (ELF_R_TYPE (rel->r_info))
        {
        case R_ARM_ABS32:
          {
@@ -76,16 +144,58 @@ do_relocations (Elf_Shdr * relhdr, Elf_Ehdr * e, grub_dl_t mod)
        case R_ARM_CALL:
        case R_ARM_JUMP24:
          {
-           retval = grub_arm_reloc_jump24 (target, sym_addr);
-           if (retval != GRUB_ERR_NONE)
-             return retval;
+           grub_int32_t offset;
+
+           sym_addr += grub_arm_jump24_get_offset (target);
+           offset = sym_addr - (grub_uint32_t) target;
+
+           if ((sym_addr & 1) || !grub_arm_jump24_check_offset (offset))
+             {
+               struct trampoline_arm *tp = mod->trampptr;
+               mod->trampptr = tp + 1;
+               tp->load_ip = ARM_LOAD_IP;
+               tp->bx = (sym_addr & 1) ? ARM_BX : ARM_MOV_PC;
+               tp->addr = sym_addr + 8;
+               offset = (grub_uint8_t *) tp - (grub_uint8_t *) target - 8;
+             }
+           if (!grub_arm_jump24_check_offset (offset))
+             return grub_error (GRUB_ERR_BAD_MODULE,
+                                "trampoline out of range");
+           grub_arm_jump24_set_offset (target, offset);
          }
          break;
        case R_ARM_THM_CALL:
        case R_ARM_THM_JUMP24:
          {
            /* Thumb instructions can be 16-bit aligned */
-           retval = grub_arm_reloc_thm_call ((grub_uint16_t *) target, sym_addr);
+           grub_int32_t offset;
+
+           sym_addr += grub_arm_thm_call_get_offset ((grub_uint16_t *) target);
+
+           grub_dprintf ("dl", "    sym_addr = 0x%08x\n", sym_addr);
+
+           offset = sym_addr - (grub_uint32_t) target;
+
+           grub_dprintf("dl", " BL*: target=%p, sym_addr=0x%08x, offset=%d\n",
+                        target, sym_addr, offset);
+
+           if (!(sym_addr & 1) || (offset < -0x200000 || offset >= 0x200000))
+             {
+               struct trampoline_thumb *tp = mod->trampptr;
+               mod->trampptr = tp + 1;
+               grub_memcpy (tp->template, thumb_template, sizeof (tp->template));
+               tp->neg_addr = -sym_addr - 4;
+               offset = ((grub_uint8_t *) tp - (grub_uint8_t *) target - 4) | 1;
+             }
+
+           if (offset < -0x200000 || offset >= 0x200000)
+             return grub_error (GRUB_ERR_BAD_MODULE,
+                                "trampoline out of range");
+
+           grub_dprintf ("dl", "    relative destination = %p\n",
+                         (char *) target + offset);
+
+           retval = grub_arm_thm_call_set_offset ((grub_uint16_t *) target, offset);
            if (retval != GRUB_ERR_NONE)
              return retval;
          }
@@ -98,15 +208,37 @@ do_relocations (Elf_Shdr * relhdr, Elf_Ehdr * e, grub_dl_t mod)
        case R_ARM_THM_JUMP19:
          {
            /* Thumb instructions can be 16-bit aligned */
-           retval = grub_arm_reloc_thm_jump19 ((grub_uint16_t *) target, sym_addr);
-           if (retval != GRUB_ERR_NONE)
-             return retval;
+           grub_int32_t offset;
+
+           if (!(sym_addr & 1))
+             return grub_error (GRUB_ERR_BAD_MODULE,
+                                N_("Relocation targeting wrong execution state"));
+
+           sym_addr += grub_arm_thm_jump19_get_offset ((grub_uint16_t *) target);
+
+           offset = sym_addr - (grub_uint32_t) target;
+
+           if (!grub_arm_thm_jump19_check_offset (offset)
+               || !(sym_addr & 1))
+             {
+               struct trampoline_thumb *tp = mod->gotptr;
+               mod->gotptr = tp + 1;
+               grub_memcpy (tp->template, thumb_template, sizeof (tp->template));
+               tp->neg_addr = -sym_addr - 4;
+               offset = ((grub_uint8_t *) tp - (grub_uint8_t *) target - 4) | 1;
+             }
+
+           if (!grub_arm_thm_jump19_check_offset (offset))
+             return grub_error (GRUB_ERR_BAD_MODULE,
+                                "trampoline out of range");
+
+           grub_arm_thm_jump19_set_offset ((grub_uint16_t *) target, offset);
          }
          break;
        default:
          return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
                             N_("relocation 0x%x is not implemented yet"),
-                            reltype);
+                            ELF_R_TYPE (rel->r_info));
        }
     }
 
@@ -130,77 +262,3 @@ grub_arch_dl_check_header (void *ehdr)
 
   return GRUB_ERR_NONE;
 }
-
-/*
- * Verify that provided ELF header contains reference to a symbol table
- */
-static int
-has_symtab (Elf_Ehdr * e)
-{
-  int i;
-  Elf_Shdr *s;
-
-  for (i = 0, s = (Elf_Shdr *) ((grub_uint32_t) e + e->e_shoff);
-       i < e->e_shnum;
-       i++, s = (Elf_Shdr *) ((grub_uint32_t) s + e->e_shentsize))
-    if (s->sh_type == SHT_SYMTAB)
-      return 1;
-
-  return 0;
-}
-
-/*
- * grub_arch_dl_relocate_symbols():
- *   Only externally visible function in this file.
- *   Locates the relocations section of the ELF object, and calls
- *   do_relocations() to deal with it.
- */
-grub_err_t
-grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr)
-{
-  Elf_Ehdr *e = ehdr;
-  Elf_Shdr *s;
-  unsigned i;
-
-  if (!has_symtab (e))
-    return grub_error (GRUB_ERR_BAD_MODULE, N_("no symbol table"));
-
-#define FIRST_SHDR(x) ((Elf_Shdr *) ((grub_addr_t)(x) + (x)->e_shoff))
-#define NEXT_SHDR(x, y) ((Elf_Shdr *) ((grub_addr_t)(y) + (x)->e_shentsize))
-
-  for (i = 0, s = FIRST_SHDR (e); i < e->e_shnum; i++, s = NEXT_SHDR (e, s))
-    {
-      grub_err_t ret;
-
-      switch (s->sh_type)
-       {
-       case SHT_REL:
-         {
-           /* Relocations, no addends */
-           ret = do_relocations (s, e, mod);
-           if (ret != GRUB_ERR_NONE)
-             return ret;
-         }
-         break;
-       case SHT_NULL:
-       case SHT_PROGBITS:
-       case SHT_SYMTAB:
-       case SHT_STRTAB:
-       case SHT_NOBITS:
-       case SHT_ARM_ATTRIBUTES:
-         break;
-       case SHT_RELA:
-       default:
-         {
-           grub_dprintf ("dl", "unhandled section_type: %d (0x%08x)\n",
-                        s->sh_type, s->sh_type);
-           return GRUB_ERR_NOT_IMPLEMENTED_YET;
-         };
-       }
-    }
-
-#undef FIRST_SHDR
-#undef NEXT_SHDR
-
-  return GRUB_ERR_NONE;
-}
index 68a9e3bbf9d9ee363c65c516b9c10d4158f93235..5721939c13b78dca6d71cdcf8f9412fa1408f94e 100644 (file)
@@ -38,8 +38,6 @@ grub_arm_reloc_abs32 (Elf32_Word *target, Elf32_Addr sym_addr)
   tmp = grub_le_to_cpu32 (*target);
   tmp += sym_addr;
   *target = grub_cpu_to_le32 (tmp);
-  grub_dprintf ("dl", "  %s:  reloc_abs32 0x%08x => 0x%08x", __FUNCTION__,
-               (unsigned int) sym_addr, (unsigned int) tmp);
 
   return GRUB_ERR_NONE;
 }
@@ -51,37 +49,16 @@ grub_arm_reloc_abs32 (Elf32_Word *target, Elf32_Addr sym_addr)
  * little-endian, requiring some additional fiddling.               *
  ********************************************************************/
 
-/*
- * R_ARM_THM_CALL/THM_JUMP24
- *
- * Relocate Thumb (T32) instruction set relative branches:
- *   B.W, BL and BLX
- */
-grub_err_t
-grub_arm_reloc_thm_call (grub_uint16_t *target, Elf32_Addr sym_addr)
+grub_int32_t
+grub_arm_thm_call_get_offset (grub_uint16_t *target)
 {
-  grub_int32_t offset, offset_low, offset_high;
-  grub_uint32_t sign, j1, j2, is_blx;
-  grub_uint32_t insword, insmask;
+  grub_uint32_t sign, j1, j2;
+  grub_uint32_t insword;
+  grub_int32_t offset;
 
   /* Extract instruction word in alignment-safe manner */
   insword = (grub_le_to_cpu16 (*target) << 16)
     | (grub_le_to_cpu16(*(target + 1)));
-  insmask = 0xf800d000;
-
-  /* B.W/BL or BLX? Affects range and expected target state */
-  if (((insword >> 12) & 0xd) == 0xc)
-    is_blx = 1;
-  else
-    is_blx = 0;
-
-  /* If BLX, target symbol must be ARM (target address LSB == 0) */
-  if (is_blx && (sym_addr & 1))
-    return grub_error (GRUB_ERR_BAD_MODULE,
-                      N_("Relocation targeting wrong execution state"));
-
-  offset_low = -16777216;
-  offset_high = is_blx ? 16777212 : 16777214;
 
   /* Extract bitfields from instruction words */
   sign = (insword >> 26) & 1;
@@ -95,22 +72,32 @@ grub_arm_reloc_thm_call (grub_uint16_t *target, Elf32_Addr sym_addr)
   if (offset & (1 << 24))
     offset -= (1 << 25);
 
-  grub_dprintf ("dl", "    sym_addr = 0x%08x", sym_addr);
+  return offset;
+}
 
-  offset += sym_addr;
-#ifndef GRUB_UTIL
-  offset -= (grub_uint32_t) target;
-#endif
+grub_err_t
+grub_arm_thm_call_set_offset (grub_uint16_t *target, grub_int32_t offset)
+{
+  grub_uint32_t sign, j1, j2;
+  const grub_uint32_t insmask = 0xf800d000;
+  grub_uint32_t insword;
+  int is_blx;
 
-  grub_dprintf("dl", " %s: target=%p, sym_addr=0x%08x, offset=%d\n",
-             is_blx ? "BLX" : "BL", target, sym_addr, offset);
+  /* Extract instruction word in alignment-safe manner */
+  insword = (grub_le_to_cpu16 (*target) << 16)
+    | (grub_le_to_cpu16(*(target + 1)));
+
+  if (((insword >> 12) & 0xd) == 0xc)
+    is_blx = 1;
+  else
+    is_blx = 0;
 
-  if ((offset < offset_low) || (offset > offset_high))
-    return grub_error (GRUB_ERR_BAD_MODULE,
-                      N_("THM_CALL Relocation out of range."));
+  if (!is_blx && !(offset & 1))
+    return grub_error (GRUB_ERR_BAD_MODULE, "bl/b.w targettting ARM");
 
-  grub_dprintf ("dl", "    relative destination = %p",
-               (char *) target + offset);
+  /* Transform blx into bl if necessarry.  */
+  if (is_blx && (offset & 1))
+    insword |= (1 << 12);
 
   /* Reassemble instruction word */
   sign = (offset >> 24) & 1;
@@ -130,21 +117,15 @@ grub_arm_reloc_thm_call (grub_uint16_t *target, Elf32_Addr sym_addr)
   return GRUB_ERR_NONE;
 }
 
-/*
- * R_ARM_THM_JUMP19
- *
- * Relocate conditional Thumb (T32) B<c>.W
- */
-grub_err_t
-grub_arm_reloc_thm_jump19 (grub_uint16_t *target, Elf32_Addr sym_addr)
+grub_int32_t
+grub_arm_thm_jump19_get_offset (grub_uint16_t *target)
 {
   grub_int32_t offset;
-  grub_uint32_t insword, insmask;
+  grub_uint32_t insword;
 
   /* Extract instruction word in alignment-safe manner */
-  insword = grub_le_to_cpu16 ((*target)) << 16
-    | grub_le_to_cpu16 (*(target + 1));
-  insmask = 0xfbc0d000;
+  insword = (grub_le_to_cpu16 (*target) << 16)
+    | (grub_le_to_cpu16(*(target + 1)));
 
   /* Extract and sign extend offset */
   offset = ((insword >> 26) & 1) << 19
@@ -156,18 +137,22 @@ grub_arm_reloc_thm_jump19 (grub_uint16_t *target, Elf32_Addr sym_addr)
   if (offset & (1 << 20))
     offset -= (1 << 21);
 
-  /* Adjust and re-truncate offset */
-  offset += sym_addr;
-#ifndef GRUB_UTIL
-  offset -= (grub_uint32_t) target;
-#endif
-  if ((offset > 1048574) || (offset < -1048576))
-    return grub_error (GRUB_ERR_BAD_MODULE,
-                      N_("THM_JUMP19 Relocation out of range."));
+  return offset;
+}
+
+void
+grub_arm_thm_jump19_set_offset (grub_uint16_t *target, grub_int32_t offset)
+{
+  grub_uint32_t insword;
+  const grub_uint32_t insmask = 0xfbc0d000;
 
   offset >>= 1;
   offset &= 0xfffff;
 
+  /* Extract instruction word in alignment-safe manner */
+  insword = grub_le_to_cpu16 ((*target)) << 16
+    | grub_le_to_cpu16 (*(target + 1));
+
   /* Reassemble instruction word and write back */
   insword &= insmask;
   insword |= ((offset >> 19) & 1) << 26
@@ -177,9 +162,15 @@ grub_arm_reloc_thm_jump19 (grub_uint16_t *target, Elf32_Addr sym_addr)
     | (offset & 0x7ff);
   *target = grub_cpu_to_le16 (insword >> 16);
   *(target + 1) = grub_cpu_to_le16 (insword & 0xffff);
-  return GRUB_ERR_NONE;
 }
 
+int
+grub_arm_thm_jump19_check_offset (grub_int32_t offset)
+{
+  if ((offset > 1048574) || (offset < -1048576))
+    return 0;
+  return 1;
+}
 
 
 /***********************************************************
@@ -188,35 +179,38 @@ grub_arm_reloc_thm_jump19 (grub_uint16_t *target, Elf32_Addr sym_addr)
  * ARM instructions are 32-bit in size and 32-bit aligned. *
  ***********************************************************/
 
-/*
- * R_ARM_JUMP24
- *
- * Relocate ARM (A32) B
- */
-grub_err_t
-grub_arm_reloc_jump24 (grub_uint32_t *target, Elf32_Addr sym_addr)
+grub_int32_t
+grub_arm_jump24_get_offset (grub_uint32_t *target)
 {
-  grub_uint32_t insword;
   grub_int32_t offset;
-
-  if (sym_addr & 1)
-    return grub_error (GRUB_ERR_BAD_MODULE,
-                      N_("Relocation targeting wrong execution state"));
+  grub_uint32_t insword;
 
   insword = grub_le_to_cpu32 (*target);
 
   offset = (insword & 0x00ffffff) << 2;
   if (offset & 0x02000000)
     offset -= 0x04000000;
-  offset += sym_addr;
-#ifndef GRUB_UTIL
-  offset -= (grub_uint32_t) target;
-#endif
+  return offset;
+}
+
+int
+grub_arm_jump24_check_offset (grub_int32_t offset)
+{
+  if (offset >= 0x02000000 || offset < -0x02000000)
+    return 0;
+  return 1;
+}
+
+void
+grub_arm_jump24_set_offset (grub_uint32_t *target,
+                           grub_int32_t offset)
+{
+  grub_uint32_t insword;
+
+  insword = grub_le_to_cpu32 (*target);
 
   insword &= 0xff000000;
   insword |= (offset >> 2) & 0x00ffffff;
 
   *target = grub_cpu_to_le32 (insword);
-
-  return GRUB_ERR_NONE;
 }
index afd0de2f36ad0bbfea10b530c1a5228a20414c82..07756110d79c754fd26c104e91e22007a8d9ef33 100644 (file)
 #include <grub/i18n.h>
 #include <grub/cpu/reloc.h>
 
+struct trampoline
+{
+#define LDR 0x58000050
+#define BR 0xd61f0200
+  grub_uint32_t ldr; /* ldr    x16, 8 */
+  grub_uint32_t br; /* br x16 */
+  grub_uint64_t addr;
+};
+
 /*
  * Check if EHDR is a valid ELF header.
  */
@@ -42,59 +51,76 @@ grub_arch_dl_check_header (void *ehdr)
   return GRUB_ERR_NONE;
 }
 
+#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;
+
+  *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_AARCH64_CALL26:
+           case R_AARCH64_JUMP26:
+             {
+               *tramp += sizeof (struct trampoline);
+               break;
+             }
+           }
+      }
+
+  return GRUB_ERR_NONE;
+}
+
 /*
  * Unified function for both REL and RELA 
  */
-static grub_err_t
-do_relX (Elf_Shdr * relhdr, Elf_Ehdr * e, grub_dl_t mod)
+grub_err_t
+grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr,
+                              Elf_Shdr *s, grub_dl_segment_t seg)
 {
-  grub_err_t retval;
-  grub_dl_segment_t segment;
-  Elf_Rel *rel;
-  Elf_Rela *rela;
-  Elf_Sym *symbol;
-  int i, entnum;
-  unsigned long long  entsize;
-
-  /* Find the target segment for this relocation section. */
-  for (segment = mod->segment ; segment != 0 ; segment = segment->next)
-    if (segment->section == relhdr->sh_info)
-      break;
-  if (!segment)
-    return grub_error (GRUB_ERR_EOF, N_("relocation segment not found"));
-
-  rel = (Elf_Rel *) ((grub_addr_t) e + relhdr->sh_offset);
-  rela = (Elf_Rela *) rel;
-  if (relhdr->sh_type == SHT_RELA)
-    entsize = sizeof (Elf_Rela);
-  else
-    entsize = sizeof (Elf_Rel);
-
-  entnum = relhdr->sh_size / entsize;
-  retval = GRUB_ERR_NONE;
-
-  grub_dprintf("dl", "Processing %d relocation entries.\n", entnum);
-
-  /* Step through all relocations */
-  for (i = 0, symbol = mod->symtab; i < entnum; i++)
+  Elf_Rel *rel, *max;
+
+  for (rel = (Elf_Rel *) ((char *) ehdr + s->sh_offset),
+        max = (Elf_Rel *) ((char *) rel + s->sh_size);
+       rel < max;
+       rel = (Elf_Rel *) ((char *) rel + s->sh_entsize))
     {
+      Elf_Sym *sym;
       void *place;
-      grub_uint64_t sym_addr, symidx, reltype;
+      grub_uint64_t sym_addr;
 
-      if (rel->r_offset >= segment->size)
+      if (rel->r_offset >= seg->size)
        return grub_error (GRUB_ERR_BAD_MODULE,
                           "reloc offset is out of the segment");
 
-      symidx = ELF_R_SYM (rel->r_info);
-      reltype = ELF_R_TYPE (rel->r_info);
+      sym = (Elf_Sym *) ((char *) mod->symtab
+                        + mod->symsize * ELF_R_SYM (rel->r_info));
 
-      sym_addr = symbol[symidx].st_value;
-      if (relhdr->sh_type == SHT_RELA)
-       sym_addr += rela->r_addend;
+      sym_addr = sym->st_value;
+      if (s->sh_type == SHT_RELA)
+       sym_addr += ((Elf_Rela *) rel)->r_addend;
 
-      place = (void *) ((grub_addr_t) segment->addr + rel->r_offset);
+      place = (void *) ((grub_addr_t) seg->addr + rel->r_offset);
 
-      switch (reltype)
+      switch (ELF_R_TYPE (rel->r_info))
        {
        case R_AARCH64_ABS64:
          {
@@ -108,92 +134,32 @@ do_relX (Elf_Shdr * relhdr, Elf_Ehdr * e, grub_dl_t mod)
          break;
        case R_AARCH64_CALL26:
        case R_AARCH64_JUMP26:
-         retval = grub_arm64_reloc_xxxx26 (place, sym_addr);
-         break;
-       default:
-         return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
-                            N_("relocation 0x%x is not implemented yet"),
-                            reltype);
-       }
-
-      if (retval != GRUB_ERR_NONE)
-       break;
-
-      rel = (Elf_Rel *) ((grub_addr_t) rel + entsize);
-      rela++;
-    }
-
-  return retval;
-}
-
-/*
- * Verify that provided ELF header contains reference to a symbol table
- */
-static int
-has_symtab (Elf_Ehdr * e)
-{
-  int i;
-  Elf_Shdr *s;
-
-  for (i = 0, s = (Elf_Shdr *) ((grub_addr_t) e + e->e_shoff);
-       i < e->e_shnum;
-       i++, s = (Elf_Shdr *) ((grub_addr_t) s + e->e_shentsize))
-    if (s->sh_type == SHT_SYMTAB)
-      return 1;
-
-  return 0;
-}
-
-/*
- * grub_arch_dl_relocate_symbols():
- *   Locates the relocations section of the ELF object, and calls
- *   do_relX() to deal with it.
- */
-grub_err_t
-grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr)
-{
-  Elf_Ehdr *e = ehdr;
-  Elf_Shdr *s;
-  unsigned i;
-
-  if (!has_symtab (e))
-    return grub_error (GRUB_ERR_BAD_MODULE, N_("no symbol table"));
-
-#define FIRST_SHDR(x) ((Elf_Shdr *) ((grub_addr_t)(x) + (x)->e_shoff))
-#define NEXT_SHDR(x, y) ((Elf_Shdr *) ((grub_addr_t)(y) + (x)->e_shentsize))
-
-  for (i = 0, s = FIRST_SHDR (e); i < e->e_shnum; i++, s = NEXT_SHDR (e, s))
-    {
-      grub_err_t ret;
-
-      switch (s->sh_type)
-       {
-       case SHT_REL:
-       case SHT_RELA:
          {
-           ret = do_relX (s, e, mod);
-           if (ret != GRUB_ERR_NONE)
-             return ret;
+           grub_int64_t offset = sym_addr - (grub_uint64_t) place;
+
+           if (!grub_arm_64_check_xxxx26_offset (offset))
+             {
+               struct trampoline *tp = mod->trampptr;
+               mod->trampptr = tp + 1;
+               tp->ldr = LDR;
+               tp->br = BR;
+               tp->addr = sym_addr;
+               offset = (grub_uint8_t *) tp - (grub_uint8_t *) place;
+             }
+
+           if (!grub_arm_64_check_xxxx26_offset (offset))
+               return grub_error (GRUB_ERR_BAD_MODULE,
+                                  N_("Trampoline out of range"));
+
+           grub_arm64_set_xxxx26_offset (place, offset);
          }
          break;
-       case SHT_ARM_ATTRIBUTES:
-       case SHT_NOBITS:
-       case SHT_NULL:
-       case SHT_PROGBITS:
-       case SHT_SYMTAB:
-       case SHT_STRTAB:
-         break;
        default:
-         {
-           grub_dprintf ("dl", "unhandled section_type: %d (0x%08x)\n",
-                         s->sh_type, s->sh_type);
-           return GRUB_ERR_NOT_IMPLEMENTED_YET;
-         };
+         return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
+                            N_("relocation 0x%x is not implemented yet"),
+                            ELF_R_TYPE (rel->r_info));
        }
     }
 
-#undef FIRST_SHDR
-#undef NEXT_SHDR
-
   return GRUB_ERR_NONE;
 }
index 6f99087e3b71bde98f97babeed344c3b1b2f61cd..d213ab93e88180b6013446e6121ead05edc50a6b 100644 (file)
 #include <grub/i18n.h>
 #include <grub/arm64/reloc.h>
 
-static grub_ssize_t
-sign_compress_offset (grub_ssize_t offset, int bitpos)
-{
-  return offset & ((1LL << (bitpos + 1)) - 1);
-}
-
 /*
  * grub_arm64_reloc_xxxx26():
  *
  * JUMP26/CALL26 relocations for B and BL instructions.
  */
 
-grub_err_t
-grub_arm64_reloc_xxxx26 (grub_uint32_t *place, Elf64_Addr adjust)
+int
+grub_arm_64_check_xxxx26_offset (grub_int64_t offset)
 {
-  grub_uint32_t insword, insmask;
-  grub_ssize_t offset;
   const grub_ssize_t offset_low = -(1 << 27), offset_high = (1 << 27) - 1;
 
-  insword = grub_le_to_cpu32 (*place);
-  insmask = 0xfc000000;
-
-  offset = adjust;
-#ifndef GRUB_UTIL
-  offset -= (grub_addr_t) place;
-#endif
-
   if ((offset < offset_low) || (offset > offset_high))
-    {
-      return grub_error (GRUB_ERR_BAD_MODULE,
-                        N_("CALL26 Relocation out of range"));
-    }
+    return 0;
+  return 1;
+}
+
+void
+grub_arm64_set_xxxx26_offset (grub_uint32_t *place, grub_int64_t offset)
+{
+  const grub_uint32_t insmask = grub_cpu_to_le32_compile_time (0xfc000000);
 
   grub_dprintf ("dl", "  reloc_xxxx64 %p %c= 0x%llx\n",
                place, offset > 0 ? '+' : '-',
                offset < 0 ? (long long) -(unsigned long long) offset : offset);
 
-  offset = sign_compress_offset (offset, 27) >> 2;
-
-  *place = grub_cpu_to_le32 ((insword & insmask) | offset);
-
-  return GRUB_ERR_NONE;
+  *place &= insmask;
+  *place |= grub_cpu_to_le32 (offset >> 2) & ~insmask;
 }
index 1d6841479f620ccea968a7af6986396a524ad3fa..f01fcfd4f8156983d5a8f48b0563c47a0b47cec2 100644 (file)
@@ -229,7 +229,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 (__ia64__) || defined (__powerpc__) || defined (__mips__)
+#if !defined (__i386__) && !defined (__x86_64__) && !defined (__sparc__)
   grub_size_t tramp;
   grub_size_t got;
   grub_err_t err;
@@ -245,7 +245,7 @@ grub_dl_load_segments (grub_dl_t mod, const Elf_Ehdr *e)
        talign = s->sh_addralign;
     }
 
-#if defined (__ia64__) || defined (__powerpc__) || defined (__mips__)
+#if !defined (__i386__) && !defined (__x86_64__) && !defined (__sparc__)
   err = grub_arch_dl_get_tramp_got_size (e, &tramp, &got);
   if (err)
     return err;
@@ -314,12 +314,14 @@ grub_dl_load_segments (grub_dl_t mod, const Elf_Ehdr *e)
          mod->segment = seg;
        }
     }
-#if defined (__ia64__) || defined (__powerpc__) || defined (__mips__)
+#if !defined (__i386__) && !defined (__x86_64__) && !defined (__sparc__)
   ptr = (char *) ALIGN_UP ((grub_addr_t) ptr, GRUB_ARCH_DL_TRAMP_ALIGN);
   mod->tramp = ptr;
+  mod->trampptr = ptr;
   ptr += tramp;
   ptr = (char *) ALIGN_UP ((grub_addr_t) ptr, GRUB_ARCH_DL_GOT_ALIGN);
   mod->got = ptr;
+  mod->gotptr = ptr;
   ptr += got;
 #endif
 
@@ -352,6 +354,7 @@ grub_dl_resolve_symbols (grub_dl_t mod, Elf_Ehdr *e)
 #else
   mod->symtab = (Elf_Sym *) ((char *) e + s->sh_offset);
 #endif
+  mod->symsize = s->sh_entsize;
   sym = mod->symtab;
   size = s->sh_size;
   entsize = s->sh_entsize;
@@ -561,6 +564,37 @@ grub_dl_flush_cache (grub_dl_t mod)
   grub_arch_sync_caches (mod->base, mod->sz);
 }
 
+static grub_err_t
+grub_dl_relocate_symbols (grub_dl_t mod, void *ehdr)
+{
+  Elf_Ehdr *e = ehdr;
+  Elf_Shdr *s;
+  unsigned i;
+
+  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 || s->sh_type == SHT_RELA)
+      {
+       grub_dl_segment_t seg;
+       grub_err_t err;
+
+       /* Find the target segment.  */
+       for (seg = mod->segment; seg; seg = seg->next)
+         if (seg->section == s->sh_info)
+           break;
+
+       if (seg)
+         {
+           err = grub_arch_dl_relocate_symbols (mod, ehdr, s, seg);
+           if (err)
+             return err;
+         }
+      }
+
+  return GRUB_ERR_NONE;
+}
+
 /* Load a module from core memory.  */
 grub_dl_t
 grub_dl_load_core_noinit (void *addr, grub_size_t size)
@@ -607,7 +641,7 @@ grub_dl_load_core_noinit (void *addr, grub_size_t size)
       || grub_dl_resolve_dependencies (mod, e)
       || grub_dl_load_segments (mod, e)
       || grub_dl_resolve_symbols (mod, e)
-      || grub_arch_dl_relocate_symbols (mod, e))
+      || grub_dl_relocate_symbols (mod, e))
     {
       mod->fini = 0;
       grub_dl_unload (mod);
index 8034c637b0000cdaf9f6da78db7a03e7da71a432..e056ded97be68749c0f7c4e74beb1635f155272c 100644 (file)
@@ -39,10 +39,13 @@ grub_arch_dl_check_header (void *ehdr)
 }
 
 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)
 {
   (void) mod;
   (void) ehdr;
+  (void) s;
+  (void) seg;
   return GRUB_ERR_BAD_MODULE;
 }
 
index c5539cb051275e27d53cd83b5409215e9e7d0935..1346da5cc91dd5097f12efdfdffc17ec503f2009 100644 (file)
@@ -40,75 +40,42 @@ 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)
 {
-  Elf_Ehdr *e = ehdr;
-  Elf_Shdr *s;
-  Elf_Word entsize;
-  unsigned i;
-
-  /* Find a symbol table.  */
-  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_SYMTAB)
-      break;
-
-  if (i == e->e_shnum)
-    return grub_error (GRUB_ERR_BAD_MODULE, N_("no symbol table"));
-
-  entsize = s->sh_entsize;
-
-  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++)
-             {
-               Elf_Word *addr;
-               Elf_Sym *sym;
-
-               if (seg->size < rel->r_offset)
-                 return grub_error (GRUB_ERR_BAD_MODULE,
-                                    "reloc offset is out of the segment");
-
-               addr = (Elf_Word *) ((char *) seg->addr + rel->r_offset);
-               sym = (Elf_Sym *) ((char *) mod->symtab
-                                    + entsize * ELF_R_SYM (rel->r_info));
-
-               switch (ELF_R_TYPE (rel->r_info))
-                 {
-                 case R_386_32:
-                   *addr += sym->st_value;
-                   break;
-
-                 case R_386_PC32:
-                   *addr += (sym->st_value - (Elf_Word) seg->addr
-                             - rel->r_offset);
-                   break;
-                 default:
-                   return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
-                                      N_("relocation 0x%x is not implemented yet"),
-                                      ELF_R_TYPE (rel->r_info));
-                 }
-             }
-         }
-      }
+  Elf_Rel *rel, *max;
+
+  for (rel = (Elf_Rel *) ((char *) ehdr + s->sh_offset),
+        max = (Elf_Rel *) ((char *) rel + s->sh_size);
+       rel < max;
+       rel = (Elf_Rel *) ((char *) rel + s->sh_entsize))
+    {
+      Elf_Word *addr;
+      Elf_Sym *sym;
+
+      if (seg->size < rel->r_offset)
+       return grub_error (GRUB_ERR_BAD_MODULE,
+                          "reloc offset is out of the segment");
+
+      addr = (Elf_Word *) ((char *) seg->addr + rel->r_offset);
+      sym = (Elf_Sym *) ((char *) mod->symtab
+                        + mod->symsize * ELF_R_SYM (rel->r_info));
+
+      switch (ELF_R_TYPE (rel->r_info))
+       {
+       case R_386_32:
+         *addr += sym->st_value;
+         break;
+
+       case R_386_PC32:
+         *addr += (sym->st_value - (grub_addr_t) addr);
+         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;
 }
index 91a2645ce846cbea7b6a2c69ed59cba630112f2f..e623cdc81328f0f0dd719f16f178545bb8fca43f 100644 (file)
@@ -46,118 +46,82 @@ 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)
 {
-  Elf_Ehdr *e = ehdr;
-  Elf_Shdr *s;
-  Elf_Word entsize;
-  unsigned i;
-  grub_uint64_t *gp, *gpptr;
-  struct grub_ia64_trampoline *tr;
-
-  gp = (grub_uint64_t *) mod->base;
-  gpptr = (grub_uint64_t *) mod->got;
-  tr = mod->tramp;
-
-  /* Find a symbol table.  */
-  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_SYMTAB)
-      break;
-
-  if (i == e->e_shnum)
-    return grub_error (GRUB_ERR_BAD_MODULE, N_("no symbol table"));
-
-  entsize = s->sh_entsize;
-
-  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_RELA)
-      {
-       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_Rela *rel, *max;
+
+  for (rel = (Elf_Rela *) ((char *) ehdr + s->sh_offset),
+        max = (Elf_Rela *) ((char *) rel + s->sh_size);
+       rel < max;
+       rel = (Elf_Rela *) ((char *) rel + s->sh_entsize))
+    {
+      grub_addr_t addr;
+      Elf_Sym *sym;
+      grub_uint64_t value;
+
+      if (seg->size < (rel->r_offset & ~3))
+       return grub_error (GRUB_ERR_BAD_MODULE,
+                          "reloc offset is out of the segment");
+
+      addr = (grub_addr_t) seg->addr + rel->r_offset;
+      sym = (Elf_Sym *) ((char *) mod->symtab
+                        + mod->symsize * ELF_R_SYM (rel->r_info));
+
+      /* On the PPC the value does not have an explicit
+        addend, add it.  */
+      value = sym->st_value + rel->r_addend;
+
+      switch (ELF_R_TYPE (rel->r_info))
+       {
+       case R_IA64_PCREL21B:
          {
-           Elf_Rela *rel, *max;
-
-           for (rel = (Elf_Rela *) ((char *) e + s->sh_offset),
-                  max = rel + s->sh_size / s->sh_entsize;
-                rel < max;
-                rel++)
-             {
-               grub_addr_t addr;
-               Elf_Sym *sym;
-               grub_uint64_t value;
-
-               if (seg->size < (rel->r_offset & ~3))
-                 return grub_error (GRUB_ERR_BAD_MODULE,
-                                    "reloc offset is out of the segment");
-
-               addr = (grub_addr_t) seg->addr + rel->r_offset;
-               sym = (Elf_Sym *) ((char *) mod->symtab
-                                    + entsize * ELF_R_SYM (rel->r_info));
-
-               /* On the PPC the value does not have an explicit
-                  addend, add it.  */
-               value = sym->st_value + rel->r_addend;
-
-               switch (ELF_R_TYPE (rel->r_info))
-                 {
-                 case R_IA64_PCREL21B:
-                   {
-                     grub_uint64_t noff;
-                     grub_ia64_make_trampoline (tr, value);
-                     noff = ((char *) tr - (char *) (addr & ~3)) >> 4;
-                     tr++;
-
-                     if (noff & ~MASK19)
-                       return grub_error (GRUB_ERR_BAD_OS,
-                                          "trampoline offset too big (%lx)", noff);
-                     grub_ia64_add_value_to_slot_20b (addr, noff);
-                   }
-                   break;
-                 case R_IA64_SEGREL64LSB:
-                   *(grub_uint64_t *) addr += value - (grub_addr_t) seg->addr;
-                   break;
-                 case R_IA64_FPTR64LSB:
-                 case R_IA64_DIR64LSB:
-                   *(grub_uint64_t *) addr += value;
-                   break;
-                 case R_IA64_PCREL64LSB:
-                   *(grub_uint64_t *) addr += value - addr;
-                   break;
-                 case R_IA64_GPREL22:
-                   grub_ia64_add_value_to_slot_21 (addr, value - (grub_addr_t) gp);
-                   break;
-
-                 case R_IA64_LTOFF22X:
-                 case R_IA64_LTOFF22:
-                   if (ELF_ST_TYPE (sym->st_info) == STT_FUNC)
-                     value = *(grub_uint64_t *) sym->st_value + rel->r_addend;
-                 case R_IA64_LTOFF_FPTR22:
-                   *gpptr = value;
-                   grub_ia64_add_value_to_slot_21 (addr, (grub_addr_t) gpptr - (grub_addr_t) gp);
-                   gpptr++;
-                   break;
-
-                   /* We treat LTOFF22X as LTOFF22, so we can ignore LDXMOV.  */
-                 case R_IA64_LDXMOV:
-                   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_uint64_t noff;
+           struct grub_ia64_trampoline *tr = mod->trampptr;
+           grub_ia64_make_trampoline (tr, value);
+           noff = ((char *) tr - (char *) (addr & ~3)) >> 4;
+           mod->trampptr = tr + 1;
+
+           if (noff & ~MASK19)
+             return grub_error (GRUB_ERR_BAD_OS,
+                                "trampoline offset too big (%lx)", noff);
+           grub_ia64_add_value_to_slot_20b (addr, noff);
          }
-      }
-
+         break;
+       case R_IA64_SEGREL64LSB:
+         *(grub_uint64_t *) addr += value - (grub_addr_t) seg->addr;
+         break;
+       case R_IA64_FPTR64LSB:
+       case R_IA64_DIR64LSB:
+         *(grub_uint64_t *) addr += value;
+         break;
+       case R_IA64_PCREL64LSB:
+         *(grub_uint64_t *) addr += value - addr;
+         break;
+       case R_IA64_GPREL22:
+         grub_ia64_add_value_to_slot_21 (addr, value - (grub_addr_t) mod->base);
+         break;
+
+       case R_IA64_LTOFF22X:
+       case R_IA64_LTOFF22:
+         if (ELF_ST_TYPE (sym->st_info) == STT_FUNC)
+           value = *(grub_uint64_t *) sym->st_value + rel->r_addend;
+       case R_IA64_LTOFF_FPTR22:
+         {
+           grub_uint64_t *gpptr = mod->gotptr;
+           *gpptr = value;
+           grub_ia64_add_value_to_slot_21 (addr, (grub_addr_t) gpptr - (grub_addr_t) mod->base);
+           mod->gotptr = gpptr + 1;
+           break;
+         }
+         /* We treat LTOFF22X as LTOFF22, so we can ignore LDXMOV.  */
+       case R_IA64_LDXMOV:
+         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;
 }
index b97510a1e1460fac57c831e5412458406321df5c..8c057e01d3cf930ab83f12fa1f956e2dbab95552 100644 (file)
@@ -58,23 +58,13 @@ grub_arch_dl_get_tramp_got_size (const void *ehdr, grub_size_t *tramp,
 {
   const Elf_Ehdr *e = ehdr;
   const Elf_Shdr *s;
-  unsigned i;
   /* FIXME: suboptimal.  */
   grub_size_t gp_size = 0;
+  unsigned i;
 
   *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))
@@ -106,192 +96,166 @@ grub_arch_dl_get_tramp_got_size (const void *ehdr, grub_size_t *tramp,
 
 /* 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)
 {
-  Elf_Ehdr *e = ehdr;
-  Elf_Shdr *s;
-  Elf_Word entsize;
-  unsigned i;
-  /* FIXME: suboptimal.  */
-  grub_uint32_t *gp, *gpptr;
   grub_uint32_t gp0;
+  Elf_Ehdr *e = ehdr;
 
-  /* Find a symbol table.  */
-  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_SYMTAB)
-      break;
-
-  if (i == e->e_shnum)
-    return grub_error (GRUB_ERR_BAD_MODULE, N_("no symbol table"));
-
-  entsize = s->sh_entsize;
-
-  /* Find reginfo. */
-  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_MIPS_REGINFO)
-      break;
-
-  if (i == e->e_shnum)
-    return grub_error (GRUB_ERR_BAD_MODULE, "no reginfo found");
-
-  gp0 = ((grub_uint32_t *)((char *) e + s->sh_offset))[5];
-
-  gpptr = gp = mod->got;
-
-  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)
+  if (!mod->reginfo)
+    {
+      unsigned i;
+      Elf_Shdr *ri;
+
+      /* Find reginfo. */
+      for (i = 0, ri = (Elf_Shdr *) ((char *) ehdr + e->e_shoff);
+          i < e->e_shnum;
+          i++, ri = (Elf_Shdr *) ((char *) ri + e->e_shentsize))
+       if (ri->sh_type == SHT_MIPS_REGINFO)
+         break;
+      if (i == e->e_shnum)
+       return grub_error (GRUB_ERR_BAD_MODULE, "no reginfo found");
+      mod->reginfo = (grub_uint32_t *)((char *) ehdr + ri->sh_offset);
+    }
+
+  gp0 = mod->reginfo[5];
+  Elf_Rel *rel, *max;
+
+  for (rel = (Elf_Rel *) ((char *) e + s->sh_offset),
+        max = (Elf_Rel *) ((char *) rel + s->sh_size);
+       rel < max;
+       rel = (Elf_Rel *) ((char *) rel + s->sh_entsize))
+    {
+      grub_uint8_t *addr;
+      Elf_Sym *sym;
+      grub_uint32_t sym_value;
+
+      if (seg->size < rel->r_offset)
+       return grub_error (GRUB_ERR_BAD_MODULE,
+                          "reloc offset is out of the segment");
+
+      addr = (grub_uint8_t *) ((char *) seg->addr + rel->r_offset);
+      sym = (Elf_Sym *) ((char *) mod->symtab
+                        + mod->symsize * ELF_R_SYM (rel->r_info));
+      sym_value = sym->st_value;
+      if (sym_value == (grub_addr_t) &__gnu_local_gp_dummy)
+       sym_value = (grub_addr_t) mod->got;
+      else if (sym_value == (grub_addr_t) &_gp_disp_dummy)
+       {
+         sym_value = (grub_addr_t) mod->got - (grub_addr_t) addr;
+         if (ELF_R_TYPE (rel->r_info) == R_MIPS_LO16)
+           /* ABI mandates +4 even if partner lui doesn't
+              immediately precede addiu.  */
+           sym_value += 4;
+       }
+      switch (ELF_R_TYPE (rel->r_info))
+       {
+       case R_MIPS_HI16:
          {
-           Elf_Rel *rel, *max;
-
-           for (rel = (Elf_Rel *) ((char *) e + s->sh_offset),
-                  max = rel + s->sh_size / s->sh_entsize;
-                rel < max;
-                rel++)
-             {
-               grub_uint8_t *addr;
-               Elf_Sym *sym;
-               grub_uint32_t sym_value;
-
-               if (seg->size < rel->r_offset)
-                 return grub_error (GRUB_ERR_BAD_MODULE,
-                                    "reloc offset is out of the segment");
-
-               addr = (grub_uint8_t *) ((char *) seg->addr + rel->r_offset);
-               sym = (Elf_Sym *) ((char *) mod->symtab
-                                    + entsize * ELF_R_SYM (rel->r_info));
-               sym_value = sym->st_value;
-               if (sym_value == (grub_addr_t) &__gnu_local_gp_dummy)
-                 sym_value = (grub_addr_t) gp;
-               else if (sym_value == (grub_addr_t) &_gp_disp_dummy)
-                 {
-                   sym_value = (grub_addr_t) gp - (grub_addr_t) addr;
-                   if (ELF_R_TYPE (rel->r_info) == R_MIPS_LO16)
-                     /* ABI mandates +4 even if partner lui doesn't
-                        immediately precede addiu.  */
-                     sym_value += 4;
-                 }
-               switch (ELF_R_TYPE (rel->r_info))
-                 {
-                 case R_MIPS_HI16:
-                   {
-                     grub_uint32_t value;
-                     Elf_Rel *rel2;
+           grub_uint32_t value;
+           Elf_Rel *rel2;
 
 #ifdef GRUB_CPU_WORDS_BIGENDIAN
-                     addr += 2;
+           addr += 2;
 #endif
 
-                     /* Handle partner lo16 relocation. Lower part is
-                        treated as signed. Hence add 0x8000 to compensate. 
-                      */
-                     value = (*(grub_uint16_t *) addr << 16)
-                       + sym_value + 0x8000;
-                     for (rel2 = rel + 1; rel2 < max; rel2++)
-                       if (ELF_R_SYM (rel2->r_info)
-                           == ELF_R_SYM (rel->r_info)
-                           && ELF_R_TYPE (rel2->r_info) == R_MIPS_LO16)
-                         {
-                           value += *(grub_int16_t *)
-                             ((char *) seg->addr + rel2->r_offset
+           /* Handle partner lo16 relocation. Lower part is
+              treated as signed. Hence add 0x8000 to compensate. 
+           */
+           value = (*(grub_uint16_t *) addr << 16)
+             + sym_value + 0x8000;
+           for (rel2 = rel + 1; rel2 < max; rel2++)
+             if (ELF_R_SYM (rel2->r_info)
+                 == ELF_R_SYM (rel->r_info)
+                 && ELF_R_TYPE (rel2->r_info) == R_MIPS_LO16)
+               {
+                 value += *(grub_int16_t *)
+                   ((char *) seg->addr + rel2->r_offset
 #ifdef GRUB_CPU_WORDS_BIGENDIAN
-                              + 2
+                    + 2
 #endif
-                              );
-                           break;
-                         }
-                     *(grub_uint16_t *) addr = (value >> 16) & 0xffff;
-                   }
-                   break;
-                 case R_MIPS_LO16:
+                    );
+                 break;
+               }
+           *(grub_uint16_t *) addr = (value >> 16) & 0xffff;
+         }
+         break;
+       case R_MIPS_LO16:
 #ifdef GRUB_CPU_WORDS_BIGENDIAN
-                   addr += 2;
+         addr += 2;
 #endif
-                   *(grub_uint16_t *) addr += sym_value & 0xffff;
-                   break;
-                 case R_MIPS_32:
-                   *(grub_uint32_t *) addr += sym_value;
-                   break;
-                 case R_MIPS_GPREL32:
-                   *(grub_uint32_t *) addr = sym_value
-                     + *(grub_uint32_t *) addr + gp0 - (grub_uint32_t)gp;
-                   break;
-
-                 case R_MIPS_26:
-                   {
-                     grub_uint32_t value;
-                     grub_uint32_t raw;
-                     raw = (*(grub_uint32_t *) addr) & 0x3ffffff;
-                     value = raw << 2;
-                     value += sym_value;
-                     raw = (value >> 2) & 0x3ffffff;
+         *(grub_uint16_t *) addr += sym_value & 0xffff;
+         break;
+       case R_MIPS_32:
+         *(grub_uint32_t *) addr += sym_value;
+         break;
+       case R_MIPS_GPREL32:
+         *(grub_uint32_t *) addr = sym_value
+           + *(grub_uint32_t *) addr + gp0 - (grub_uint32_t)mod->got;
+         break;
+
+       case R_MIPS_26:
+         {
+           grub_uint32_t value;
+           grub_uint32_t raw;
+           raw = (*(grub_uint32_t *) addr) & 0x3ffffff;
+           value = raw << 2;
+           value += sym_value;
+           raw = (value >> 2) & 0x3ffffff;
                        
-                     *(grub_uint32_t *) addr = 
-                       raw | ((*(grub_uint32_t *) addr) & 0xfc000000);
-                   }
-                   break;
-                 case R_MIPS_GOT16:
-                   if (ELF_ST_BIND (sym->st_info) == STB_LOCAL)
-                     {
-                       Elf_Rel *rel2;
-                       /* Handle partner lo16 relocation. Lower part is
-                          treated as signed. Hence add 0x8000 to compensate.
-                       */
-                       sym_value += (*(grub_uint16_t *) addr << 16)
-                         + 0x8000;
-                       for (rel2 = rel + 1; rel2 < max; rel2++)
-                         if (ELF_R_SYM (rel2->r_info)
-                             == ELF_R_SYM (rel->r_info)
-                             && ELF_R_TYPE (rel2->r_info) == R_MIPS_LO16)
-                           {
-                             sym_value += *(grub_int16_t *)
-                               ((char *) seg->addr + rel2->r_offset
-#ifdef GRUB_CPU_WORDS_BIGENDIAN
-                                + 2
-#endif
-                                );
-                             break;
-                           }
-                       sym_value &= 0xffff0000;
-                       *(grub_uint16_t *) addr = 0;
-                     }
-                 case R_MIPS_CALL16:
-                   /* FIXME: reuse*/
+           *(grub_uint32_t *) addr = 
+             raw | ((*(grub_uint32_t *) addr) & 0xfc000000);
+         }
+         break;
+       case R_MIPS_GOT16:
+         if (ELF_ST_BIND (sym->st_info) == STB_LOCAL)
+           {
+             Elf_Rel *rel2;
+             /* Handle partner lo16 relocation. Lower part is
+                treated as signed. Hence add 0x8000 to compensate.
+             */
+             sym_value += (*(grub_uint16_t *) addr << 16)
+               + 0x8000;
+             for (rel2 = rel + 1; rel2 < max; rel2++)
+               if (ELF_R_SYM (rel2->r_info)
+                   == ELF_R_SYM (rel->r_info)
+                   && ELF_R_TYPE (rel2->r_info) == R_MIPS_LO16)
+                 {
+                   sym_value += *(grub_int16_t *)
+                     ((char *) seg->addr + rel2->r_offset
 #ifdef GRUB_CPU_WORDS_BIGENDIAN
-                   addr += 2;
+                      + 2
 #endif
-                   *gpptr = sym_value + *(grub_uint16_t *) addr;
-                   *(grub_uint16_t *) addr
-                     = sizeof (grub_uint32_t) * (gpptr - gp);
-                   gpptr++;
-                   break;
-                 case R_MIPS_JALR:
-                   break;
-                 default:
-                   {
-                     return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
-                                        N_("relocation 0x%x is not implemented yet"),
-                                        ELF_R_TYPE (rel->r_info));
-                   }
+                      );
                    break;
                  }
-             }
+             sym_value &= 0xffff0000;
+             *(grub_uint16_t *) addr = 0;
+           }
+       case R_MIPS_CALL16:
+         {
+           grub_uint32_t *gpptr = mod->gotptr;
+         /* FIXME: reuse*/
+#ifdef GRUB_CPU_WORDS_BIGENDIAN
+           addr += 2;
+#endif
+           *gpptr = sym_value + *(grub_uint16_t *) addr;
+           *(grub_uint16_t *) addr
+             = sizeof (grub_uint32_t) * (gpptr - (grub_uint32_t *) mod->got);
+           mod->gotptr = gpptr + 1;
+           break;
          }
-      }
+       case R_MIPS_JALR:
+         break;
+       default:
+         {
+           return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
+                              N_("relocation 0x%x is not implemented yet"),
+                              ELF_R_TYPE (rel->r_info));
+         }
+         break;
+       }
+    }
 
   return GRUB_ERR_NONE;
 }
index e874234389b6ae653fa4a0f664c2184d4a62e0bf..ec204f35b39987cf6a47ba1926c1985831287fd2 100644 (file)
@@ -101,109 +101,77 @@ grub_arch_dl_get_tramp_got_size (const void *ehdr, grub_size_t *tramp,
 
 /* 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)
 {
-  Elf_Ehdr *e = ehdr;
-  Elf_Shdr *s;
-  Elf_Word entsize;
-  unsigned i;
-  struct trampoline *tptr = mod->tramp;
-
-  /* Find a symbol table.  */
-  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_SYMTAB)
-      break;
-
-  if (i == e->e_shnum)
-    return grub_error (GRUB_ERR_BAD_MODULE, N_("no symbol table"));
-
-  entsize = s->sh_entsize;
-
-  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_RELA)
-      {
-       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_Rela *rel, *max;
+
+  for (rel = (Elf_Rela *) ((char *) ehdr + s->sh_offset),
+        max = (Elf_Rela *) ((char *) rel + s->sh_size);
+       rel < max;
+       rel = (Elf_Rela *) ((char *) rel + s->sh_entsize))
+    {
+      Elf_Word *addr;
+      Elf_Sym *sym;
+      grub_uint32_t value;
+
+      if (seg->size < rel->r_offset)
+       return grub_error (GRUB_ERR_BAD_MODULE,
+                          "reloc offset is out of the segment");
+
+      addr = (Elf_Word *) ((char *) seg->addr + rel->r_offset);
+      sym = (Elf_Sym *) ((char *) mod->symtab
+                        + mod->symsize * ELF_R_SYM (rel->r_info));
+
+      /* On the PPC the value does not have an explicit
+        addend, add it.  */
+      value = sym->st_value + rel->r_addend;
+      switch (ELF_R_TYPE (rel->r_info))
+       {
+       case GRUB_ELF_R_PPC_ADDR16_LO:
+         *(Elf_Half *) addr = value;
+         break;
+
+       case GRUB_ELF_R_PPC_REL24:
          {
-           Elf_Rela *rel, *max;
+           Elf_Sword delta = value - (Elf_Word) addr;
 
-           for (rel = (Elf_Rela *) ((char *) e + s->sh_offset),
-                  max = rel + s->sh_size / s->sh_entsize;
-                rel < max;
-                rel++)
+           if (delta << 6 >> 6 != delta)
              {
-               Elf_Word *addr;
-               Elf_Sym *sym;
-               grub_uint32_t value;
-
-               if (seg->size < rel->r_offset)
-                 return grub_error (GRUB_ERR_BAD_MODULE,
-                                    "reloc offset is out of the segment");
-
-               addr = (Elf_Word *) ((char *) seg->addr + rel->r_offset);
-               sym = (Elf_Sym *) ((char *) mod->symtab
-                                    + entsize * ELF_R_SYM (rel->r_info));
-
-               /* On the PPC the value does not have an explicit
-                  addend, add it.  */
-               value = sym->st_value + rel->r_addend;
-               switch (ELF_R_TYPE (rel->r_info))
-                 {
-                 case GRUB_ELF_R_PPC_ADDR16_LO:
-                   *(Elf_Half *) addr = value;
-                   break;
-
-                 case GRUB_ELF_R_PPC_REL24:
-                   {
-                     Elf_Sword delta = value - (Elf_Word) addr;
-
-                     if (delta << 6 >> 6 != delta)
-                       {
-                         grub_memcpy (tptr, &trampoline_template,
-                                      sizeof (*tptr));
-                         delta = (grub_uint8_t *) tptr - (grub_uint8_t *) addr;
-                         tptr->lis |= (((value) >> 16) & 0xffff);
-                         tptr->ori |= ((value) & 0xffff);
-                         tptr++;
-                       }
-                       
-                     if (delta << 6 >> 6 != delta)
-                       return grub_error (GRUB_ERR_BAD_MODULE,
-                                          "relocation overflow");
-                     *addr = (*addr & 0xfc000003) | (delta & 0x3fffffc);
-                     break;
-                   }
-
-                 case GRUB_ELF_R_PPC_ADDR16_HA:
-                   *(Elf_Half *) addr = (value + 0x8000) >> 16;
-                   break;
-
-                 case GRUB_ELF_R_PPC_ADDR32:
-                   *addr = value;
-                   break;
-
-                 case GRUB_ELF_R_PPC_REL32:
-                   *addr = value - (Elf_Word) addr;
-                   break;
-
-                 default:
-                   return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
-                                      N_("relocation 0x%x is not implemented yet"),
-                                      ELF_R_TYPE (rel->r_info));
-                 }
+               struct trampoline *tptr = mod->trampptr;
+               grub_memcpy (tptr, &trampoline_template,
+                            sizeof (*tptr));
+               delta = (grub_uint8_t *) tptr - (grub_uint8_t *) addr;
+               tptr->lis |= (((value) >> 16) & 0xffff);
+               tptr->ori |= ((value) & 0xffff);
+               mod->trampptr = tptr + 1;
              }
+                       
+           if (delta << 6 >> 6 != delta)
+             return grub_error (GRUB_ERR_BAD_MODULE,
+                                "relocation overflow");
+           *addr = (*addr & 0xfc000003) | (delta & 0x3fffffc);
+           break;
          }
-      }
+
+       case GRUB_ELF_R_PPC_ADDR16_HA:
+         *(Elf_Half *) addr = (value + 0x8000) >> 16;
+         break;
+
+       case GRUB_ELF_R_PPC_ADDR32:
+         *addr = value;
+         break;
+
+       case GRUB_ELF_R_PPC_REL32:
+         *addr = value - (Elf_Word) addr;
+         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;
 }
index d188c4f23cc5dfa3845e28657bf47a9aa6a101bc..fa086ff5cdad2cda3e66e0b98d66bbb77cf89a14 100644 (file)
@@ -42,106 +42,74 @@ 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)
 {
-  Elf_Ehdr *e = ehdr;
-  Elf_Shdr *s;
-  Elf_Word entsize;
-  unsigned i;
-
-  /* Find a symbol table.  */
-  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_SYMTAB)
-      break;
-
-  if (i == e->e_shnum)
-    return grub_error (GRUB_ERR_BAD_MODULE, N_("no symbol table"));
-
-  entsize = s->sh_entsize;
-
-  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_RELA)
-      {
-       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_Rela *rel, *max;
-
-           for (rel = (Elf_Rela *) ((char *) e + s->sh_offset),
-                  max = rel + s->sh_size / s->sh_entsize;
-                rel < max;
-                rel++)
-             {
-               Elf_Word *addr;
-               Elf_Sym *sym;
-               Elf_Addr value;
-
-               if (seg->size < rel->r_offset)
-                 return grub_error (GRUB_ERR_BAD_MODULE,
-                                    "reloc offset is out of the segment");
-
-               addr = (Elf_Word *) ((char *) seg->addr + rel->r_offset);
-               sym = (Elf_Sym *) ((char *) mod->symtab
-                                    + entsize * ELF_R_SYM (rel->r_info));
-
-               value = sym->st_value + rel->r_addend;
-               switch (ELF_R_TYPE (rel->r_info) & 0xff)
-                 {
-                  case R_SPARC_32: /* 3 V-word32 */
-                    if (value & 0xFFFFFFFF00000000)
-                      return grub_error (GRUB_ERR_BAD_MODULE,
-                                         "address out of 32 bits range");
-                    *addr = value;
-                    break;
-                  case R_SPARC_WDISP30: /* 7 V-disp30 */
-                    if (((value - (Elf_Addr) addr) & 0xFFFFFFFF00000000) &&
-                        (((value - (Elf_Addr) addr) & 0xFFFFFFFF00000000)
-                        != 0xFFFFFFFF00000000))
-                      return grub_error (GRUB_ERR_BAD_MODULE,
-                                         "displacement out of 30 bits range");
-                    *addr = (*addr & 0xC0000000) |
-                      (((grub_int32_t) ((value - (Elf_Addr) addr) >> 2)) &
-                       0x3FFFFFFF);
-                    break;
-                  case R_SPARC_HH22: /* 9 V-imm22 */
-                    *addr = (*addr & 0xFFC00000) | ((value >> 42) & 0x3FFFFF);
-                    break;
-                  case R_SPARC_HM10: /* 12 T-simm13 */
-                    *addr = (*addr & 0xFFFFFC00) | ((value >> 32) & 0x3FF);
-                    break;
-                  case R_SPARC_HI22: /* 9 V-imm22 */
-                    *addr = (*addr & 0xFFC00000) | ((value >> 10) & 0x3FFFFF);
-                    break;
-                  case R_SPARC_LO10: /* 12 T-simm13 */
-                    *addr = (*addr & 0xFFFFFC00) | (value & 0x3FF);
-                    break;
-                  case R_SPARC_64: /* 32 V-xwords64 */
-                    *(Elf_Xword *) addr = value;
-                    break;
-                 case R_SPARC_OLO10:
-                   *addr = (*addr & ~0x1fff)
-                     | (((value & 0x3ff) +
-                         (ELF_R_TYPE (rel->r_info) >> 8))
-                        & 0x1fff);
-                   break;
-                 default:
-                   return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
-                                      N_("relocation 0x%x is not implemented yet"),
-                                      ELF_R_TYPE (rel->r_info));
-                 }
-             }
-         }
-      }
+  Elf_Rela *rel, *max;
+
+  for (rel = (Elf_Rela *) ((char *) ehdr + s->sh_offset),
+        max = (Elf_Rela *) ((char *) rel + s->sh_size);
+       rel < max;
+       rel = (Elf_Rela *) ((char *) rel + s->sh_entsize))
+    {
+      Elf_Word *addr;
+      Elf_Sym *sym;
+      Elf_Addr value;
+
+      if (seg->size < rel->r_offset)
+       return grub_error (GRUB_ERR_BAD_MODULE,
+                          "reloc offset is out of the segment");
+
+      addr = (Elf_Word *) ((char *) seg->addr + rel->r_offset);
+      sym = (Elf_Sym *) ((char *) mod->symtab
+                        + mod->symsize * ELF_R_SYM (rel->r_info));
+
+      value = sym->st_value + rel->r_addend;
+      switch (ELF_R_TYPE (rel->r_info) & 0xff)
+       {
+       case R_SPARC_32: /* 3 V-word32 */
+         if (value & 0xFFFFFFFF00000000)
+           return grub_error (GRUB_ERR_BAD_MODULE,
+                              "address out of 32 bits range");
+         *addr = value;
+         break;
+       case R_SPARC_WDISP30: /* 7 V-disp30 */
+         if (((value - (Elf_Addr) addr) & 0xFFFFFFFF00000000) &&
+             (((value - (Elf_Addr) addr) & 0xFFFFFFFF00000000)
+              != 0xFFFFFFFF00000000))
+           return grub_error (GRUB_ERR_BAD_MODULE,
+                              "displacement out of 30 bits range");
+         *addr = (*addr & 0xC0000000) |
+           (((grub_int32_t) ((value - (Elf_Addr) addr) >> 2)) &
+            0x3FFFFFFF);
+         break;
+       case R_SPARC_HH22: /* 9 V-imm22 */
+         *addr = (*addr & 0xFFC00000) | ((value >> 42) & 0x3FFFFF);
+         break;
+       case R_SPARC_HM10: /* 12 T-simm13 */
+         *addr = (*addr & 0xFFFFFC00) | ((value >> 32) & 0x3FF);
+         break;
+       case R_SPARC_HI22: /* 9 V-imm22 */
+         *addr = (*addr & 0xFFC00000) | ((value >> 10) & 0x3FFFFF);
+         break;
+       case R_SPARC_LO10: /* 12 T-simm13 */
+         *addr = (*addr & 0xFFFFFC00) | (value & 0x3FF);
+         break;
+       case R_SPARC_64: /* 32 V-xwords64 */
+         *(Elf_Xword *) addr = value;
+         break;
+       case R_SPARC_OLO10:
+         *addr = (*addr & ~0x1fff)
+           | (((value & 0x3ff) +
+               (ELF_R_TYPE (rel->r_info) >> 8))
+              & 0x1fff);
+         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;
 }
index 17c12154444b55ed73281919b1fef89b3ed0cab3..e00e51d4027cda1cd64aa2c797b52383fe88ec7c 100644 (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;
-
-  /* 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;
-
-  if (i == e->e_shnum)
-    return grub_error (GRUB_ERR_BAD_MODULE, N_("no symbol table"));
-
-  entsize = s->sh_entsize;
-
-  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;
-
-       /* Find the target segment.  */
-       for (seg = mod->segment; seg; seg = seg->next)
-         if (seg->section == s->sh_info)
-           break;
-
-       if (seg)
+  Elf64_Rela *rel, *max;
+
+  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 (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
+                          + mod->symsize * 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:
          {
-           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;
+           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;
 
-                 default:
-                   return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
-                                      N_("relocation 0x%x is not implemented yet"),
-                                      ELF_R_TYPE (rel->r_info));
-                 }
-             }
+       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;
 }
index 50d070f0177d6dab0f7283d3af7bd8764492baf6..b938037a06b258b30201b3afc47dd7f3a825fe15 100644 (file)
 #define GRUB_ARM_RELOC_H 1
 
 grub_err_t grub_arm_reloc_abs32 (grub_uint32_t *addr, Elf32_Addr sym_addr);
-grub_err_t grub_arm_reloc_jump24 (grub_uint32_t *addr, Elf32_Addr sym_addr);
-grub_err_t grub_arm_reloc_thm_call (grub_uint16_t *addr, Elf32_Addr sym_addr);
-grub_err_t grub_arm_reloc_thm_jump19 (grub_uint16_t *addr, Elf32_Addr sym_addr);
+
+int
+grub_arm_thm_call_check_offset (grub_int32_t offset, int is_thumb);
+grub_int32_t
+grub_arm_thm_call_get_offset (grub_uint16_t *target);
+grub_err_t
+grub_arm_thm_call_set_offset (grub_uint16_t *target, grub_int32_t offset);
+
+grub_int32_t
+grub_arm_thm_jump19_get_offset (grub_uint16_t *target);
+void
+grub_arm_thm_jump19_set_offset (grub_uint16_t *target, grub_int32_t offset);
+int
+grub_arm_thm_jump19_check_offset (grub_int32_t offset);
+
+grub_int32_t
+grub_arm_jump24_get_offset (grub_uint32_t *target);
+int
+grub_arm_jump24_check_offset (grub_int32_t offset);
+void
+grub_arm_jump24_set_offset (grub_uint32_t *target,
+                           grub_int32_t offset);
 
 #endif
index 606d71c77d44053ff8975fea23f4a1cfa396ee74..4aed3d715e00104ecbddc34db7d73b61c054e3ff 100644 (file)
@@ -19,6 +19,8 @@
 #ifndef GRUB_ARM64_RELOC_H
 #define GRUB_ARM64_RELOC_H 1
 
-grub_err_t grub_arm64_reloc_xxxx26 (grub_uint32_t *target, Elf64_Addr sym_addr);
+int grub_arm_64_check_xxxx26_offset (grub_int64_t offset);
+void
+grub_arm64_set_xxxx26_offset (grub_uint32_t *place, grub_int64_t offset);
 
 #endif
index d1d20d9d2887d7d5a7aa7f7d0f9388c53eca5533..6c758c01a92491ba931c5996d1df29993597bac9 100644 (file)
@@ -177,11 +177,17 @@ struct grub_dl
   grub_dl_dep_t dep;
   grub_dl_segment_t segment;
   Elf_Sym *symtab;
+  grub_size_t symsize;
   void (*init) (struct grub_dl *mod);
   void (*fini) (void);
-#if defined (__ia64__) || defined (__powerpc__) || defined (__mips__)
+#if !defined (__i386__) && !defined (__x86_64__) && !defined (__sparc__)
   void *got;
+  void *gotptr;
   void *tramp;
+  void *trampptr;
+#endif
+#ifdef __mips__
+  grub_uint32_t *reginfo;
 #endif
   void *base;
   grub_size_t sz;
@@ -232,7 +238,11 @@ grub_err_t grub_dl_register_symbol (const char *name, void *addr,
                                    int isfunc, grub_dl_t mod);
 
 grub_err_t grub_arch_dl_check_header (void *ehdr);
-grub_err_t grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr);
+#ifndef GRUB_UTIL
+grub_err_t
+grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr,
+                              Elf_Shdr *s, grub_dl_segment_t seg);
+#endif
 
 #if defined (_mips)
 #define GRUB_LINKER_HAVE_INIT 1
@@ -256,11 +266,16 @@ grub_arch_dl_get_tramp_got_size (const void *ehdr, grub_size_t *tramp,
                                 grub_size_t *got);
 #endif
 
-#if defined (__powerpc__) || defined (__mips__)
+#if defined (__powerpc__) || defined (__mips__) || defined (__arm__)
 #define GRUB_ARCH_DL_TRAMP_ALIGN 4
 #define GRUB_ARCH_DL_GOT_ALIGN 4
 #endif
 
+#if defined (__aarch64__)
+#define GRUB_ARCH_DL_TRAMP_ALIGN 8
+#define GRUB_ARCH_DL_GOT_ALIGN 8
+#endif
+
 #endif
 
 #endif /* ! GRUB_DL_H */
index b562fddf237d5a75b852e0ee3e0c54e8a7540a05..b7c01ab1a61658d2aec75b379153937161465991 100644 (file)
@@ -493,6 +493,85 @@ SUFFIX (count_funcs) (Elf_Ehdr *e, Elf_Shdr *symtab_section,
 }
 #endif
 
+#ifdef MKIMAGE_ELF32
+/* 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 grub_size_t
+arm_get_trampoline_size (Elf_Ehdr *e,
+                        Elf_Shdr *sections,
+                        Elf_Half section_entsize,
+                        Elf_Half num_sections,
+                        const struct grub_install_image_target_desc *image_target)
+{
+  Elf_Half i;
+  Elf_Shdr *s;
+  grub_size_t ret = 0;
+
+  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 j;
+
+       symtab_section = (Elf_Shdr *) ((char *) sections
+                                        + (grub_target_to_host32 (s->sh_link)
+                                           * section_entsize));
+
+       rtab_size = grub_target_to_host (s->sh_size);
+       r_size = grub_target_to_host (s->sh_entsize);
+       rtab_offset = grub_target_to_host (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 sym_addr;
+
+           info = grub_target_to_host (r->r_info);
+           sym_addr = SUFFIX (get_symbol_address) (e, symtab_section,
+                                                   ELF_R_SYM (info), image_target);
+
+            sym_addr += (s->sh_type == grub_target_to_host32 (SHT_RELA)) ?
+             grub_target_to_host (r->r_addend) : 0;
+
+           switch (ELF_R_TYPE (info))
+             {
+             case R_ARM_ABS32:
+             case R_ARM_V4BX:
+               break;
+             case R_ARM_THM_CALL:
+             case R_ARM_THM_JUMP24:
+             case R_ARM_THM_JUMP19:
+               if (!(sym_addr & 1))
+                 ret += 8;
+               break;
+
+             case R_ARM_CALL:
+             case R_ARM_JUMP24:
+               if (sym_addr & 1)
+                 ret += 16;
+               break;
+
+             default:
+               grub_util_error (_("relocation 0x%x is not implemented yet!"), ELF_R_TYPE (info));
+               break;
+             }
+         }
+      }
+  return ret;
+}
+#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
@@ -512,6 +591,8 @@ SUFFIX (relocate_addresses) (Elf_Ehdr *e, Elf_Shdr *sections,
   struct grub_ia64_trampoline *tr = (void *) (pe_target + tramp_off);
   grub_uint64_t *gpptr = (void *) (pe_target + got_off);
 #define MASK19 ((1 << 19) - 1)
+#else
+  grub_uint32_t *tr = (void *) (pe_target + tramp_off);
 #endif
 
   for (i = 0, s = sections;
@@ -731,13 +812,13 @@ SUFFIX (relocate_addresses) (Elf_Ehdr *e, Elf_Shdr *sections,
                   case R_AARCH64_JUMP26:
                   case R_AARCH64_CALL26:
                     {
-                      grub_err_t err;
                       sym_addr -= offset;
                       sym_addr -= SUFFIX (entry_point);
-                      err = grub_arm64_reloc_xxxx26((grub_uint32_t *)target,
+                      if (!grub_arm_64_check_xxxx26_offset (sym_addr))
+                        grub_util_error ("%s", _("CALL26 Relocation out of range"));
+
+                      grub_arm64_set_xxxx26_offset((grub_uint32_t *)target,
                                                     sym_addr);
-                      if (err)
-                        grub_util_error ("%s", grub_errmsg);
                     }
                     break;
                   default:
@@ -764,32 +845,74 @@ SUFFIX (relocate_addresses) (Elf_Ehdr *e, Elf_Shdr *sections,
                       *target = grub_host_to_target32 (grub_target_to_host32 (*target) + sym_addr);
                     }
                     break;
+                    /* Happens when compiled with -march=armv4.
+                       Since currently we need at least armv5, keep bx as-is.
+                    */
+                  case R_ARM_V4BX:
+                    break;
                   case R_ARM_THM_CALL:
                   case R_ARM_THM_JUMP24:
+                  case R_ARM_THM_JUMP19:
                     {
                       grub_err_t err;
                       grub_util_info ("  THM_JUMP24:\ttarget=0x%08lx\toffset=(0x%08x)",        (unsigned long) target, sym_addr);
+                      if (!(sym_addr & 1))
+                        {
+                          grub_uint32_t tr_addr;
+                          grub_int32_t new_offset;
+                          tr_addr = (char *) tr - (char *) pe_target
+                            - target_section_addr;
+                          new_offset = sym_addr - tr_addr - 12;
+
+                          if (!grub_arm_jump24_check_offset (new_offset))
+                            return grub_util_error ("jump24 relocation out of range");
+
+                          tr[0] = grub_host_to_target32 (0x46c04778); /* bx pc; nop  */
+                          tr[1] = grub_host_to_target32 (((new_offset >> 2) & 0xffffff) | 0xea000000); /* b new_offset */
+                          tr += 2;
+                          sym_addr = tr_addr | 1;
+                        }
                       sym_addr -= offset;
                       /* Thumb instructions can be 16-bit aligned */
-                      err = grub_arm_reloc_thm_call ((grub_uint16_t *) target,
-                                                     sym_addr);
+                      if (ELF_R_TYPE (info) == R_ARM_THM_JUMP19)
+                        err = grub_arm_reloc_thm_jump19 ((grub_uint16_t *) target, sym_addr);
+                      else
+                        err = grub_arm_reloc_thm_call ((grub_uint16_t *) target,
+                                                       sym_addr);
                       if (err)
                         grub_util_error ("%s", grub_errmsg);
                     }
                     break;
-                  case R_ARM_THM_JUMP19:
+
+                  case R_ARM_CALL:
+                  case R_ARM_JUMP24:
                     {
                       grub_err_t err;
-                      grub_util_info ("  THM_JUMP19:\toffset=%d\t(0x%08x)",
-                                      sym_addr, sym_addr);
+                      grub_util_info ("  JUMP24:\ttarget=0x%08lx\toffset=(0x%08x)",    (unsigned long) target, sym_addr);
+                      if (sym_addr & 1)
+                        {
+                          grub_uint32_t tr_addr;
+                          grub_int32_t new_offset;
+                          tr_addr = (char *) tr - (char *) pe_target
+                            - target_section_addr;
+                          new_offset = sym_addr - tr_addr - 12;
+
+                          /* There is no immediate version of bx, only register one...  */
+                          tr[0] = grub_host_to_target32 (0xe59fc004); /* ldr   ip, [pc, #4] */
+                          tr[1] = grub_host_to_target32 (0xe08cc00f); /* add   ip, ip, pc */
+                          tr[2] = grub_host_to_target32 (0xe12fff1c); /* bx    ip */
+                          tr[3] = grub_host_to_target32 (new_offset | 1);
+                          tr += 4;
+                          sym_addr = tr_addr;
+                        }
                       sym_addr -= offset;
-
-                      /* Thumb instructions can be 16-bit aligned */
-                      err = grub_arm_reloc_thm_jump19 ((grub_uint16_t *) target, sym_addr);
+                      err = grub_arm_reloc_jump24 (target,
+                                                   sym_addr);
                       if (err)
                         grub_util_error ("%s", grub_errmsg);
                     }
                     break;
+
                   default:
                     grub_util_error (_("relocation 0x%x is not implemented yet!"), ELF_R_TYPE (info));
                     break;
@@ -1054,11 +1177,13 @@ SUFFIX (make_reloc_section) (Elf_Ehdr *e, void **out,
              case EM_ARM:
                switch (ELF_R_TYPE (info))
                  {
+                 case R_ARM_V4BX:
                    /* Relative relocations do not require fixup entries. */
                  case R_ARM_JUMP24:
                  case R_ARM_THM_CALL:
                  case R_ARM_THM_JUMP19:
                  case R_ARM_THM_JUMP24:
+                 case R_ARM_CALL:
                    {
                      Elf_Addr addr;
 
@@ -1280,7 +1405,7 @@ SUFFIX (load_image) (const char *kernel_path, size_t *exec_size,
   Elf_Off section_offset;
   Elf_Half section_entsize;
   grub_size_t kernel_size;
-  grub_size_t ia64jmp_off = 0, ia64_toff = 0, ia64_got_off = 0;
+  grub_size_t ia64jmp_off = 0, tramp_off = 0, ia64_got_off = 0;
   unsigned ia64jmpnum = 0;
   Elf_Shdr *symtab_section = 0;
   grub_size_t got = 0;
@@ -1373,6 +1498,21 @@ SUFFIX (load_image) (const char *kernel_path, size_t *exec_size,
            break;
          }
 
+#ifdef MKIMAGE_ELF32
+      if (image_target->elf_target == EM_ARM)
+       {
+         grub_size_t tramp;
+
+         *kernel_sz = ALIGN_UP (*kernel_sz, 16);
+
+         tramp = arm_get_trampoline_size (e, sections, section_entsize,
+                                          num_sections, image_target);
+
+         tramp_off = *kernel_sz;
+         *kernel_sz += ALIGN_UP (tramp, 16);
+       }
+#endif
+
 #ifdef MKIMAGE_ELF64
       if (image_target->elf_target == EM_IA_64)
        {
@@ -1382,7 +1522,7 @@ SUFFIX (load_image) (const char *kernel_path, size_t *exec_size,
 
          grub_ia64_dl_get_tramp_got_size (e, &tramp, &got);
 
-         ia64_toff = *kernel_sz;
+         tramp_off = *kernel_sz;
          *kernel_sz += ALIGN_UP (tramp, 16);
 
          ia64jmp_off = *kernel_sz;
@@ -1424,7 +1564,7 @@ SUFFIX (load_image) (const char *kernel_path, size_t *exec_size,
       SUFFIX (relocate_addresses) (e, sections, section_addresses, 
                                   section_entsize,
                                   num_sections, strtab,
-                                  out_img, ia64_toff, ia64_got_off,
+                                  out_img, tramp_off, ia64_got_off,
                                   image_target);
 
       *reloc_size = SUFFIX (make_reloc_section) (e, reloc_section,
index 11d4a474b617b03ed75f72836e896055615ef639..ad12f8a76f7d7b77bf2cc1ef9c4d19d407ba5017 100644 (file)
@@ -834,6 +834,94 @@ struct fixup_block_list
   struct grub_pe32_fixup_block b;
 };
 
+/*
+ * R_ARM_THM_CALL/THM_JUMP24
+ *
+ * Relocate Thumb (T32) instruction set relative branches:
+ *   B.W, BL and BLX
+ */
+static grub_err_t
+grub_arm_reloc_thm_call (grub_uint16_t *target, Elf32_Addr sym_addr)
+{
+  grub_int32_t offset;
+
+  offset = grub_arm_thm_call_get_offset (target);
+
+  grub_dprintf ("dl", "    sym_addr = 0x%08x", sym_addr);
+
+  offset += sym_addr;
+
+  grub_dprintf("dl", " BL*: target=%p, sym_addr=0x%08x, offset=%d\n",
+              target, sym_addr, offset);
+
+  /* Keep traditional (pre-Thumb2) limits on blx. In any case if the kernel
+     is bigger than 2M  (currently under 150K) then we probably have a problem
+     somewhere else.  */
+  if (offset < -0x200000 || offset >= 0x200000)
+    return grub_error (GRUB_ERR_BAD_MODULE,
+                      N_("THM_CALL Relocation out of range."));
+
+  grub_dprintf ("dl", "    relative destination = %p",
+               (char *) target + offset);
+
+  return grub_arm_thm_call_set_offset (target, offset);
+}
+
+/*
+ * R_ARM_THM_JUMP19
+ *
+ * Relocate conditional Thumb (T32) B<c>.W
+ */
+static grub_err_t
+grub_arm_reloc_thm_jump19 (grub_uint16_t *target, Elf32_Addr sym_addr)
+{
+  grub_int32_t offset;
+
+  if (!(sym_addr & 1))
+    return grub_error (GRUB_ERR_BAD_MODULE,
+                      N_("Relocation targeting wrong execution state"));
+
+  offset = grub_arm_thm_jump19_get_offset (target);
+
+  /* Adjust and re-truncate offset */
+  offset += sym_addr;
+
+  if (!grub_arm_thm_jump19_check_offset (offset))
+    return grub_error (GRUB_ERR_BAD_MODULE,
+                      N_("THM_JUMP19 Relocation out of range."));
+
+  grub_arm_thm_jump19_set_offset (target, offset);
+
+  return GRUB_ERR_NONE;
+}
+
+/*
+ * R_ARM_JUMP24
+ *
+ * Relocate ARM (A32) B
+ */
+static grub_err_t
+grub_arm_reloc_jump24 (grub_uint32_t *target, Elf32_Addr sym_addr)
+{
+  grub_int32_t offset;
+
+  if (sym_addr & 1)
+    return grub_error (GRUB_ERR_BAD_MODULE,
+                      N_("Relocation targeting wrong execution state"));
+
+  offset = grub_arm_jump24_get_offset (target);
+  offset += sym_addr;
+
+  if (!grub_arm_jump24_check_offset (offset))
+    return grub_error (GRUB_ERR_BAD_MODULE,
+                      N_("JUMP24 Relocation out of range."));
+
+
+  grub_arm_jump24_set_offset (target, offset);
+
+  return GRUB_ERR_NONE;
+}
+
 #pragma GCC diagnostic ignored "-Wcast-align"
 
 #define MKIMAGE_ELF32 1