linker: Move ARM interworking note to SymbolExtras.c
[ghc.git] / rts / linker / SymbolExtras.c
1 /* --------------------------------------------------------------------------
2 * Symbol Extras.
3 * This is about allocating a small chunk of memory for every symbol in the
4 * object file. We make sure that the SymboLExtras are always "in range" of
5 * limited-range PC-relative instructions on various platforms by allocating
6 * them right next to the object code itself.
7 *
8 * This implementation is shared by the MachO and ELF implementations. Windows'
9 * PEi386 has its own implementation of symbol extras.
10 */
11
12 #include "LinkerInternals.h"
13
14 #if NEED_SYMBOL_EXTRAS
15 #if !defined(x86_64_HOST_ARCH) || !defined(mingw32_HOST_OS)
16
17 #include "RtsUtils.h"
18 #include "sm/OSMem.h"
19 #include "linker/SymbolExtras.h"
20 #include "linker/M32Alloc.h"
21
22 #include <string.h>
23 #if RTS_LINKER_USE_MMAP
24 #include <sys/mman.h>
25 #endif /* RTS_LINKER_USE_MMAP */
26
27 /*
28 ocAllocateSymbolExtras
29
30 Allocate additional space at the end of the object file image to make room
31 for jump islands (powerpc, x86_64, arm) and GOT entries (x86_64).
32
33 PowerPC relative branch instructions have a 24 bit displacement field.
34 As PPC code is always 4-byte-aligned, this yields a +-32MB range.
35 If a particular imported symbol is outside this range, we have to redirect
36 the jump to a short piece of new code that just loads the 32bit absolute
37 address and jumps there.
38 On x86_64, PC-relative jumps and PC-relative accesses to the GOT are limited
39 to 32 bits (+-2GB).
40
41 This function just allocates space for one SymbolExtra for every
42 undefined symbol in the object file. The code for the jump islands is
43 filled in by makeSymbolExtra below.
44 */
45
46 int ocAllocateSymbolExtras( ObjectCode* oc, int count, int first )
47 {
48 size_t n;
49
50 if (RTS_LINKER_USE_MMAP && USE_CONTIGUOUS_MMAP) {
51 n = roundUpToPage(oc->fileSize);
52
53 /* Keep image and symbol_extras contiguous */
54 void *new = mmapForLinker(n + (sizeof(SymbolExtra) * count),
55 MAP_ANONYMOUS, -1, 0);
56 if (new) {
57 memcpy(new, oc->image, oc->fileSize);
58 if (oc->imageMapped) {
59 munmap(oc->image, n);
60 }
61 oc->image = new;
62 oc->imageMapped = rtsTrue;
63 oc->fileSize = n + (sizeof(SymbolExtra) * count);
64 oc->symbol_extras = (SymbolExtra *) (oc->image + n);
65 }
66 else {
67 oc->symbol_extras = NULL;
68 return 0;
69 }
70 }
71 else if( count > 0 ) {
72 if (RTS_LINKER_USE_MMAP) {
73 n = roundUpToPage(oc->fileSize);
74
75 oc->symbol_extras = m32_alloc(sizeof(SymbolExtra) * count, 8);
76 if (oc->symbol_extras == NULL) return 0;
77 }
78 else {
79 // round up to the nearest 4
80 int aligned = (oc->fileSize + 3) & ~3;
81 int misalignment = oc->misalignment;
82
83 oc->image -= misalignment;
84 oc->image = stgReallocBytes( oc->image,
85 misalignment +
86 aligned + sizeof (SymbolExtra) * count,
87 "ocAllocateSymbolExtras" );
88 oc->image += misalignment;
89
90 oc->symbol_extras = (SymbolExtra *) (oc->image + aligned);
91 }
92 }
93
94 if (oc->symbol_extras != NULL) {
95 memset( oc->symbol_extras, 0, sizeof (SymbolExtra) * count );
96 }
97
98 oc->first_symbol_extra = first;
99 oc->n_symbol_extras = count;
100
101 return 1;
102 }
103
104
105 #ifndef arm_HOST_ARCH
106 SymbolExtra* makeSymbolExtra( ObjectCode* oc,
107 unsigned long symbolNumber,
108 unsigned long target )
109 {
110 SymbolExtra *extra;
111
112 ASSERT( symbolNumber >= oc->first_symbol_extra
113 && symbolNumber - oc->first_symbol_extra < oc->n_symbol_extras);
114
115 extra = &oc->symbol_extras[symbolNumber - oc->first_symbol_extra];
116
117 #ifdef powerpc_HOST_ARCH
118 // lis r12, hi16(target)
119 extra->jumpIsland.lis_r12 = 0x3d80;
120 extra->jumpIsland.hi_addr = target >> 16;
121
122 // ori r12, r12, lo16(target)
123 extra->jumpIsland.ori_r12_r12 = 0x618c;
124 extra->jumpIsland.lo_addr = target & 0xffff;
125
126 // mtctr r12
127 extra->jumpIsland.mtctr_r12 = 0x7d8903a6;
128
129 // bctr
130 extra->jumpIsland.bctr = 0x4e800420;
131 #endif /* powerpc_HOST_ARCH */
132 #ifdef x86_64_HOST_ARCH
133 // jmp *-14(%rip)
134 static uint8_t jmp[] = { 0xFF, 0x25, 0xF2, 0xFF, 0xFF, 0xFF };
135 extra->addr = target;
136 memcpy(extra->jumpIsland, jmp, 6);
137 #endif /* x86_64_HOST_ARCH */
138
139 return extra;
140 }
141 #endif
142
143 #ifdef arm_HOST_ARCH
144 /*
145 Note [The ARM/Thumb Story]
146 ~~~~~~~~~~~~~~~~~~~~~~~~~~
147
148 Support for the ARM architecture is complicated by the fact that ARM has not
149 one but several instruction encodings. The two relevant ones here are the original
150 ARM encoding and Thumb, a more dense variant of ARM supporting only a subset
151 of the instruction set.
152
153 How the CPU decodes a particular instruction is determined by a mode bit. This
154 mode bit is set on jump instructions, the value being determined by the low
155 bit of the target address: An odd address means the target is a procedure
156 encoded in the Thumb encoding whereas an even address means it's a traditional
157 ARM procedure (the actual address jumped to is even regardless of the encoding bit).
158
159 Interoperation between Thumb- and ARM-encoded object code (known as "interworking")
160 is tricky. If the linker needs to link a call by an ARM object into Thumb code
161 (or vice-versa) it will produce a jump island using makeArmSymbolExtra. This,
162 however, is incompatible with GHC's tables-next-to-code since pointers
163 fixed-up in this way will point to a bit of generated code, not a info
164 table/Haskell closure like TNTC expects. For this reason, it is critical that
165 GHC emit exclusively ARM or Thumb objects for all Haskell code.
166
167 We still do, however, need to worry about calls to foreign code, hence the
168 need for makeArmSymbolExtra.
169 */
170
171 /* Produce a jump island for ARM/Thumb interworking */
172 SymbolExtra* makeArmSymbolExtra( ObjectCode* oc,
173 unsigned long symbolNumber,
174 unsigned long target,
175 int fromThumb,
176 int toThumb )
177 {
178 SymbolExtra *extra;
179
180 ASSERT( symbolNumber >= oc->first_symbol_extra
181 && symbolNumber - oc->first_symbol_extra < oc->n_symbol_extras);
182
183 extra = &oc->symbol_extras[symbolNumber - oc->first_symbol_extra];
184
185 // Make sure instruction mode bit is set properly
186 if (toThumb)
187 target |= 1;
188 else
189 target &= ~1;
190
191 if (!fromThumb) {
192 // In ARM encoding:
193 // movw r12, #0
194 // movt r12, #0
195 // bx r12
196 uint32_t code[] = { 0xe300c000, 0xe340c000, 0xe12fff1c };
197
198 // Patch lower half-word into movw
199 code[0] |= ((target>>12) & 0xf) << 16;
200 code[0] |= target & 0xfff;
201 // Patch upper half-word into movt
202 target >>= 16;
203 code[1] |= ((target>>12) & 0xf) << 16;
204 code[1] |= target & 0xfff;
205
206 memcpy(extra->jumpIsland, code, 12);
207
208 } else {
209 // In Thumb encoding:
210 // movw r12, #0
211 // movt r12, #0
212 // bx r12
213 uint16_t code[] = { 0xf240, 0x0c00,
214 0xf2c0, 0x0c00,
215 0x4760 };
216
217 // Patch lower half-word into movw
218 code[0] |= (target>>12) & 0xf;
219 code[0] |= ((target>>11) & 0x1) << 10;
220 code[1] |= ((target>>8) & 0x7) << 12;
221 code[1] |= target & 0xff;
222 // Patch upper half-word into movt
223 target >>= 16;
224 code[2] |= (target>>12) & 0xf;
225 code[2] |= ((target>>11) & 0x1) << 10;
226 code[3] |= ((target>>8) & 0x7) << 12;
227 code[3] |= target & 0xff;
228
229 memcpy(extra->jumpIsland, code, 10);
230 }
231
232 return extra;
233 }
234 #endif // arm_HOST_ARCH
235
236 #endif
237 #endif // NEED_SYMBOL_EXTRAS