Greetings everyone,

while working on adding support for R_ARM_THM_JUMP24 to a PLT entry, I needed 
to store an extra information for some symbols about the nature of the call 
sites of a PLT entry. Since most symbols don't have a PLT entry, I was 
reluctant in adding even a byte for all symbols in a set of object file. Then I 
realized bit 0 of entries in the got_offsets array (in TCCState) is always 0 
and could be reused for my purpose.

Currently (see attached patch 0002-Generate-PLT-thumb-stub-only-when-
necessary.patch), I reuse the bit 0 without using bitfields. This has the 
advantage of being able to reuse put_got_offset() function by changing the 
assignment of the value with a bit ORing but doesn't mark explicitely to the 
compiler what's going on. On the other hand, I could use bitfields but that 
would require some more modifications to put_got_offset to be able to set a 
got_offset and/or the bit 0.

Note that in both cases all access to got_offset needs to be changed: if 
bitfields is used the value of got_offset needs to be shifted by one bit to the 
left, else, the value needs to be ANDed with -2 to ignore bit 0.

What's your opinion about it? What's the best approach? Is there another one 
possible? (I initially thought about storing the extra information in a hash 
table but it requires to add an hashtable implementation while I could reuse 
the unused bit of got_offsets).

Best regards,

Thomas Preud'homme
From f2365979217811d651dd9c029196906fede16084 Mon Sep 17 00:00:00 2001
From: Thomas Preud'homme <[email protected]>
Date: Sun, 28 Oct 2012 19:38:38 +0100
Subject: [PATCH 1/2] Support R_ARM_THM_JUMP24 relocation to plt

Add thumb stubs switching from thumb mode to arm mode to *all* PLT
entries so that R_ARM_THM_JUMP24 relocations to PLT entries can be
satisfied.
---
 tccelf.c |   26 ++++++++++++++++++--------
 1 file changed, 18 insertions(+), 8 deletions(-)

diff --git a/tccelf.c b/tccelf.c
index e5bb1cb..d0e4c3b 100644
--- a/tccelf.c
+++ b/tccelf.c
@@ -638,7 +638,8 @@ ST_FUNC void relocate_section(TCCState *s1, Section *s)
         case R_ARM_THM_JUMP24:
 	    {
                 int x, hi, lo, s, j1, j2, i1, i2, imm10, imm11;
-                int to_thumb, is_call, blx_bit = 1 << 12;
+                int to_thumb, is_call, to_plt, blx_bit = 1 << 12;
+                Section *plt;
 
                 /* weak reference */
                 if (sym->st_shndx == SHN_UNDEF &&
@@ -662,9 +663,14 @@ ST_FUNC void relocate_section(TCCState *s1, Section *s)
 
                 /* Relocation infos */
                 to_thumb = val & 1;
+                plt = s1->plt;
+                to_plt = (val >= plt->sh_addr) &&
+                         (val < plt->sh_addr + plt->data_offset);
                 is_call = (type == R_ARM_THM_CALL);
 
                 /* Compute final offset */
+                if (to_plt && !is_call) /* Point to 1st instr of Thumb stub */
+                    x -= 4;
                 x += val - addr;
                 if (!to_thumb && is_call) {
                     blx_bit = 0; /* bl -> blx */
@@ -675,9 +681,9 @@ ST_FUNC void relocate_section(TCCState *s1, Section *s)
                    * offset must not be out of range
                    * if target is to be entered in arm mode:
                      - bit 1 must not set
-                     - instruction must be a call (bl) */
+                     - instruction must be a call (bl) or a jump to PLT */
                 if (!to_thumb || x >= 0x1000000 || x < -0x1000000)
-                    if (to_thumb || (val & 2) || !is_call)
+                    if (to_thumb || (val & 2) || (!is_call && !to_plt))
                         tcc_error("can't relocate value at %x",addr);
 
                 /* Compute and store final offset */
@@ -1096,10 +1102,13 @@ static void put_got_entry(TCCState *s1,
                 put32(p + 12, 0xe5bef008);
             }
 
-            p = section_ptr_add(plt, 16);
-            put32(p  , 0xe59fc004);
-            put32(p+4, 0xe08fc00c);
-            put32(p+8, 0xe59cf000);
+            p = section_ptr_add(plt, 20);
+            put32(p  , 0x4778); // bx pc
+            put32(p+2, 0x46c0); // nop
+            p += 4;
+            put32(p  , 0xe59fc004); // ldr ip, [pc, #4] // offset in GOT
+            put32(p+4, 0xe08fc00c); // add ip, pc, ip // absolute address or offset
+            put32(p+8, 0xe59cf000); // ldr pc, [ip] // load absolute address or load offset
             put32(p+12, s1->got->data_offset);
 
             /* the symbol is modified so that it will be relocated to
@@ -2057,8 +2066,9 @@ static int elf_output_file(TCCState *s1, const char *filename)
 #elif defined(TCC_TARGET_ARM)
                     int x;
                     x=s1->got->sh_addr - s1->plt->sh_addr - 12;
-                    p +=16;
+                    p += 16;
                     while (p < p_end) {
+                        p += 4;
                         put32(p + 12, x + get32(p + 12) + s1->plt->data - p);
                         p += 16;
                     }
-- 
1.7.10.4

From 2cb6e93e9af77a2e4ff20bd5119dbd7fdc6aeea4 Mon Sep 17 00:00:00 2001
From: Thomas Preud'homme <[email protected]>
Date: Sun, 4 Nov 2012 00:40:05 +0100
Subject: [PATCH 2/2] Generate PLT thumb stub only when necessary

Generate PLT thumb stub for an ARM PLT entry only when at least one
Thumb instruction branches to that entry.

Warning: To save space, this commit reuses the bit 0 of entries of
got_offsets array. Make sure to ignore this bit when using the GOT
offset in one of these entries.
---
 tcc.h    |    2 +-
 tccelf.c |   41 +++++++++++++++++++++++++++++------------
 2 files changed, 30 insertions(+), 13 deletions(-)

diff --git a/tcc.h b/tcc.h
index 4e88782..25c160c 100644
--- a/tcc.h
+++ b/tcc.h
@@ -527,7 +527,7 @@ struct TCCState {
     /* got handling */
     Section *got;
     Section *plt;
-    unsigned long *got_offsets;
+    unsigned long *got_offsets; /* bit 0 set = PLT entry requires thumb stub */
     int nb_got_offsets;
     /* give the correspondance from symtab indexes to dynsym indexes */
     int *symtab_to_dynsym;
diff --git a/tccelf.c b/tccelf.c
index d0e4c3b..a12dd24 100644
--- a/tccelf.c
+++ b/tccelf.c
@@ -581,7 +581,7 @@ ST_FUNC void relocate_section(TCCState *s1, Section *s)
             break;
         case R_386_GOT32:
             /* we load the got offset */
-            *(int *)ptr += s1->got_offsets[sym_index];
+            *(int *)ptr += s1->got_offsets[sym_index] & -2;
             break;
         case R_386_16:
             if (s1->output_format != TCC_OUTPUT_FORMAT_BINARY) {
@@ -758,7 +758,7 @@ ST_FUNC void relocate_section(TCCState *s1, Section *s)
             break;
         case R_ARM_GOT_BREL:
             /* we load the got offset */
-            *(int *)ptr += s1->got_offsets[sym_index];
+            *(int *)ptr += s1->got_offsets[sym_index] & -2;
             break;
         case R_ARM_COPY:
             break;
@@ -864,14 +864,14 @@ ST_FUNC void relocate_section(TCCState *s1, Section *s)
             }
 #endif
             *(int *)ptr += (s1->got->sh_addr - addr +
-                            s1->got_offsets[sym_index] - 4);
+                            (s1->got_offsets[sym_index] & -2) - 4);
             break;
         case R_X86_64_GOTTPOFF:
             *(int *)ptr += val - s1->got->sh_addr;
             break;
         case R_X86_64_GOT32:
             /* we load the got offset */
-            *(int *)ptr += s1->got_offsets[sym_index];
+            *(int *)ptr += s1->got_offsets[sym_index] & -2;
             break;
 #else
 #error unsupported processor
@@ -957,7 +957,7 @@ static void put_got_offset(TCCState *s1, int index, unsigned long val)
                (n - s1->nb_got_offsets) * sizeof(unsigned long));
         s1->nb_got_offsets = n;
     }
-    s1->got_offsets[index] = val;
+    s1->got_offsets[index] |= val;
 }
 
 /* XXX: suppress that */
@@ -1022,7 +1022,7 @@ static void put_got_entry(TCCState *s1,
 
     /* if a got entry already exists for that symbol, no need to add one */
     if (sym_index < s1->nb_got_offsets &&
-        s1->got_offsets[sym_index] != 0)
+        s1->got_offsets[sym_index] & -2)
         return;
     
     put_got_offset(s1, sym_index, s1->got->data_offset);
@@ -1102,10 +1102,13 @@ static void put_got_entry(TCCState *s1,
                 put32(p + 12, 0xe5bef008);
             }
 
-            p = section_ptr_add(plt, 20);
-            put32(p  , 0x4778); // bx pc
-            put32(p+2, 0x46c0); // nop
-            p += 4;
+            if (s1->got_offsets[sym_index] & 1) {
+                p = section_ptr_add(plt, 20);
+                put32(p  , 0x4778); // bx pc
+                put32(p+2, 0x46c0); // nop
+                p += 4;
+            } else
+                p = section_ptr_add(plt, 16);
             put32(p  , 0xe59fc004); // ldr ip, [pc, #4] // offset in GOT
             put32(p+4, 0xe08fc00c); // add ip, pc, ip // absolute address or offset
             put32(p+8, 0xe59cf000); // ldr pc, [ip] // load absolute address or load offset
@@ -1494,7 +1497,7 @@ ST_FUNC void fill_got_entry(TCCState *s1, ElfW_Rel *rel)
 
 	if (sym_index >= s1->nb_got_offsets)
 		return;
-	offset = s1->got_offsets[sym_index];
+	offset = s1->got_offsets[sym_index] & -2;
 	section_reserve(s1->got, offset + PTR_SIZE);
 #ifdef TCC_TARGET_X86_64
 	/* only works for x86-64 */
@@ -2068,7 +2071,8 @@ static int elf_output_file(TCCState *s1, const char *filename)
                     x=s1->got->sh_addr - s1->plt->sh_addr - 12;
                     p += 16;
                     while (p < p_end) {
-                        p += 4;
+                        if (get32(p) == 0x46c04778) /* PLT Thumb stub present */
+                            p += 4;
                         put32(p + 12, x + get32(p + 12) + s1->plt->data - p);
                         p += 16;
                     }
@@ -2597,6 +2601,19 @@ ST_FUNC int tcc_load_object_file(TCCState *s1,
                 rel->r_info = ELFW(R_INFO)(sym_index, type);
                 /* offset the relocation offset */
                 rel->r_offset += offseti;
+#ifdef TCC_TARGET_ARM
+                /* Jumps and branches from a Thumb code to a PLT entry need
+                   special handling since PLT entries are ARM code.
+                   Unconditional bl instructions referencing PLT entries are
+                   handled by converting these instructions into blx
+                   instructions. Other case of instructions referencing a PLT
+                   entry require to add a Thumb stub before the PLT entry to
+                   switch to ARM mode. We set bit 0 of the got offset of a
+                   symbol to indicate such a case. */
+                if (type == R_ARM_THM_JUMP24) {
+                    put_got_offset(s1, sym_index, 1);
+                }
+#endif
             }
             break;
         default:
-- 
1.7.10.4

Attachment: signature.asc
Description: This is a digitally signed message part.

_______________________________________________
Tinycc-devel mailing list
[email protected]
https://lists.nongnu.org/mailman/listinfo/tinycc-devel

Reply via email to