aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoland McGrath <roland@hack.frob.com>2012-10-05 09:39:23 -0700
committerRoland McGrath <roland@hack.frob.com>2012-10-05 12:56:00 -0700
commitfb228a2d94a130eda38c8794b0f264aa9f0e4714 (patch)
tree441564b6fb4086db2ad9e4a566607542d320737f
parent53cea63e99264bf81b278546581d6c2efc2c3fc5 (diff)
downloadglibc-fb228a2d94a130eda38c8794b0f264aa9f0e4714.tar
glibc-fb228a2d94a130eda38c8794b0f264aa9f0e4714.tar.gz
glibc-fb228a2d94a130eda38c8794b0f264aa9f0e4714.tar.bz2
glibc-fb228a2d94a130eda38c8794b0f264aa9f0e4714.zip
Clean up R_ARM_PC24 handling.
-rw-r--r--ports/ChangeLog.arm6
-rw-r--r--ports/sysdeps/arm/dl-machine.h120
2 files changed, 57 insertions, 69 deletions
diff --git a/ports/ChangeLog.arm b/ports/ChangeLog.arm
index cbbec9b5d9..678b2ab78c 100644
--- a/ports/ChangeLog.arm
+++ b/ports/ChangeLog.arm
@@ -1,3 +1,9 @@
+2012-10-05 Roland McGrath <roland@hack.frob.com>
+
+ * sysdeps/arm/dl-machine.h (fix_bad_pc24): Rewritten, replaced with ...
+ (relocate_pc24): ... this new function.
+ (elf_machine_rel, elf_machine_rela): Update callers.
+
2012-10-02 Siddhesh Poyarekar <siddhesh@redhat.com>
* sysdeps/unix/sysv/linux/arm/nptl/lowlevellock.h: Fix clone
diff --git a/ports/sysdeps/arm/dl-machine.h b/ports/sysdeps/arm/dl-machine.h
index 343a83e988..3b25e0f0f0 100644
--- a/ports/sysdeps/arm/dl-machine.h
+++ b/ports/sysdeps/arm/dl-machine.h
@@ -302,36 +302,56 @@ elf_machine_plt_value (struct link_map *map, const Elf32_Rel *reloc,
#define ARCH_LA_PLTEXIT arm_gnu_pltexit
#ifdef RESOLVE_MAP
-
-/* Deal with an out-of-range PC24 reloc. */
-auto Elf32_Addr
-fix_bad_pc24 (Elf32_Addr *const reloc_addr, Elf32_Addr value)
+/* Handle a PC24 reloc, including the out-of-range case. */
+auto void
+relocate_pc24 (struct link_map *map, Elf32_Addr value,
+ Elf32_Addr *const reloc_addr, Elf32_Sword addend)
{
- static void *fix_page;
- static unsigned int fix_offset;
- static size_t pagesize;
- Elf32_Word *fix_address;
+ Elf32_Addr new_value;
+
+ /* Set NEW_VALUE based on V, and return true iff it overflows 24 bits. */
+ inline bool set_new_value (Elf32_Addr v)
+ {
+ new_value = v + addend - (Elf32_Addr) reloc_addr;
+ Elf32_Addr topbits = new_value & 0xfe000000;
+ return topbits != 0xfe000000 && topbits != 0x00000000;
+ }
- if (! fix_page)
+ if (set_new_value (value))
{
- if (! pagesize)
- pagesize = getpagesize ();
- fix_page = mmap (NULL, pagesize, PROT_READ | PROT_WRITE | PROT_EXEC,
- MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
- if (! fix_page)
- assert (! "could not map page for fixup");
- fix_offset = 0;
+ /* The PC-relative address doesn't fit in 24 bits! */
+
+ static void *fix_page;
+ static size_t fix_offset;
+ if (fix_page == NULL)
+ {
+ void *new_page = __mmap (NULL, GLRO(dl_pagesize),
+ PROT_READ | PROT_WRITE | PROT_EXEC,
+ MAP_PRIVATE | MAP_ANON, -1, 0);
+ if (new_page == MAP_FAILED)
+ _dl_signal_error (0, map->l_name, NULL,
+ "could not map page for fixup");
+ fix_page = new_page;
+ assert (fix_offset == 0);
+ }
+
+ Elf32_Word *fix_address = fix_page + fix_offset;
+ fix_address[0] = 0xe51ff004; /* ldr pc, [pc, #-4] */
+ fix_address[1] = value;
+
+ fix_offset += sizeof fix_address[0] * 2;
+ if (fix_offset >= GLRO(dl_pagesize))
+ {
+ fix_page = NULL;
+ fix_offset = 0;
+ }
+
+ if (set_new_value ((Elf32_Addr) fix_address))
+ _dl_signal_error (0, map->l_name, NULL,
+ "R_ARM_PC24 relocation out of range");
}
- fix_address = (Elf32_Word *)(fix_page + fix_offset);
- fix_address[0] = 0xe51ff004; /* ldr pc, [pc, #-4] */
- fix_address[1] = value;
-
- fix_offset += 8;
- if (fix_offset >= pagesize)
- fix_page = NULL;
-
- return (Elf32_Addr)fix_address;
+ *reloc_addr = (*reloc_addr & 0xff000000) | ((new_value >> 2) & 0x00ffffff);
}
/* Perform the relocation specified by RELOC and SYM (which is fully resolved).
@@ -473,30 +493,11 @@ elf_machine_rel (struct link_map *map, const Elf32_Rel *reloc,
}
break;
case R_ARM_PC24:
- {
- Elf32_Sword addend;
- Elf32_Addr newvalue, topbits;
-
- addend = *reloc_addr & 0x00ffffff;
- if (addend & 0x00800000) addend |= 0xff000000;
-
- newvalue = value - (Elf32_Addr)reloc_addr + (addend << 2);
- topbits = newvalue & 0xfe000000;
- if (topbits != 0xfe000000 && topbits != 0x00000000)
- {
- newvalue = fix_bad_pc24(reloc_addr, value)
- - (Elf32_Addr)reloc_addr + (addend << 2);
- topbits = newvalue & 0xfe000000;
- if (topbits != 0xfe000000 && topbits != 0x00000000)
- {
- _dl_signal_error (0, map->l_name, NULL,
- "R_ARM_PC24 relocation out of range");
- }
- }
- newvalue >>= 2;
- value = (*reloc_addr & 0xff000000) | (newvalue & 0x00ffffff);
- *reloc_addr = value;
- }
+ relocate_pc24 (map, value, reloc_addr,
+ /* Sign-extend the 24-bit addend in the
+ instruction (which counts instructions), and
+ then shift it up two so as to count bytes. */
+ (((Elf32_Sword) *reloc_addr << 8) >> 8) << 2);
break;
#if !defined RTLD_BOOTSTRAP
case R_ARM_TLS_DTPMOD32:
@@ -589,26 +590,7 @@ elf_machine_rela (struct link_map *map, const Elf32_Rela *reloc,
*reloc_addr = value + reloc->r_addend;
break;
case R_ARM_PC24:
- {
- Elf32_Addr newvalue, topbits;
-
- newvalue = value + reloc->r_addend - (Elf32_Addr)reloc_addr;
- topbits = newvalue & 0xfe000000;
- if (topbits != 0xfe000000 && topbits != 0x00000000)
- {
- newvalue = fix_bad_pc24(reloc_addr, value)
- - (Elf32_Addr)reloc_addr + (reloc->r_addend << 2);
- topbits = newvalue & 0xfe000000;
- if (topbits != 0xfe000000 && topbits != 0x00000000)
- {
- _dl_signal_error (0, map->l_name, NULL,
- "R_ARM_PC24 relocation out of range");
- }
- }
- newvalue >>= 2;
- value = (*reloc_addr & 0xff000000) | (newvalue & 0x00ffffff);
- *reloc_addr = value;
- }
+ relocate_pc24 (map, value, reloc_addr, reloc->r_addend);
break;
#if !defined RTLD_BOOTSTRAP
case R_ARM_TLS_DTPMOD32: