1//===- MipsRelocationFactory.cpp  -----------------------------------------===//
2//
3//                     The MCLinker Project
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9
10#include <llvm/ADT/Twine.h>
11#include <llvm/Support/ELF.h>
12#include <mcld/LD/Layout.h>
13#include <mcld/Target/OutputRelocSection.h>
14#include <mcld/Support/MsgHandling.h>
15
16#include "MipsRelocationFactory.h"
17#include "MipsRelocationFunctions.h"
18
19using namespace mcld;
20
21//===----------------------------------------------------------------------===//
22// Relocation Functions and Tables
23//===----------------------------------------------------------------------===//
24DECL_MIPS_APPLY_RELOC_FUNCS
25
26/// the prototype of applying function
27typedef RelocationFactory::Result (*ApplyFunctionType)(Relocation&,
28                                                       const MCLDInfo& pLDInfo,
29                                                       MipsRelocationFactory&);
30
31// the table entry of applying functions
32struct ApplyFunctionTriple
33{
34  ApplyFunctionType func;
35  unsigned int type;
36  const char* name;
37};
38
39// declare the table of applying functions
40static const ApplyFunctionTriple ApplyFunctions[] = {
41  DECL_MIPS_APPLY_RELOC_FUNC_PTRS
42};
43
44//===----------------------------------------------------------------------===//
45// MipsRelocationFactory
46//===----------------------------------------------------------------------===//
47MipsRelocationFactory::MipsRelocationFactory(size_t pNum,
48                                             MipsGNULDBackend& pParent)
49  : RelocationFactory(pNum),
50    m_Target(pParent),
51    m_AHL(0)
52{
53}
54
55RelocationFactory::Result
56MipsRelocationFactory::applyRelocation(Relocation& pRelocation,
57                                       const MCLDInfo& pLDInfo)
58
59{
60  Relocation::Type type = pRelocation.type();
61
62  if (type >= sizeof(ApplyFunctions) / sizeof(ApplyFunctions[0])) {
63    fatal(diag::unknown_relocation) << (int)type
64                                    << pRelocation.symInfo()->name();
65    return Unknown;
66  }
67
68  // apply the relocation
69  return ApplyFunctions[type].func(pRelocation, pLDInfo, *this);
70}
71
72const char* MipsRelocationFactory::getName(Relocation::Type pType) const
73{
74  return ApplyFunctions[pType].name;
75}
76
77//===----------------------------------------------------------------------===//
78// Relocation helper function
79
80//===----------------------------------------------------------------------===//
81static const char * const GP_DISP_NAME = "_gp_disp";
82
83// Find next R_MIPS_LO16 relocation paired to pReloc.
84static
85Relocation* helper_FindLo16Reloc(Relocation& pReloc)
86{
87  Relocation* reloc = static_cast<Relocation*>(pReloc.getNextNode());
88  while (NULL != reloc)
89  {
90    if (llvm::ELF::R_MIPS_LO16 == reloc->type() &&
91        reloc->symInfo() == pReloc.symInfo())
92      return reloc;
93
94    reloc = static_cast<Relocation*>(reloc->getNextNode());
95  }
96  return NULL;
97}
98
99// Check the symbol is _gp_disp.
100static
101bool helper_isGpDisp(const Relocation& pReloc)
102{
103  const ResolveInfo* rsym = pReloc.symInfo();
104  return 0 == strcmp(GP_DISP_NAME, rsym->name());
105}
106
107static
108RelocationFactory::Address helper_GetGP(MipsRelocationFactory& pParent)
109{
110  return pParent.getTarget().getGOT().getSection().addr() + 0x7FF0;
111}
112
113static
114GOTEntry& helper_GetGOTEntry(Relocation& pReloc,
115                             MipsRelocationFactory& pParent,
116                             bool& pExist, int32_t value)
117{
118  // rsym - The relocation target symbol
119  ResolveInfo* rsym = pReloc.symInfo();
120  MipsGNULDBackend& ld_backend = pParent.getTarget();
121  MipsGOT& got = ld_backend.getGOT();
122
123  GOTEntry& got_entry = *got.getEntry(*rsym, pExist);
124
125  if (pExist)
126    return got_entry;
127
128  // If we first get this GOT entry, we should initialize it.
129  if (!(got.isLocal(rsym) && rsym->type() == ResolveInfo::Section)) {
130    if (rsym->reserved() & MipsGNULDBackend::ReserveGot) {
131      got_entry.setContent(pReloc.symValue());
132    }
133    else {
134      fatal(diag::reserve_entry_number_mismatch_got);
135    }
136  }
137
138  return got_entry;
139}
140
141static
142RelocationFactory::Address helper_GetGOTOffset(Relocation& pReloc,
143                                               MipsRelocationFactory& pParent)
144{
145  bool exist;
146  GOTEntry& got_entry = helper_GetGOTEntry(pReloc, pParent, exist, 0);
147  return pParent.getLayout().getOutputOffset(got_entry) - 0x7FF0;
148}
149
150static
151int32_t helper_CalcAHL(const Relocation& pHiReloc, const Relocation& pLoReloc)
152{
153  assert((pHiReloc.type() == llvm::ELF::R_MIPS_HI16 ||
154          pHiReloc.type() == llvm::ELF::R_MIPS_GOT16) &&
155         pLoReloc.type() == llvm::ELF::R_MIPS_LO16 &&
156         "Incorrect type of relocation for AHL calculation");
157
158  // Note the addend is section symbol offset here
159  assert (pHiReloc.addend() == pLoReloc.addend());
160
161  int32_t AHI = pHiReloc.target();
162  int32_t ALO = pLoReloc.target();
163  int32_t AHL = ((AHI & 0xFFFF) << 16) + (int16_t)(ALO & 0xFFFF) + pLoReloc.addend();
164  return AHL;
165}
166
167static
168void helper_DynRel(Relocation& pReloc,
169                   MipsRelocationFactory& pParent)
170{
171  ResolveInfo* rsym = pReloc.symInfo();
172  MipsGNULDBackend& ld_backend = pParent.getTarget();
173  MipsGOT& got = ld_backend.getGOT();
174
175  bool exist;
176  Relocation& rel_entry =
177    *ld_backend.getRelDyn().getEntry(*rsym, false, exist);
178
179  rel_entry.setType(llvm::ELF::R_MIPS_REL32);
180  rel_entry.targetRef() = pReloc.targetRef();
181
182  RelocationFactory::DWord A = pReloc.target() + pReloc.addend();
183  RelocationFactory::DWord S = pReloc.symValue();
184
185  if (got.isLocal(rsym)) {
186    rel_entry.setSymInfo(NULL);
187    pReloc.target() = A + S;
188  }
189  else {
190    rel_entry.setSymInfo(rsym);
191    // Don't add symbol value that will be resolved by the dynamic linker
192    pReloc.target() = A;
193  }
194}
195
196//=========================================//
197// Relocation functions implementation     //
198//=========================================//
199
200// R_MIPS_NONE and those unsupported/deprecated relocation type
201static
202MipsRelocationFactory::Result none(Relocation& pReloc,
203                                   const MCLDInfo& pLDInfo,
204                                   MipsRelocationFactory& pParent)
205{
206  return MipsRelocationFactory::OK;
207}
208
209// R_MIPS_32: S + A
210static
211MipsRelocationFactory::Result abs32(Relocation& pReloc,
212                                    const MCLDInfo& pLDInfo,
213                                    MipsRelocationFactory& pParent)
214{
215  ResolveInfo* rsym = pReloc.symInfo();
216
217  RelocationFactory::DWord A = pReloc.target() + pReloc.addend();
218  RelocationFactory::DWord S = pReloc.symValue();
219
220  const LDSection* target_sect = pParent.getLayout().getOutputLDSection(
221                                                  *(pReloc.targetRef().frag()));
222  assert(NULL != target_sect);
223  // If the flag of target section is not ALLOC, we will not scan this relocation
224  // but perform static relocation. (e.g., applying .debug section)
225  if (0x0 == (llvm::ELF::SHF_ALLOC & target_sect->flag())) {
226    pReloc.target() = S + A;
227    return MipsRelocationFactory::OK;
228  }
229
230  if (rsym->reserved() & MipsGNULDBackend::ReserveRel) {
231    helper_DynRel(pReloc, pParent);
232
233    return MipsRelocationFactory::OK;
234  }
235
236  pReloc.target() = (S + A);
237
238  return MipsRelocationFactory::OK;
239}
240
241// R_MIPS_HI16:
242//   local/external: ((AHL + S) - (short)(AHL + S)) >> 16
243//   _gp_disp      : ((AHL + GP - P) - (short)(AHL + GP - P)) >> 16
244static
245MipsRelocationFactory::Result hi16(Relocation& pReloc,
246                                   const MCLDInfo& pLDInfo,
247                                   MipsRelocationFactory& pParent)
248{
249  Relocation* lo_reloc = helper_FindLo16Reloc(pReloc);
250  assert(NULL != lo_reloc && "There is no paired R_MIPS_LO16 for R_MIPS_HI16");
251
252  int32_t AHL = helper_CalcAHL(pReloc, *lo_reloc);
253  int32_t res = 0;
254
255  pParent.setAHL(AHL);
256
257  if (helper_isGpDisp(pReloc)) {
258    int32_t P = pReloc.place(pParent.getLayout());
259    int32_t GP = helper_GetGP(pParent);
260    res = ((AHL + GP - P) - (int16_t)(AHL + GP - P)) >> 16;
261  }
262  else {
263    int32_t S = pReloc.symValue();
264    res = ((AHL + S) - (int16_t)(AHL + S)) >> 16;
265  }
266
267  pReloc.target() &= 0xFFFF0000;
268  pReloc.target() |= (res & 0xFFFF);
269
270  return MipsRelocationFactory::OK;
271}
272
273// R_MIPS_LO16:
274//   local/external: AHL + S
275//   _gp_disp      : AHL + GP - P + 4
276static
277MipsRelocationFactory::Result lo16(Relocation& pReloc,
278                                   const MCLDInfo& pLDInfo,
279                                   MipsRelocationFactory& pParent)
280{
281  int32_t res = 0;
282
283  if (helper_isGpDisp(pReloc)) {
284    int32_t P = pReloc.place(pParent.getLayout());
285    int32_t GP = helper_GetGP(pParent);
286    int32_t AHL = pParent.getAHL();
287    res = AHL + GP - P + 4;
288  }
289  else {
290    int32_t S = pReloc.symValue();
291    // The previous AHL may be for other hi/lo pairs.
292    // We need to calcuate the lo part now.  It is easy.
293    // Remember to add the section offset to ALO.
294    int32_t ALO = (pReloc.target() & 0xFFFF) + pReloc.addend();
295    res = ALO + S;
296  }
297
298  pReloc.target() &= 0xFFFF0000;
299  pReloc.target() |= (res & 0xFFFF);
300
301  return MipsRelocationFactory::OK;
302}
303
304// R_MIPS_GOT16:
305//   local   : G (calculate AHL and put high 16 bit to GOT)
306//   external: G
307static
308MipsRelocationFactory::Result got16(Relocation& pReloc,
309                                    const MCLDInfo& pLDInfo,
310                                    MipsRelocationFactory& pParent)
311{
312  ResolveInfo* rsym = pReloc.symInfo();
313  RelocationFactory::Address G = 0;
314
315  if (rsym->isLocal()) {
316    Relocation* lo_reloc = helper_FindLo16Reloc(pReloc);
317    assert(NULL != lo_reloc && "There is no paired R_MIPS_LO16 for R_MIPS_GOT16");
318
319    int32_t AHL = helper_CalcAHL(pReloc, *lo_reloc);
320    int32_t S = pReloc.symValue();
321
322    pParent.setAHL(AHL);
323
324    int32_t res = (AHL + S + 0x8000) & 0xFFFF0000;
325    bool exist;
326    GOTEntry& got_entry = helper_GetGOTEntry(pReloc, pParent, exist, res);
327
328    got_entry.setContent(res);
329    G = pParent.getLayout().getOutputOffset(got_entry) - 0x7FF0;
330  }
331  else {
332    G = helper_GetGOTOffset(pReloc, pParent);
333  }
334
335  pReloc.target() &= 0xFFFF0000;
336  pReloc.target() |= (G & 0xFFFF);
337
338  return MipsRelocationFactory::OK;
339}
340
341// R_MIPS_CALL16: G
342static
343MipsRelocationFactory::Result call16(Relocation& pReloc,
344                                     const MCLDInfo& pLDInfo,
345                                     MipsRelocationFactory& pParent)
346{
347  RelocationFactory::Address G = helper_GetGOTOffset(pReloc, pParent);
348
349  pReloc.target() &= 0xFFFF0000;
350  pReloc.target() |= (G & 0xFFFF);
351
352  return MipsRelocationFactory::OK;
353}
354
355// R_MIPS_GPREL32: A + S + GP0 - GP
356static
357MipsRelocationFactory::Result gprel32(Relocation& pReloc,
358                                      const MCLDInfo& pLDInfo,
359                                      MipsRelocationFactory& pParent)
360{
361  // Remember to add the section offset to A.
362  int32_t A = pReloc.target() + pReloc.addend();
363  int32_t S = pReloc.symValue();
364  int32_t GP = helper_GetGP(pParent);
365
366  // llvm does not emits SHT_MIPS_REGINFO section.
367  // Assume that GP0 is zero.
368  pReloc.target() = (A + S - GP) & 0xFFFFFFFF;
369
370  return MipsRelocationFactory::OK;
371}
372