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