]> git.ipfire.org Git - thirdparty/grub.git/commitdiff
Add support for a new magic symbol _gp_disp on mips to handle PIC
authorVladimir Serbinenko <phcoder@gmail.com>
Fri, 22 Nov 2013 03:56:10 +0000 (04:56 +0100)
committerVladimir Serbinenko <phcoder@gmail.com>
Fri, 22 Nov 2013 03:56:10 +0000 (04:56 +0100)
binaries.

ChangeLog
grub-core/genmoddep.awk
grub-core/kern/mips/dl.c

index 856185496f4e8db1a03d1a281cab4a4b20b10d92..8fac253ef818d46be8b7aaf0bea9dc62b0267dec 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,8 @@
+2013-11-22  Vladimir Serbinenko  <phcoder@gmail.com>
+
+       Add support for a new magic symbol _gp_disp on mips to handle PIC
+       binaries.
+
 2013-11-22  Vladimir Serbinenko  <phcoder@gmail.com>
 
        Use $t9 for indirect calls from asm to C as PIC ABI requires.
index 9ffea7309dc0f7d925d0799fd51440325f642840..dc555b93ccb7f7f12cda40395047c3573dfc3cd3 100644 (file)
@@ -23,7 +23,7 @@ BEGIN {
     } else if ($1 == "undefined") {
       if ($3 in symtab)
        modtab[$2] = modtab[$2] " " symtab[$3];
-      else if ($3 != "__gnu_local_gp") {
+      else if (and ($3 != "__gnu_local_gp", $3 != "_gp_disp")) {
        printf "%s in %s is not defined\n", $3, $2 >"/dev/stderr";
        error++;
       }
index 36ca9a2259fc774ff513bf67c1e3f98efe2a7db9..111241831ee3c94101dd6e17ebb360dc48a20b8e 100644 (file)
@@ -27,6 +27,7 @@
 
 /* Dummy __gnu_local_gp. Resolved by linker.  */
 static char __gnu_local_gp_dummy;
+static char _gp_disp_dummy;
 
 /* Check if EHDR is a valid ELF header.  */
 grub_err_t
@@ -164,6 +165,7 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr)
              {
                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,
@@ -172,9 +174,17 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr)
                addr = (grub_uint8_t *) ((char *) seg->addr + rel->r_offset);
                sym = (Elf_Sym *) ((char *) mod->symtab
                                     + entsize * ELF_R_SYM (rel->r_info));
-               if (sym->st_value == (grub_addr_t) &__gnu_local_gp_dummy)
-                 sym->st_value = (grub_addr_t) gp;
-
+               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:
@@ -190,7 +200,7 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr)
                         treated as signed. Hence add 0x8000 to compensate. 
                       */
                      value = (*(grub_uint16_t *) addr << 16)
-                       + sym->st_value + 0x8000;
+                       + sym_value + 0x8000;
                      for (rel2 = rel + 1; rel2 < max; rel2++)
                        if (ELF_R_SYM (rel2->r_info)
                            == ELF_R_SYM (rel->r_info)
@@ -211,13 +221,13 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr)
 #ifdef GRUB_CPU_WORDS_BIGENDIAN
                    addr += 2;
 #endif
-                   *(grub_uint16_t *) addr += (sym->st_value) & 0xffff;
+                   *(grub_uint16_t *) addr += sym_value & 0xffff;
                    break;
                  case R_MIPS_32:
-                   *(grub_uint32_t *) addr += sym->st_value;
+                   *(grub_uint32_t *) addr += sym_value;
                    break;
                  case R_MIPS_GPREL32:
-                   *(grub_uint32_t *) addr = sym->st_value
+                   *(grub_uint32_t *) addr = sym_value
                      + *(grub_uint32_t *) addr + gp0 - (grub_uint32_t)gp;
                    break;
 
@@ -227,7 +237,7 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr)
                      grub_uint32_t raw;
                      raw = (*(grub_uint32_t *) addr) & 0x3ffffff;
                      value = raw << 2;
-                     value += sym->st_value;
+                     value += sym_value;
                      raw = (value >> 2) & 0x3ffffff;
                        
                      *(grub_uint32_t *) addr = 
@@ -240,7 +250,7 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr)
 #ifdef GRUB_CPU_WORDS_BIGENDIAN
                    addr += 2;
 #endif
-                   *gpptr = sym->st_value + *(grub_uint16_t *) addr;
+                   *gpptr = sym_value + *(grub_uint16_t *) addr;
                    *(grub_uint16_t *) addr
                      = sizeof (grub_uint32_t) * (gpptr - gp);
                    gpptr++;
@@ -266,5 +276,6 @@ void
 grub_arch_dl_init_linker (void)
 {
   grub_dl_register_symbol ("__gnu_local_gp", &__gnu_local_gp_dummy, 0, 0);
+  grub_dl_register_symbol ("_gp_disp", &_gp_disp_dummy, 0, 0);
 }