Bug 898998 - Turn BL into BLX when doing thumb call relocations and the target is ARM. r=nfroyd
authorMike Hommey <mh+mozilla@glandium.org>
Tue, 30 Jul 2013 08:57:28 +0900
changeset 140494 09fa1683d16a33f08562545002245bf45527b219
parent 140493 7506c6ee2e8b25f5893dc23e1b392ec5828ba545
child 140495 1f59dc8474a5dd5fe4a48efdb35111d92d4bbe83
push id1970
push userryanvm@gmail.com
push dateTue, 30 Jul 2013 17:12:32 +0000
treeherderfx-team@72240998c094 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnfroyd
bugs898998
milestone25.0a1
Bug 898998 - Turn BL into BLX when doing thumb call relocations and the target is ARM. r=nfroyd
build/unix/elfhack/elfhack.cpp
build/unix/elfhack/inject.c
--- a/build/unix/elfhack/elfhack.cpp
+++ b/build/unix/elfhack/elfhack.cpp
@@ -235,33 +235,50 @@ private:
                ARM Architecture Reference Manual ARMĀ® v7-A and ARMĀ® v7-R edition, A8.6.16
                We limit ourselves to Encoding T4 of b.w and Encoding T1 of bl.
                We don't care about sign_extend because the only case where this is
                going to be used only jumps forward. */
             Elf32_Addr tmp = (Elf32_Addr) (addr - offset - base_addr);
             unsigned int word0 = addend & 0xffff,
                          word1 = addend >> 16;
 
-            if (((word0 & 0xf800) != 0xf000) || ((word1 & 0x9000) != 0x9000))
+            /* Encoding T4 of B.W is 10x1 ; Encoding T1 of BL is 11x1. */
+            unsigned int type = (word1 & 0xd000) >> 12;
+            if (((word0 & 0xf800) != 0xf000) || ((type & 0x9) != 0x9))
                 throw std::runtime_error("R_ARM_THM_JUMP24/R_ARM_THM_CALL relocation only supported for B.W <label> and BL <label>");
 
+            /* When the target address points to ARM code, switch a BL to a
+             * BLX. This however can't be done with a B.W without adding a
+             * trampoline, which is not supported as of now. */
+            if ((addr & 0x1) == 0) {
+                if (type == 0x9)
+                    throw std::runtime_error("R_ARM_THM_JUMP24/R_ARM_THM_CALL relocation only supported for BL <label> when label points to ARM code");
+                /* The address of the target is always relative to a 4-bytes
+                 * aligned address, so if the address of the BL instruction is
+                 * not 4-bytes aligned, adjust for it. */
+                if ((base_addr + offset) & 0x2)
+                    tmp += 2;
+                /* Encoding T2 of BLX is 11x0. */
+                type = 0xc;
+            }
+
             unsigned int s = (word0 & (1 << 10)) >> 10;
             unsigned int j1 = (word1 & (1 << 13)) >> 13;
             unsigned int j2 = (word1 & (1 << 11)) >> 11;
             unsigned int i1 = j1 ^ s ? 0 : 1;
             unsigned int i2 = j2 ^ s ? 0 : 1;
 
             tmp += ((s << 24) | (i1 << 23) | (i2 << 22) | ((word0 & 0x3ff) << 12) | ((word1 & 0x7ff) << 1));
 
             s = (tmp & (1 << 24)) >> 24;
             j1 = ((tmp & (1 << 23)) >> 23) ^ !s;
             j2 = ((tmp & (1 << 22)) >> 22) ^ !s;
 
             return 0xf000 | (s << 10) | ((tmp & (0x3ff << 12)) >> 12) |
-                   ((word1 & 0xd000) << 16) | (j1 << 29) | (j2 << 27) | ((tmp & 0xffe) << 15);
+                   (type << 28) | (j1 << 29) | (j2 << 27) | ((tmp & 0xffe) << 15);
         }
     };
 
     class gotoff_relocation {
     public:
         Elf32_Addr operator()(unsigned int base_addr, Elf32_Off offset,
                               Elf32_Word addend, unsigned int addr)
         {
--- a/build/unix/elfhack/inject.c
+++ b/build/unix/elfhack/inject.c
@@ -17,22 +17,25 @@
 #define Elf_Addr Elf64_Addr
 #endif
 
 extern __attribute__((visibility("hidden"))) void original_init(int argc, char **argv, char **env);
 
 extern __attribute__((visibility("hidden"))) Elf32_Rel relhack[];
 extern __attribute__((visibility("hidden"))) Elf_Ehdr elf_header;
 
-void init(int argc, char **argv, char **env)
+int init(int argc, char **argv, char **env)
 {
     Elf32_Rel *rel;
     Elf_Addr *ptr, *start;
     for (rel = relhack; rel->r_offset; rel++) {
         start = (Elf_Addr *)((intptr_t)&elf_header + rel->r_offset);
         for (ptr = start; ptr < &start[rel->r_info]; ptr++)
             *ptr += (intptr_t)&elf_header;
     }
 
 #ifndef NOINIT
     original_init(argc, argv, env);
 #endif
+    // Ensure there is no tail-call optimization, avoiding the use of the
+    // B.W instruction in Thumb for the call above.
+    return 0;
 }