llvm.org GIT mirror llvm / 4edb309
[RuntimeDyldELF] Improve GOT support Summary: This is the first in a series of patches to eventually add support for TLS relocations to RuntimeDyld. This patch resolves an issue in the current GOT handling, where GOT entries would be reused between object files, which leads to the same situation that necessitates the GOT in the first place, i.e. that the 32-bit offset can not cover all of the address space. Thus this patch makes the GOT object-file-local. Unfortunately, this still isn't quite enough, because the MemoryManager does not yet guarantee that sections are allocated sufficiently close to each other, even if they belong to the same object file. To address this concern, this patch also adds a small API abstraction on top of the GOT allocation mechanism that will allow (temporarily, until the MemoryManager is improved) using the stub mechanism instead of allocating a different section. The actual switch from separate section to stub mechanism will be part of a follow-on commit, so that it can be easily reverted independently at the appropriate time. Test Plan: Includes a test case where the GOT of two object files is artificially forced to be apart by several GB. Reviewers: lhames Reviewed By: lhames Subscribers: llvm-commits Differential Revision: http://reviews.llvm.org/D8813 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@234839 91177308-0d34-0410-b5e6-96231b3b80d8 Keno Fischer 5 years ago
8 changed file(s) with 125 addition(s) and 116 deletion(s). Raw diff Collapse all Expand all
813813 report_fatal_error("Program used external function '" + Name +
814814 "' which could not be resolved!");
815815
816 updateGOTEntries(Name, Addr);
817816 DEBUG(dbgs() << "Resolving relocations Name: " << Name << "\t"
818817 << format("0x%lx", Addr) << "\n");
819818 // This list may have been updated when we called getSymbolAddress, so
1818 friend class RuntimeDyldChecker;
1919 friend class RuntimeDyldImpl;
2020 friend class RuntimeDyldCheckerExprEval;
21 friend class RuntimeDyldELF;
2122
2223 public:
2324 RuntimeDyldCheckerImpl(RuntimeDyld &RTDyld, MCDisassembler *Disassembler,
1111 //===----------------------------------------------------------------------===//
1212
1313 #include "RuntimeDyldELF.h"
14 #include "RuntimeDyldCheckerImpl.h"
1415 #include "llvm/ADT/IntervalMap.h"
1516 #include "llvm/ADT/STLExtras.h"
1617 #include "llvm/ADT/StringRef.h"
184185
185186 RuntimeDyldELF::RuntimeDyldELF(RuntimeDyld::MemoryManager &MemMgr,
186187 RuntimeDyld::SymbolResolver &Resolver)
187 : RuntimeDyldImpl(MemMgr, Resolver) {}
188 : RuntimeDyldImpl(MemMgr, Resolver), GOTSectionID(0), CurrentGOTIndex(0) {}
188189 RuntimeDyldELF::~RuntimeDyldELF() {}
189190
190191 void RuntimeDyldELF::registerEHFrames() {
244245 << format("%p\n", Section.Address + Offset));
245246 break;
246247 }
247 case ELF::R_X86_64_GOTPCREL: {
248 // findGOTEntry returns the 'G + GOT' part of the relocation calculation
249 // based on the load/target address of the GOT (not the current/local addr).
250 uint64_t GOTAddr = findGOTEntry(Value, SymOffset);
251 uint64_t FinalAddress = Section.LoadAddress + Offset;
252 // The processRelocationRef method combines the symbol offset and the addend
253 // and in most cases that's what we want. For this relocation type, we need
254 // the raw addend, so we subtract the symbol offset to get it.
255 int64_t RealOffset = GOTAddr + Addend - SymOffset - FinalAddress;
256 assert(RealOffset <= INT32_MAX && RealOffset >= INT32_MIN);
257 int32_t TruncOffset = (RealOffset & 0xFFFFFFFF);
258 support::ulittle32_t::ref(Section.Address + Offset) = TruncOffset;
259 break;
260 }
261248 case ELF::R_X86_64_PC32: {
262249 // Get the placeholder value from the generated object since
263250 // a previous relocation attempt may have overwritten the loaded version
264251 support::ulittle32_t::ref Placeholder(
265252 (void *)(Section.ObjAddress + Offset));
266253 uint64_t FinalAddress = Section.LoadAddress + Offset;
267 int64_t RealOffset = Placeholder + Value + Addend - FinalAddress;
254 int64_t RealOffset = Value + Addend - FinalAddress;
255 // Don't add the placeholder if this is a stub
256 if (Offset < Section.Size)
257 RealOffset += Placeholder;
268258 assert(RealOffset <= INT32_MAX && RealOffset >= INT32_MIN);
269259 int32_t TruncOffset = (RealOffset & 0xFFFFFFFF);
270260 support::ulittle32_t::ref(Section.Address + Offset) = TruncOffset;
276266 support::ulittle64_t::ref Placeholder(
277267 (void *)(Section.ObjAddress + Offset));
278268 uint64_t FinalAddress = Section.LoadAddress + Offset;
279 support::ulittle64_t::ref(Section.Address + Offset) =
280 Placeholder + Value + Addend - FinalAddress;
269 int64_t RealOffset = Value + Addend - FinalAddress;
270 if (Offset < Section.Size)
271 RealOffset += Placeholder;
272 support::ulittle64_t::ref(Section.Address + Offset) = RealOffset;
281273 break;
282274 }
283275 }
13221314 Stubs[Value] = StubOffset;
13231315 createStubFunction((uint8_t *)StubAddress);
13241316
1325 // Create a GOT entry for the external function.
1326 GOTEntries.push_back(Value);
1327
1328 // Make our stub function a relative call to the GOT entry.
1329 RelocationEntry RE(SectionID, StubOffset + 2, ELF::R_X86_64_GOTPCREL,
1330 -4);
1331 addRelocationForSymbol(RE, Value.SymbolName);
1332
13331317 // Bump our stub offset counter
13341318 Section.StubOffset = StubOffset + getMaxStubSize();
1319
1320 // Allocate a GOT Entry
1321 uint64_t GOTOffset = allocateGOTEntries(SectionID, 1);
1322
1323 // The load of the GOT address has an addend of -4
1324 resolveGOTOffsetRelocation(SectionID, StubOffset + 2, GOTOffset - 4);
1325
1326 // Fill in the value of the symbol we're targeting into the GOT
1327 addRelocationForSymbol(computeGOTOffsetRE(SectionID,GOTOffset,0,ELF::R_X86_64_64),
1328 Value.SymbolName);
13351329 }
13361330
13371331 // Make the target call a call into the stub table.
13421336 Value.Offset);
13431337 addRelocationForSection(RE, Value.SectionID);
13441338 }
1339 } else if (Arch == Triple::x86_64 && RelType == ELF::R_X86_64_GOTPCREL) {
1340 uint64_t GOTOffset = allocateGOTEntries(SectionID, 1);
1341 resolveGOTOffsetRelocation(SectionID, Offset, GOTOffset + Addend);
1342
1343 // Fill in the value of the symbol we're targeting into the GOT
1344 RelocationEntry RE = computeGOTOffsetRE(SectionID, GOTOffset, Value.Offset, ELF::R_X86_64_64);
1345 if (Value.SymbolName)
1346 addRelocationForSymbol(RE, Value.SymbolName);
1347 else
1348 addRelocationForSection(RE, Value.SectionID);
13451349 } else {
1346 if (Arch == Triple::x86_64 && RelType == ELF::R_X86_64_GOTPCREL) {
1347 GOTEntries.push_back(Value);
1348 }
13491350 RelocationEntry RE(SectionID, Offset, RelType, Value.Addend, Value.Offset);
13501351 if (Value.SymbolName)
13511352 addRelocationForSymbol(RE, Value.SymbolName);
13531354 addRelocationForSection(RE, Value.SectionID);
13541355 }
13551356 return ++RelI;
1356 }
1357
1358 void RuntimeDyldELF::updateGOTEntries(StringRef Name, uint64_t Addr) {
1359
1360 SmallVectorImpl>::iterator it;
1361 SmallVectorImpl>::iterator end = GOTs.end();
1362
1363 for (it = GOTs.begin(); it != end; ++it) {
1364 GOTRelocations &GOTEntries = it->second;
1365 for (int i = 0, e = GOTEntries.size(); i != e; ++i) {
1366 if (GOTEntries[i].SymbolName != nullptr &&
1367 GOTEntries[i].SymbolName == Name) {
1368 GOTEntries[i].Offset = Addr;
1369 }
1370 }
1371 }
13721357 }
13731358
13741359 size_t RuntimeDyldELF::getGOTEntrySize() {
13971382 return Result;
13981383 }
13991384
1400 uint64_t RuntimeDyldELF::findGOTEntry(uint64_t LoadAddress, uint64_t Offset) {
1401
1402 const size_t GOTEntrySize = getGOTEntrySize();
1403
1404 SmallVectorImpl>::const_iterator it;
1405 SmallVectorImpl>::const_iterator end =
1406 GOTs.end();
1407
1408 int GOTIndex = -1;
1409 for (it = GOTs.begin(); it != end; ++it) {
1410 SID GOTSectionID = it->first;
1411 const GOTRelocations &GOTEntries = it->second;
1412
1413 // Find the matching entry in our vector.
1414 uint64_t SymbolOffset = 0;
1415 for (int i = 0, e = GOTEntries.size(); i != e; ++i) {
1416 if (!GOTEntries[i].SymbolName) {
1417 if (getSectionLoadAddress(GOTEntries[i].SectionID) == LoadAddress &&
1418 GOTEntries[i].Offset == Offset) {
1419 GOTIndex = i;
1420 SymbolOffset = GOTEntries[i].Offset;
1421 break;
1422 }
1423 } else {
1424 // GOT entries for external symbols use the addend as the address when
1425 // the external symbol has been resolved.
1426 if (GOTEntries[i].Offset == LoadAddress) {
1427 GOTIndex = i;
1428 // Don't use the Addend here. The relocation handler will use it.
1429 break;
1430 }
1431 }
1432 }
1433
1434 if (GOTIndex != -1) {
1435 if (GOTEntrySize == sizeof(uint64_t)) {
1436 uint64_t *LocalGOTAddr = (uint64_t *)getSectionAddress(GOTSectionID);
1437 // Fill in this entry with the address of the symbol being referenced.
1438 LocalGOTAddr[GOTIndex] = LoadAddress + SymbolOffset;
1439 } else {
1440 uint32_t *LocalGOTAddr = (uint32_t *)getSectionAddress(GOTSectionID);
1441 // Fill in this entry with the address of the symbol being referenced.
1442 LocalGOTAddr[GOTIndex] = (uint32_t)(LoadAddress + SymbolOffset);
1443 }
1444
1445 // Calculate the load address of this entry
1446 return getSectionLoadAddress(GOTSectionID) + (GOTIndex * GOTEntrySize);
1447 }
1448 }
1449
1450 assert(GOTIndex != -1 && "Unable to find requested GOT entry.");
1451 return 0;
1385 uint64_t RuntimeDyldELF::allocateGOTEntries(unsigned SectionID, unsigned no)
1386 {
1387 (void)SectionID; // The GOT Section is the same for all section in the object file
1388 if (GOTSectionID == 0) {
1389 GOTSectionID = Sections.size();
1390 // Reserve a section id. We'll allocate the section later
1391 // once we know the total size
1392 Sections.push_back(SectionEntry(".got", 0, 0, 0));
1393 }
1394 uint64_t StartOffset = CurrentGOTIndex * getGOTEntrySize();
1395 CurrentGOTIndex += no;
1396 return StartOffset;
1397 }
1398
1399 void RuntimeDyldELF::resolveGOTOffsetRelocation(unsigned SectionID, uint64_t Offset, uint64_t GOTOffset)
1400 {
1401 // Fill in the relative address of the GOT Entry into the stub
1402 RelocationEntry GOTRE(SectionID, Offset, ELF::R_X86_64_PC32, GOTOffset);
1403 addRelocationForSection(GOTRE, GOTSectionID);
1404 }
1405
1406 RelocationEntry RuntimeDyldELF::computeGOTOffsetRE(unsigned SectionID, uint64_t GOTOffset, uint64_t SymbolOffset,
1407 uint32_t Type)
1408 {
1409 (void)SectionID; // The GOT Section is the same for all section in the object file
1410 return RelocationEntry(GOTSectionID, GOTOffset, Type, SymbolOffset);
14521411 }
14531412
14541413 void RuntimeDyldELF::finalizeLoad(const ObjectFile &Obj,
14551414 ObjSectionToIDMap &SectionMap) {
14561415 // If necessary, allocate the global offset table
1457 size_t numGOTEntries = GOTEntries.size();
1458 if (numGOTEntries != 0) {
1416 if (GOTSectionID != 0) {
14591417 // Allocate memory for the section
1460 unsigned SectionID = Sections.size();
1461 size_t TotalSize = numGOTEntries * getGOTEntrySize();
1418 size_t TotalSize = CurrentGOTIndex * getGOTEntrySize();
14621419 uint8_t *Addr = MemMgr.allocateDataSection(TotalSize, getGOTEntrySize(),
1463 SectionID, ".got", false);
1420 GOTSectionID, ".got", false);
14641421 if (!Addr)
14651422 report_fatal_error("Unable to allocate memory for GOT!");
14661423
1467 GOTs.push_back(std::make_pair(SectionID, GOTEntries));
1468 Sections.push_back(SectionEntry(".got", Addr, TotalSize, 0));
1424 Sections[GOTSectionID] = SectionEntry(".got", Addr, TotalSize, 0);
1425
1426 if (Checker)
1427 Checker->registerSection(Obj.getFileName(), GOTSectionID);
1428
14691429 // For now, initialize all GOT entries to zero. We'll fill them in as
14701430 // needed when GOT-based relocations are applied.
14711431 memset(Addr, 0, TotalSize);
14821442 break;
14831443 }
14841444 }
1445
1446 GOTSectionID = 0;
1447 CurrentGOTIndex = 0;
14851448 }
14861449
14871450 bool RuntimeDyldELF::isCompatibleFile(const object::ObjectFile &Obj) const {
7979 ObjSectionToIDMap &LocalSections,
8080 RelocationValueRef &Rel);
8181
82 uint64_t findGOTEntry(uint64_t LoadAddr, uint64_t Offset);
8382 size_t getGOTEntrySize();
8483
85 void updateGOTEntries(StringRef Name, uint64_t Addr) override;
84 SectionEntry &getSection(unsigned SectionID) { return Sections[SectionID]; }
8685
87 // Relocation entries for symbols whose position-independent offset is
88 // updated in a global offset table.
89 typedef SmallVector GOTRelocations;
90 GOTRelocations GOTEntries; // List of entries requiring finalization.
91 SmallVector, 8> GOTs; // Allocated tables.
86 // Allocate no GOT entries for use in the given section.
87 uint64_t allocateGOTEntries(unsigned SectionID, unsigned no);
88
89 // Resolve the relvative address of GOTOffset in Section ID and place
90 // it at the given Offset
91 void resolveGOTOffsetRelocation(unsigned SectionID, uint64_t Offset,
92 uint64_t GOTOffset);
93
94 // For a GOT entry referenced from SectionID, compute a relocation entry
95 // that will place the final resolved value in the GOT slot
96 RelocationEntry computeGOTOffsetRE(unsigned SectionID,
97 uint64_t GOTOffset,
98 uint64_t SymbolOffset,
99 unsigned Type);
100
101 // The tentative ID for the GOT section
102 unsigned GOTSectionID;
103
104 // Records the current number of allocated slots in the GOT
105 // (This would be equivalent to GOTEntries.size() were it not for relocations
106 // that consume more than one slot)
107 unsigned CurrentGOTIndex;
92108
93109 // When a module is loaded we save the SectionID of the EH frame section
94110 // in a table until we receive a request to register all unregistered
360360 /// \brief Resolve relocations to external symbols.
361361 void resolveExternalSymbols();
362362
363 /// \brief Update GOT entries for external symbols.
364 // The base class does nothing. ELF overrides this.
365 virtual void updateGOTEntries(StringRef Name, uint64_t Addr) {}
366
367363 // \brief Compute an upper bound of the memory that is required to load all
368364 // sections
369365 void computeTotalAllocSize(const ObjectFile &Obj, uint64_t &CodeSize,
0 ; RUN: %lli -extra-module=%p/Inputs/cross-module-b.ll -relocation-model=pic -code-model=small %s > /dev/null
1 ; RUN: %lli -mtriple=x86_64-pc-linux -extra-module=%p/Inputs/cross-module-b.ll -relocation-model=pic -code-model=small %s > /dev/null
12 ; XFAIL: mips, i686, i386
23
34 declare i32 @FB()
0 # RUN: llvm-mc -triple=x86_64-pc-linux -relocation-model=pic -filetype=obj -o %T/test_ELF1_x86-64.o %s
1 # RUN: llvm-mc -triple=x86_64-pc-linux -relocation-model=pic -filetype=obj -o %T/test_ELF2_x86-64.o %s
2 # RUN: llc -mtriple=x86_64-pc-linux -relocation-model=pic -filetype=obj -o %T/test_ELF_ExternalGlobal_x86-64.o %S/Inputs/ExternalGlobal.ll
3 # RUN: llvm-rtdyld -triple=x86_64-pc-linux -verify %T/test_ELF1_x86-64.o %T/test_ELF_ExternalGlobal_x86-64.o
4 # Test that we can load this code twice at memory locations more than 2GB apart
5 # RUN: llvm-rtdyld -triple=x86_64-pc-linux -verify -map-section test_ELF1_x86-64.o,.got=0x10000 -map-section test_ELF2_x86-64.o,.text=0x100000000 -map-section test_ELF2_x86-64.o,.got=0x100010000 %T/test_ELF1_x86-64.o %T/test_ELF2_x86-64.o %T/test_ELF_ExternalGlobal_x86-64.o
6
7 # Assembly obtained by compiling the following and adding checks:
8 # @G = external global i8*
9 #
10 # define i8* @foo() {
11 # %ret = load i8** @G
12 # ret i32 %ret
13 # }
14 #
15
16 #
17 .text
18 .file "ELF_x64-64_PIC_relocations.ll"
19 .align 16, 0x90
20 .type foo,@function
21 foo: # @foo
22 # BB#0:
23 movq G@GOTPCREL(%rip), %rax
24 movl (%rax), %eax
25 retq
26 .Ltmp0:
27 .size foo, .Ltmp0-foo
28
29
30 .section ".note.GNU-stack","",@progbits