1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
|
# The ARM->Thumb glue uses an ldr of the target function address, this
# simply doesn't work for PIC code, changed to use 4 word PIC glue
#
--- binutils-2.16/.pc/binutils-2.16-thumb-glue.patch/bfd/elf32-arm.c 2005-09-18 03:52:15.465165051 -0700
+++ binutils-2.16/bfd/elf32-arm.c 2005-09-18 03:52:33.546302825 -0700
@@ -1493,19 +1493,20 @@
return myh;
}
-/* ARM->Thumb glue:
+/* ARM->Thumb glue (PIC version):
.arm
__func_from_arm:
ldr r12, __func_addr
+ add r12, r12, pc @ pc is __func_addr, so r12 is func
bx r12
__func_addr:
- .word func @ behave as if you saw a ARM_32 reloc. */
+ .word func-.+1 @ offset to actual function, low bit set */
-#define ARM2THUMB_GLUE_SIZE 12
-static const insn32 a2t1_ldr_insn = 0xe59fc000;
-static const insn32 a2t2_bx_r12_insn = 0xe12fff1c;
-static const insn32 a2t3_func_addr_insn = 0x00000001;
+#define ARM2THUMB_GLUE_SIZE 16
+static const insn32 a2t1_ldr_insn = 0xe59fc004;
+static const insn32 a2t2_add_r12_insn = 0xe08fc00c;
+static const insn32 a2t3_bx_r12_insn = 0xe12fff1c;
/* Thumb->ARM: Thumb->(non-interworking aware) ARM
@@ -2187,6 +2188,8 @@
if ((my_offset & 0x01) == 0x01)
{
+ long int ret_offset;
+
if (sym_sec != NULL
&& sym_sec->owner != NULL
&& !INTERWORK_FLAG (sym_sec->owner))
@@ -2203,12 +2206,31 @@
bfd_put_32 (output_bfd, (bfd_vma) a2t1_ldr_insn,
s->contents + my_offset);
- bfd_put_32 (output_bfd, (bfd_vma) a2t2_bx_r12_insn,
+ bfd_put_32 (output_bfd, (bfd_vma) a2t2_add_r12_insn,
s->contents + my_offset + 4);
- /* It's a thumb address. Add the low order bit. */
- bfd_put_32 (output_bfd, val | a2t3_func_addr_insn,
+ bfd_put_32 (output_bfd, (bfd_vma) a2t3_bx_r12_insn,
s->contents + my_offset + 8);
+
+ /* Calculate the offset to the actual function. */
+ ret_offset =
+ /* Address of destination of the stub. */
+ ((bfd_signed_vma) val)
+ - ((bfd_signed_vma)
+ /* Offset from the start of the current section
+ to the start of the stubs. */
+ (s->output_offset
+ /* Offset of the start of this stub from the start of the stubs. */
+ + my_offset
+ /* Address of the start of the current section. */
+ + s->output_section->vma)
+ /* The word is 12 bytes into the stub. */
+ + 12
+ /* The destination is a thumb function so the bottom bit must be set. */
+ - 1);
+
+ bfd_put_32 (output_bfd, (bfd_vma) ret_offset,
+ s->contents + my_offset + 12);
}
BFD_ASSERT (my_offset <= globals->arm_glue_size);
|