From fb228a2d94a130eda38c8794b0f264aa9f0e4714 Mon Sep 17 00:00:00 2001 From: Roland McGrath Date: Fri, 5 Oct 2012 09:39:23 -0700 Subject: [PATCH] Clean up R_ARM_PC24 handling. --- ports/ChangeLog.arm | 6 ++ ports/sysdeps/arm/dl-machine.h | 120 ++++++++++++++------------------- 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 + + * 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 * 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; - if (! fix_page) + /* 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 (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: