2 * See the dyninst/COPYRIGHT file for copyright information.
4 * We provide the Paradyn Tools (below described as "Paradyn")
5 * on an AS IS basis, and do not warrant its validity or performance.
6 * We reserve the right to update, modify, or discontinue this
7 * software at any time. We shall have no obligation to supply such
8 * updates or modifications or any other form of support to you.
10 * By your use of Paradyn, you understand and agree that we (or any
11 * other person or entity with proprietary rights in Paradyn) are
12 * under no obligation to provide either maintenance services,
13 * update services, notices of latent defects, or correction of
14 * defects for Paradyn.
16 * This library is free software; you can redistribute it and/or
17 * modify it under the terms of the GNU Lesser General Public
18 * License as published by the Free Software Foundation; either
19 * version 2.1 of the License, or (at your option) any later version.
21 * This library is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
24 * Lesser General Public License for more details.
26 * You should have received a copy of the GNU Lesser General Public
27 * License along with this library; if not, write to the Free Software
28 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
32 * holds architecture specific functions for x86 and x86_64 architecture needed for the
33 * static executable rewriter
44 #include "emitElfStatic.h"
52 using namespace Dyninst;
53 using namespace Dyninst::SymtabAPI;
55 static const Offset GOT_RESERVED_SLOTS = 3;
56 static const unsigned X86_WIDTH = 4;
57 static const unsigned X86_64_WIDTH = 8;
59 #if defined(os_freebsd)
60 #define R_X86_64_JUMP_SLOT R_X86_64_JMP_SLOT
63 // Used in an assert so needs to be a macro
64 #define UNKNOWN_ADDRESS_WIDTH_ASSERT "An unknown address width was encountered, can't continue"
67 * As most of these functions are defined per architecture, the description of
68 * each of these functions is in the emitElfStatic header. Comments describing
69 * the function interface are explicitly left out.
75 * Given a relocation, determines if the relocation corresponds to a .ctors or .dtors
76 * table that requires special consideration. Modifies the passed symbol offset to
77 * point to the right table, if applicable.
79 * rel The relocation entry to examine
80 * globalOffset The offset of the linked code (used for symbol offset calculation)
81 * lmap Holds information about .ctors/.dtors tables
83 * symbolOffset Modified by this routine to contain the offset of the table
85 * Returns true, if there are no errors including the case where the relocation
86 * entry doesn't reference the .ctors/.dtors tables.
88 static bool computeCtorDtorAddress(relocationEntry &rel, Offset globalOffset,
89 LinkMap &lmap, string &errMsg, Offset &symbolOffset)
91 if( rel.name() == SYMTAB_CTOR_LIST_REL ) {
92 // This needs to be: (the location of the .ctors table)
93 if( lmap.newCtorRegions.size() > 0 ) {
94 symbolOffset = lmap.ctorRegionOffset + globalOffset;
95 }else if( lmap.originalCtorRegion != NULL ) {
96 symbolOffset = lmap.originalCtorRegion->getDiskOffset();
98 errMsg = "Failed to locate original .ctors Region -- cannot apply relocation";
101 }else if( rel.name() == SYMTAB_DTOR_LIST_REL ) {
102 // This needs to be: (the location of the .dtors table)
103 if( lmap.newDtorRegions.size() > 0 ) {
104 symbolOffset = lmap.dtorRegionOffset + globalOffset;
105 }else if( lmap.originalDtorRegion != NULL ) {
106 symbolOffset = lmap.originalDtorRegion->getDiskOffset();
108 errMsg = "Failed to locate original .dtors Region -- cannot apply relocation";
116 bool emitElfStatic::archSpecificRelocation(Symtab *, Symtab *, char *targetData, relocationEntry &rel,
117 Offset dest, Offset relOffset, Offset globalOffset, LinkMap &lmap,
120 if( X86_WIDTH == addressWidth_ ) {
122 * Referring to the SYSV 386 supplement:
124 * All relocations on x86 are one word32 == Elf32_Word
131 Offset symbolOffset = rel.getDynSym()->getOffset();
134 if( rel.regionType() == Region::RT_REL ) {
135 memcpy(&addend, &targetData[dest], sizeof(Elf32_Word));
136 }else if( rel.regionType() == Region::RT_RELA ) {
137 addend = rel.addend();
140 if( !computeCtorDtorAddress(rel, globalOffset, lmap, errMsg, symbolOffset) ) {
144 rewrite_printf("relocation for '%s': TYPE = %s(%lu) S = %lx A = %lx P = %lx\n",
146 relocationEntry::relType2Str(rel.getRelType(), addressWidth_),
147 rel.getRelType(), symbolOffset, addend, relOffset);
149 Offset relocation = 0;
150 map<Symbol *, Offset>::iterator result;
153 switch(rel.getRelType()) {
155 relocation = symbolOffset + addend;
157 case R_386_PLT32: // this works because the address of the symbol is known at link time
159 relocation = symbolOffset + addend - relOffset;
161 case R_386_TLS_LE: // The necessary value is set during storage allocation
164 relocation = symbolOffset;
166 case R_386_TLS_GD: // The address is computed when the GOT is built
168 case R_386_TLS_GOTIE:
169 result = lmap.gotSymbols.find(rel.getDynSym());
170 if( result == lmap.gotSymbols.end() ) {
171 errMsg = "Expected GOT symbol does not exist in GOT symbol mapping";
175 relocation = result->second;
178 relocation = symbolOffset + addend - (lmap.gotRegionOffset + globalOffset);
181 relocation = globalOffset + lmap.gotRegionOffset + addend - relOffset;
184 result = lmap.gotSymbols.find(rel.getDynSym());
185 if( result == lmap.gotSymbols.end() ) {
186 errMsg = "Expected GOT symbol does not exist in GOT symbol mapping";
190 relocation = result->second + lmap.gotRegionOffset + globalOffset;
194 tmp << "ERROR: encountered relocation type(" << rel.getRelType() <<
195 ") that is meant for use during dynamic linking";
199 tmp << "Relocation type " << rel.getRelType()
200 << " currently unimplemented";
205 rewrite_printf("relocation = 0x%lx @ 0x%lx\n", relocation, relOffset);
207 memcpy(&targetData[dest], &relocation, sizeof(Elf32_Word));
208 }else if( X86_64_WIDTH == addressWidth_ ) {
210 * Referring to the SYSV supplement:
212 * There are a few data types used by x86_64 relocations.
216 * word32 = Elf64_Word
217 * word64 = Elf64_Xword
224 * x86_64 only uses relocations that contain the addend.
226 Offset symbolOffset = rel.getDynSym()->getOffset();
227 unsigned symbolSize = rel.getDynSym()->getSize();
229 Elf64_Xword addend = 0;
230 if( Region::RT_RELA == rel.regionType() ) {
231 addend = rel.addend();
234 if( !computeCtorDtorAddress(rel, globalOffset, lmap, errMsg, symbolOffset) ) {
238 rewrite_printf("relocation for '%s': TYPE = %s(%lu) S = %llx A = %llx P = %llx Z = %lx\n",
240 relocationEntry::relType2Str(rel.getRelType(), addressWidth_),
242 symbolOffset, addend, relOffset, symbolSize);
244 Offset relocation = 0;
245 unsigned fieldSize = 0; // This is set depending on the type of relocation
246 map<Symbol *, Offset>::iterator result;
249 switch(rel.getRelType()) {
251 fieldSize = sizeof(Elf64_Xword);
252 relocation = symbolOffset + addend;
256 fieldSize = sizeof(Elf64_Word);
257 relocation = symbolOffset + addend - relOffset;
259 case R_X86_64_GOT32: // The address is computed when the GOT is built
260 fieldSize = sizeof(Elf64_Word);
261 result = lmap.gotSymbols.find(rel.getDynSym());
262 if( result == lmap.gotSymbols.end() ) {
263 errMsg = "Expected GOT symbol does not exist in GOT symbol mapping";
267 relocation = result->second + addend;
269 case R_X86_64_TPOFF32: // The necessary value is set during storage allocation
270 fieldSize = sizeof(Elf64_Word);
271 relocation = symbolOffset;
273 case R_X86_64_GLOB_DAT:
274 case R_X86_64_JUMP_SLOT:
275 fieldSize = sizeof(Elf64_Xword);
276 relocation = symbolOffset;
278 case R_X86_64_TLSLD: // Load the symbol offset from the GOT
280 case R_X86_64_GOTTPOFF:
281 case R_X86_64_GOTPCREL:
282 fieldSize = sizeof(Elf64_Word);
284 result = lmap.gotSymbols.find(rel.getDynSym());
285 if( result == lmap.gotSymbols.end() ) {
286 errMsg = "Expected GOT symbol does not exist in GOT symbol mapping";
290 relocation = result->second + lmap.gotRegionOffset +
291 globalOffset + addend - relOffset;
293 case R_X86_64_DTPOFF32:
294 fieldSize = sizeof(Elf64_Word);
299 fieldSize = sizeof(Elf64_Word);
300 relocation = symbolOffset + addend;
303 fieldSize = sizeof(uint16_t);
304 relocation = symbolOffset + addend;
307 fieldSize = sizeof(uint16_t);
308 relocation = symbolOffset + addend - relOffset;
311 fieldSize = sizeof(uint8_t);
312 relocation = symbolOffset + addend;
315 fieldSize = sizeof(uint8_t);
316 relocation = symbolOffset + addend - relOffset;
318 case R_X86_64_RELATIVE:
320 tmp << "ERROR: encountered relocation type(" << rel.getRelType() <<
321 ") that is meant for use during dynamic linking";
324 #if defined(R_X86_64_IRELATIVE)
325 case R_X86_64_IRELATIVE:
326 // Consistency error; we should never try to process one of these
331 case R_X86_64_DTPMOD64:
332 case R_X86_64_DTPOFF64:
333 case R_X86_64_TPOFF64:
335 tmp << "Relocation type " << rel.getRelType()
336 << " currently unimplemented";
341 rewrite_printf("relocation = 0x%llx @ 0x%llx\n", relocation, relOffset);
343 memcpy(&targetData[dest], &relocation, fieldSize);
345 assert(!UNKNOWN_ADDRESS_WIDTH_ASSERT);
351 static const string LIBC_ATEXIT_INTERNAL("__cxa_exit");
352 static const string LIBC_ATEXIT("atexit");
354 bool emitElfStatic::checkSpecialCaseSymbols(Symtab *member, Symbol *checkSym) {
358 * When linking code into stripped binaries, some linked functions will not
359 * work correctly because they rely on some state being initialized or
360 * updated in the original binary.
362 * For example, the libc function atexit internally keeps a list of
363 * functions to call after the main function returns. When main returns,
364 * this list is processed by libc finalization code.
366 * When the original binary is stripped, there is a disconnect between the
367 * atexit function in the linked code and the original atexit function.
368 * Consequently, any functionality that relies on atexit will not work
369 * (i.e. destructors for global C++ objects).
371 * The initial release of the binary rewriter for static binaries doesn't
372 * provide a solution to this problem. Therefore, a warning needs to be
373 * generated to alert the user to this outstanding problem.
376 if( LIBC_ATEXIT_INTERNAL == checkSym->getPrettyName() ||
377 LIBC_ATEXIT == checkSym->getPrettyName() )
379 fprintf(stderr, "WARNING: code in %s(%s) references the libc function %s.\n",
380 member->getParentArchive()->name().c_str(),
381 member->memberName().c_str(), checkSym->getPrettyName().c_str());
382 fprintf(stderr, " Also, the binary to be rewritten is stripped.\n");
383 fprintf(stderr, " Currently, the combination of these two "
384 "is unsupported and unexpected behavior may occur.\n");
391 /* The TLS implementation on x86 is Variant 2 */
393 Offset emitElfStatic::layoutTLSImage(Offset globalOffset, Region *dataTLS, Region *bssTLS, LinkMap &lmap) {
394 return tlsLayoutVariant2(globalOffset, dataTLS, bssTLS, lmap);
397 Offset emitElfStatic::adjustTLSOffset(Offset curOffset, Offset tlsSize) {
398 return tlsAdjustVariant2(curOffset, tlsSize);
401 char emitElfStatic::getPaddingValue(Region::RegionType rtype) {
402 const char X86_NOP = 0x90;
405 if( rtype == Region::RT_TEXT || rtype == Region::RT_TEXTDATA ) {
412 void emitElfStatic::cleanupTLSRegionOffsets(map<Region *, LinkMap::AllocPair> ®ionAllocs,
413 Region *dataTLS, Region *bssTLS)
415 tlsCleanupVariant2(regionAllocs, dataTLS, bssTLS);
418 void emitElfStatic::getExcludedSymbolNames(set<string> &symNames) {
420 * It appears that some .o's have a reference to _GLOBAL_OFFSET_TABLE_
421 * This symbol is an indication to the linker that a GOT should be
422 * created, it isn't a symbol that should be resolved.
424 * Consequently, a GOT shouldn't always be created when linking
425 * the .o's into the target. A GOT is built when certain relocations
426 * exist that require a GOT.
428 symNames.insert("_GLOBAL_OFFSET_TABLE_");
431 bool emitElfStatic::isGOTRelocation(unsigned long relType) {
432 if( X86_WIDTH == addressWidth_ ) {
436 case R_386_TLS_GOTIE:
445 }else if( X86_64_WIDTH == addressWidth_ ) {
449 case R_X86_64_GOTTPOFF:
451 case R_X86_64_GOTPCREL:
459 assert(!UNKNOWN_ADDRESS_WIDTH_ASSERT);
465 Offset emitElfStatic::getGOTSize(Symtab *, LinkMap &lmap) {
468 unsigned slotSize = 0;
469 if( X86_WIDTH == addressWidth_ ) {
470 slotSize = sizeof(Elf32_Addr);
471 }else if( X86_64_WIDTH == addressWidth_ ) {
472 slotSize = sizeof(Elf64_Addr);
474 assert(!UNKNOWN_ADDRESS_WIDTH_ASSERT);
477 // According to the ELF abi, entries 0, 1, 2 are reserved in a GOT on x86
478 if( lmap.gotSymbols.size() > 0 ) {
479 size = (lmap.gotSymbols.size()+GOT_RESERVED_SLOTS)*slotSize;
485 Offset emitElfStatic::getGOTAlign(LinkMap &) {
486 if( X86_WIDTH == addressWidth_ ) {
487 return sizeof(Elf32_Word);
488 }else if( X86_64_WIDTH == addressWidth_ ) {
489 return sizeof(Elf64_Xword);
491 assert(!UNKNOWN_ADDRESS_WIDTH_ASSERT);
497 void emitElfStatic::buildGOT(Symtab *, LinkMap &lmap) {
498 char *targetData = lmap.allocatedData;
500 unsigned slotSize = 0;
501 if( X86_WIDTH == addressWidth_ ) {
502 slotSize = sizeof(Elf32_Addr);
503 }else if( X86_64_WIDTH == addressWidth_ ) {
504 slotSize = sizeof(Elf64_Addr);
506 assert(!UNKNOWN_ADDRESS_WIDTH_ASSERT);
509 // For each GOT symbol, allocate an entry and copy the value of the
510 // symbol into the table, additionally store the offset in the GOT
512 Offset curOffset = GOT_RESERVED_SLOTS*slotSize;
513 memset(&targetData[lmap.gotRegionOffset], 0, GOT_RESERVED_SLOTS*slotSize);
515 map<Symbol *, Offset>::iterator sym_it;
516 for(sym_it = lmap.gotSymbols.begin(); sym_it != lmap.gotSymbols.end(); ++sym_it) {
517 Offset value = sym_it->first->getOffset();
518 memcpy(&targetData[lmap.gotRegionOffset + curOffset], &value, slotSize);
520 sym_it->second = curOffset;
522 curOffset += slotSize;
527 * .ctors and .dtors section handling
529 * .ctors/.dtors sections are not defined by the ELF standard, LSB defines them.
530 * This is why this implementation is specific to Linux and x86.
532 * Layout of .ctors and .dtors sections on Linux x86
534 * Executable .ctors/.dtors format (size in bytes = n)
536 * byte 0..3 byte 4..7 byte 8..11 byte n-4..n-1
537 * 0xffffffff <func. ptr 1> <func. ptr 2> ... 0x00000000
539 * Relocatable file .ctors/.dtors format (size in bytes = n)
541 * byte 0..3 byte n-4..n-1
542 * <func. ptr 1> ... <last func. ptr>
544 * The layout is the same on Linux x86_64 except each entry is 8 bytes
545 * instead of 4. So the header and trailler are the same, but extended to
548 static const Elf64_Word X86_HEADER = 0xffffffff;
549 static const Elf64_Word X86_TRAILER = 0x00000000;
550 static const Elf64_Xword X86_64_HEADER = 0xffffffffffffffffULL;
551 static const Elf64_Xword X86_64_TRAILER = 0x0000000000000000ULL;
552 static const string DTOR_NAME(".dtors");
553 static const string CTOR_NAME(".ctors");
555 bool emitElfStatic::isConstructorRegion(Region *reg) {
556 return ( CTOR_NAME.compare(reg->getRegionName()) == 0 );
559 bool emitElfStatic::isGOTRegion(Region *) {
563 Offset emitElfStatic::layoutNewCtorRegion(LinkMap &lmap) {
565 * .ctors sections are processed in reverse order on Linux x86. New .ctors
566 * sections need to be placed before the original .ctors section
568 Offset retOffset = lmap.ctorRegionOffset;
569 retOffset += addressWidth_;
571 pair<map<Region *, LinkMap::AllocPair>::iterator, bool> result;
573 vector<Region *>::iterator reg_it;
574 for(reg_it = lmap.newCtorRegions.begin(); reg_it != lmap.newCtorRegions.end(); ++reg_it) {
575 result = lmap.regionAllocs.insert(make_pair(*reg_it, make_pair(0, retOffset)));
577 // If the map already contains this Region, this is a logic error
578 if( !result.second ) {
582 retOffset += (*reg_it)->getDiskSize();
585 if( lmap.originalCtorRegion != NULL ) {
586 // Account for original .ctors section (minus the header and trailer)
587 retOffset += lmap.originalCtorRegion->getDiskSize() - addressWidth_ - addressWidth_;
589 retOffset += addressWidth_;
594 bool emitElfStatic::createNewCtorRegion(LinkMap &lmap) {
595 char *targetData = lmap.allocatedData;
597 if( X86_WIDTH != addressWidth_ && X86_64_WIDTH != addressWidth_ ) {
598 assert(!UNKNOWN_ADDRESS_WIDTH_ASSERT);
601 unsigned trailerSize, headerSize;
603 /* Give the new Region a header and trailer */
604 Offset headerOffset = lmap.ctorRegionOffset;
605 Offset trailerOffset;
606 if( X86_WIDTH == addressWidth_ ) {
607 memcpy(&targetData[headerOffset], &X86_HEADER, sizeof(X86_HEADER));
608 trailerOffset = lmap.ctorRegionOffset + lmap.ctorSize - sizeof(X86_TRAILER);
609 memcpy(&targetData[trailerOffset], &X86_TRAILER, sizeof(X86_TRAILER));
610 headerSize = sizeof(X86_HEADER);
611 trailerSize = sizeof(X86_TRAILER);
613 memcpy(&targetData[headerOffset], &X86_64_HEADER, sizeof(X86_64_HEADER));
614 trailerOffset = lmap.ctorRegionOffset + lmap.ctorSize - sizeof(X86_64_TRAILER);
615 memcpy(&targetData[trailerOffset], &X86_64_TRAILER, sizeof(X86_64_TRAILER));
616 headerSize = sizeof(X86_64_HEADER);
617 trailerSize = sizeof(X86_64_TRAILER);
620 if( lmap.originalCtorRegion != NULL ) {
621 /* Determine where the original .ctors section should be placed */
622 Offset originalOffset = lmap.ctorRegionOffset + lmap.ctorSize -
623 trailerSize - (lmap.originalCtorRegion->getDiskSize() - headerSize - trailerSize);
625 /* Copy the original .ctors section w/o the header and trailer */
626 char *rawRegionData = reinterpret_cast<char *>(lmap.originalCtorRegion->getPtrToRawData());
627 memcpy(&targetData[originalOffset], &rawRegionData[headerSize],
628 lmap.originalCtorRegion->getDiskSize() - headerSize - trailerSize);
634 bool emitElfStatic::isDestructorRegion(Region *reg) {
635 return ( DTOR_NAME.compare(reg->getRegionName()) == 0 );
638 Offset emitElfStatic::layoutNewDtorRegion(LinkMap &lmap) {
640 * .dtors sections are processed in forward order on Linux x86. So new
641 * .dtors sections need to be placed after the original .dtors section
643 Offset retOffset = lmap.dtorRegionOffset;
644 retOffset += addressWidth_;
646 pair<map<Region *, LinkMap::AllocPair>::iterator, bool> result;
647 if( lmap.originalDtorRegion != NULL ) {
648 // Account for the original .dtors section (minus the header and trailer)
649 retOffset += lmap.originalDtorRegion->getDiskSize() - addressWidth_ - addressWidth_;
652 vector<Region *>::iterator reg_it;
653 for(reg_it = lmap.newDtorRegions.begin(); reg_it != lmap.newDtorRegions.end(); ++reg_it) {
654 result = lmap.regionAllocs.insert(make_pair(*reg_it, make_pair(0, retOffset)));
656 // If the map already contains this Region, this is a logic error
657 if( !result.second ) {
661 retOffset += (*reg_it)->getDiskSize();
664 retOffset += addressWidth_;
668 bool emitElfStatic::createNewDtorRegion(LinkMap &lmap) {
669 char *targetData = lmap.allocatedData;
671 if( X86_WIDTH != addressWidth_ && X86_64_WIDTH != addressWidth_ ) {
672 assert(!UNKNOWN_ADDRESS_WIDTH_ASSERT);
675 unsigned headerSize, trailerSize;
677 /* Give the new Region a header and trailer */
678 Offset headerOffset = lmap.dtorRegionOffset;
679 Offset trailerOffset;
680 if( X86_WIDTH == addressWidth_ ) {
681 memcpy(&targetData[headerOffset], &X86_HEADER, sizeof(X86_HEADER));
682 trailerOffset = lmap.dtorRegionOffset + lmap.dtorSize - sizeof(X86_TRAILER);
683 memcpy(&targetData[trailerOffset], &X86_TRAILER, sizeof(X86_TRAILER));
684 headerSize = sizeof(X86_HEADER);
685 trailerSize = sizeof(X86_TRAILER);
687 memcpy(&targetData[headerOffset], &X86_64_HEADER, sizeof(X86_64_HEADER));
688 trailerOffset = lmap.dtorRegionOffset + lmap.dtorSize - sizeof(X86_64_TRAILER);
689 memcpy(&targetData[trailerOffset], &X86_64_TRAILER, sizeof(X86_64_TRAILER));
690 headerSize = sizeof(X86_64_HEADER);
691 trailerSize = sizeof(X86_64_TRAILER);
694 if( lmap.originalDtorRegion != NULL ) {
695 /* Determine where the original .dtors section should be placed */
696 Offset originalOffset = lmap.dtorRegionOffset + headerSize;
698 /* Copy the original .dtors section w/o header and trailer */
699 char *rawRegionData = reinterpret_cast<char *>(lmap.originalDtorRegion->getPtrToRawData());
700 memcpy(&targetData[originalOffset], &rawRegionData[headerSize],
701 lmap.originalDtorRegion->getDiskSize() - headerSize - trailerSize);