1 #ifdef TARGET_DEFS_ONLY
2 
3 #define EM_TCC_TARGET EM_RISCV
4 
5 #define R_DATA_32  R_RISCV_32
6 #define R_DATA_PTR R_RISCV_64
7 #define R_JMP_SLOT R_RISCV_JUMP_SLOT
8 #define R_GLOB_DAT R_RISCV_64
9 #define R_COPY     R_RISCV_COPY
10 #define R_RELATIVE R_RISCV_RELATIVE
11 
12 #define R_NUM      R_RISCV_NUM
13 
14 #define ELF_START_ADDR 0x00010000
15 #define ELF_PAGE_SIZE 0x1000
16 
17 #define PCRELATIVE_DLLPLT 1
18 #define RELOCATE_DLLPLT 1
19 
20 #else /* !TARGET_DEFS_ONLY */
21 
22 //#define DEBUG_RELOC
23 #include "tcc.h"
24 
25 /* Returns 1 for a code relocation, 0 for a data relocation. For unknown
26    relocations, returns -1. */
code_reloc(int reloc_type)27 int code_reloc (int reloc_type)
28 {
29     switch (reloc_type) {
30 
31     case R_RISCV_BRANCH:
32     case R_RISCV_CALL:
33     case R_RISCV_JAL:
34         return 1;
35 
36     case R_RISCV_GOT_HI20:
37     case R_RISCV_PCREL_HI20:
38     case R_RISCV_PCREL_LO12_I:
39     case R_RISCV_PCREL_LO12_S:
40     case R_RISCV_32_PCREL:
41     case R_RISCV_SET6:
42     case R_RISCV_SUB6:
43     case R_RISCV_ADD16:
44     case R_RISCV_ADD32:
45     case R_RISCV_ADD64:
46     case R_RISCV_SUB16:
47     case R_RISCV_SUB32:
48     case R_RISCV_SUB64:
49     case R_RISCV_32:
50     case R_RISCV_64:
51         return 0;
52 
53     case R_RISCV_CALL_PLT:
54         return 1;
55     }
56     return -1;
57 }
58 
59 /* Returns an enumerator to describe whether and when the relocation needs a
60    GOT and/or PLT entry to be created. See tcc.h for a description of the
61    different values. */
gotplt_entry_type(int reloc_type)62 int gotplt_entry_type (int reloc_type)
63 {
64     switch (reloc_type) {
65     case R_RISCV_ALIGN:
66     case R_RISCV_RELAX:
67     case R_RISCV_RVC_BRANCH:
68     case R_RISCV_RVC_JUMP:
69     case R_RISCV_JUMP_SLOT:
70     case R_RISCV_SET6:
71     case R_RISCV_SUB6:
72     case R_RISCV_ADD16:
73     case R_RISCV_SUB16:
74         return NO_GOTPLT_ENTRY;
75 
76     case R_RISCV_BRANCH:
77     case R_RISCV_CALL:
78     case R_RISCV_PCREL_HI20:
79     case R_RISCV_PCREL_LO12_I:
80     case R_RISCV_PCREL_LO12_S:
81     case R_RISCV_32_PCREL:
82     case R_RISCV_ADD32:
83     case R_RISCV_ADD64:
84     case R_RISCV_SUB32:
85     case R_RISCV_SUB64:
86     case R_RISCV_32:
87     case R_RISCV_64:
88     case R_RISCV_JAL:
89     case R_RISCV_CALL_PLT:
90         return AUTO_GOTPLT_ENTRY;
91 
92     case R_RISCV_GOT_HI20:
93         return ALWAYS_GOTPLT_ENTRY;
94     }
95     return -1;
96 }
97 
create_plt_entry(TCCState *s1, unsigned got_offset, struct sym_attr *attr)98 ST_FUNC unsigned create_plt_entry(TCCState *s1, unsigned got_offset, struct sym_attr *attr)
99 {
100     Section *plt = s1->plt;
101     uint8_t *p;
102     unsigned plt_offset;
103 
104     if (s1->output_type == TCC_OUTPUT_DLL)
105         tcc_error("DLLs unimplemented!");
106 
107     if (plt->data_offset == 0)
108         section_ptr_add(plt, 32);
109     plt_offset = plt->data_offset;
110 
111     p = section_ptr_add(plt, 16);
112     write64le(p, got_offset);
113     return plt_offset;
114 }
115 
116 /* relocate the PLT: compute addresses and offsets in the PLT now that final
117    address for PLT and GOT are known (see fill_program_header) */
relocate_plt(TCCState *s1)118 ST_FUNC void relocate_plt(TCCState *s1)
119 {
120     uint8_t *p, *p_end;
121 
122     if (!s1->plt)
123       return;
124 
125     p = s1->plt->data;
126     p_end = p + s1->plt->data_offset;
127 
128     if (p < p_end) {
129         uint64_t plt = s1->plt->sh_addr;
130         uint64_t got = s1->got->sh_addr;
131         uint64_t off = (got - plt + 0x800) >> 12;
132         if ((off + ((uint32_t)1 << 20)) >> 21)
133             tcc_error("Failed relocating PLT (off=0x%lx, got=0x%lx, plt=0x%lx)", off, got, plt);
134         write32le(p, 0x397 | (off << 12)); // auipc t2, %pcrel_hi(got)
135         write32le(p + 4, 0x41c30333); // sub t1, t1, t3
136         write32le(p + 8, 0x0003be03   // ld t3, %pcrel_lo(got)(t2)
137                          | (((got - plt) & 0xfff) << 20));
138         write32le(p + 12, 0xfd430313); // addi t1, t1, -(32+12)
139         write32le(p + 16, 0x00038293   // addi t0, t2, %pcrel_lo(got)
140                           | (((got - plt) & 0xfff) << 20));
141         write32le(p + 20, 0x00135313); // srli t1, t1, log2(16/PTRSIZE)
142         write32le(p + 24, 0x0082b283); // ld t0, PTRSIZE(t0)
143         write32le(p + 28, 0x000e0067); // jr t3
144         p += 32;
145         while (p < p_end) {
146             uint64_t pc = plt + (p - s1->plt->data);
147             uint64_t addr = got + read64le(p);
148             uint64_t off = (addr - pc + 0x800) >> 12;
149             if ((off + ((uint32_t)1 << 20)) >> 21)
150                 tcc_error("Failed relocating PLT (off=0x%lx, addr=0x%lx, pc=0x%lx)", off, addr, pc);
151             write32le(p, 0xe17 | (off << 12)); // auipc t3, %pcrel_hi(func@got)
152             write32le(p + 4, 0x000e3e03 // ld t3, %pcrel_lo(func@got)(t3)
153                              | (((addr - pc) & 0xfff) << 20));
154             write32le(p + 8, 0x000e0367); // jalr t1, t3
155             write32le(p + 12, 0x00000013); // nop
156             p += 16;
157         }
158     }
159 }
160 
161 struct pcrel_hi {
162     addr_t addr, val;
163 };
164 static struct pcrel_hi last_hi;
165 
relocate(TCCState *s1, ElfW_Rel *rel, int type, unsigned char *ptr, addr_t addr, addr_t val)166 void relocate(TCCState *s1, ElfW_Rel *rel, int type, unsigned char *ptr,
167               addr_t addr, addr_t val)
168 {
169     uint64_t off64;
170     uint32_t off32;
171     int sym_index = ELFW(R_SYM)(rel->r_info);
172     ElfW(Sym) *sym = &((ElfW(Sym) *)symtab_section->data)[sym_index];
173 
174     switch(type) {
175     case R_RISCV_ALIGN:
176     case R_RISCV_RELAX:
177         return;
178 
179     case R_RISCV_BRANCH:
180         off64 = val - addr;
181         if ((off64 + (1 << 12)) & ~(uint64_t)0x1ffe)
182           tcc_error("R_RISCV_BRANCH relocation failed"
183                     " (val=%lx, addr=%lx)", val, addr);
184         off32 = off64 >> 1;
185         write32le(ptr, (read32le(ptr) & ~0xfe000f80)
186                        | ((off32 & 0x800) << 20)
187                        | ((off32 & 0x3f0) << 21)
188                        | ((off32 & 0x00f) << 8)
189                        | ((off32 & 0x400) >> 3));
190         return;
191     case R_RISCV_JAL:
192         off64 = val - addr;
193         if ((off64 + (1 << 21)) & ~(((uint64_t)1 << 22) - 2))
194           tcc_error("R_RISCV_JAL relocation failed"
195                     " (val=%lx, addr=%lx)", val, addr);
196         off32 = off64;
197         write32le(ptr, (read32le(ptr) & 0xfff)
198                        | (((off32 >> 12) &  0xff) << 12)
199                        | (((off32 >> 11) &     1) << 20)
200                        | (((off32 >>  1) & 0x3ff) << 21)
201                        | (((off32 >> 20) &     1) << 31));
202         return;
203     case R_RISCV_CALL:
204     case R_RISCV_CALL_PLT:
205         write32le(ptr, (read32le(ptr) & 0xfff)
206                        | ((val - addr + 0x800) & ~0xfff));
207         write32le(ptr + 4, (read32le(ptr + 4) & 0xfffff)
208                            | (((val - addr) & 0xfff) << 20));
209         return;
210     case R_RISCV_PCREL_HI20:
211 #ifdef DEBUG_RELOC
212         printf("PCREL_HI20: val=%lx addr=%lx\n", val, addr);
213 #endif
214         off64 = (int64_t)(val - addr + 0x800) >> 12;
215         if ((off64 + ((uint64_t)1 << 20)) >> 21)
216           tcc_error("R_RISCV_PCREL_HI20 relocation failed: off=%lx cond=%lx sym=%s",
217                     off64, ((int64_t)(off64 + ((uint64_t)1 << 20)) >> 21),
218                     symtab_section->link->data + sym->st_name);
219         write32le(ptr, (read32le(ptr) & 0xfff)
220                        | ((off64 & 0xfffff) << 12));
221         last_hi.addr = addr;
222         last_hi.val = val;
223         return;
224     case R_RISCV_GOT_HI20:
225         val = s1->got->sh_addr + get_sym_attr(s1, sym_index, 0)->got_offset;
226         off64 = (int64_t)(val - addr + 0x800) >> 12;
227         if ((off64 + ((uint64_t)1 << 20)) >> 21)
228           tcc_error("R_RISCV_GOT_HI20 relocation failed");
229         last_hi.addr = addr;
230         last_hi.val = val;
231         write32le(ptr, (read32le(ptr) & 0xfff)
232                        | ((off64 & 0xfffff) << 12));
233         return;
234     case R_RISCV_PCREL_LO12_I:
235 #ifdef DEBUG_RELOC
236         printf("PCREL_LO12_I: val=%lx addr=%lx\n", val, addr);
237 #endif
238         if (val != last_hi.addr)
239           tcc_error("unsupported hi/lo pcrel reloc scheme");
240         val = last_hi.val;
241         addr = last_hi.addr;
242         write32le(ptr, (read32le(ptr) & 0xfffff)
243                        | (((val - addr) & 0xfff) << 20));
244         return;
245     case R_RISCV_PCREL_LO12_S:
246         if (val != last_hi.addr)
247           tcc_error("unsupported hi/lo pcrel reloc scheme");
248         val = last_hi.val;
249         addr = last_hi.addr;
250         off32 = val - addr;
251         write32le(ptr, (read32le(ptr) & ~0xfe000f80)
252                        | ((off32 & 0xfe0) << 20)
253                        | ((off32 & 0x01f) << 7));
254         return;
255 
256     case R_RISCV_RVC_BRANCH:
257         off64 = (val - addr);
258         if ((off64 + (1 << 8)) & ~(uint64_t)0x1fe)
259           tcc_error("R_RISCV_RVC_BRANCH relocation failed"
260                     " (val=%lx, addr=%lx)", val, addr);
261         off32 = off64;
262         write16le(ptr, (read16le(ptr) & 0xe383)
263                        | (((off32 >> 5) & 1) << 2)
264                        | (((off32 >> 1) & 3) << 3)
265                        | (((off32 >> 6) & 3) << 5)
266                        | (((off32 >> 3) & 3) << 10)
267                        | (((off32 >> 8) & 1) << 12));
268         return;
269     case R_RISCV_RVC_JUMP:
270         off64 = (val - addr);
271         if ((off64 + (1 << 11)) & ~(uint64_t)0xffe)
272           tcc_error("R_RISCV_RVC_BRANCH relocation failed"
273                     " (val=%lx, addr=%lx)", val, addr);
274         off32 = off64;
275         write16le(ptr, (read16le(ptr) & 0xe003)
276                        | (((off32 >>  5) & 1) << 2)
277                        | (((off32 >>  1) & 7) << 3)
278                        | (((off32 >>  7) & 1) << 6)
279                        | (((off32 >>  6) & 1) << 7)
280                        | (((off32 >> 10) & 1) << 8)
281                        | (((off32 >>  8) & 3) << 9)
282                        | (((off32 >>  4) & 1) << 11)
283                        | (((off32 >> 11) & 1) << 12));
284         return;
285 
286     case R_RISCV_32:
287         write32le(ptr, val);
288         return;
289     case R_RISCV_JUMP_SLOT:
290     case R_RISCV_64:
291         write64le(ptr, val);
292         return;
293     case R_RISCV_ADD64:
294         write64le(ptr, read64le(ptr) + val);
295         return;
296     case R_RISCV_ADD32:
297         write32le(ptr, read32le(ptr) + val);
298         return;
299     case R_RISCV_SUB64:
300         write64le(ptr, read64le(ptr) - val);
301         return;
302     case R_RISCV_SUB32:
303         write32le(ptr, read32le(ptr) - val);
304         return;
305     case R_RISCV_ADD16:
306         write16le(ptr, read16le(ptr) + val);
307         return;
308     case R_RISCV_SUB16:
309         write16le(ptr, read16le(ptr) - val);
310         return;
311     case R_RISCV_SET6:
312         *ptr = (*ptr & ~0x3f) | (val & 0x3f);
313         return;
314     case R_RISCV_SUB6:
315         *ptr = (*ptr & ~0x3f) | ((*ptr - val) & 0x3f);
316         return;
317 
318     case R_RISCV_32_PCREL:
319     case R_RISCV_COPY:
320         /* XXX */
321         return;
322 
323     default:
324         fprintf(stderr, "FIXME: handle reloc type %x at %x [%p] to %x\n",
325                 type, (unsigned)addr, ptr, (unsigned)val);
326         return;
327     }
328 }
329 #endif
330