X-Git-Url: https://git.saurik.com/apple/ld64.git/blobdiff_plain/a645023da60d22e86be13f7b4d97adeff8bc6665..b1f7435d66a93f03b77932b3a9ad8a83ce5e1ebc:/src/ld/OutputFile.cpp diff --git a/src/ld/OutputFile.cpp b/src/ld/OutputFile.cpp index bb368c2..0f11536 100644 --- a/src/ld/OutputFile.cpp +++ b/src/ld/OutputFile.cpp @@ -1,6 +1,6 @@ /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-* * - * Copyright (c) 2009-2010 Apple Inc. All rights reserved. + * Copyright (c) 2009-2011 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -72,11 +72,12 @@ namespace tool { OutputFile::OutputFile(const Options& opts) : hasWeakExternalSymbols(false), usesWeakExternalSymbols(false), overridesWeakExternalSymbols(false), - _noReExportedDylibs(false), hasThreadLocalVariableDefinitions(false), pieDisabled(false), + _noReExportedDylibs(false), hasThreadLocalVariableDefinitions(false), pieDisabled(false), hasDataInCode(false), headerAndLoadCommandsSection(NULL), rebaseSection(NULL), bindingSection(NULL), weakBindingSection(NULL), lazyBindingSection(NULL), exportSection(NULL), splitSegInfoSection(NULL), functionStartsSection(NULL), + dataInCodeSection(NULL), dependentDRsSection(NULL), symbolTableSection(NULL), stringPoolSection(NULL), localRelocationsSection(NULL), externalRelocationsSection(NULL), sectionRelocationsSection(NULL), @@ -87,6 +88,8 @@ OutputFile::OutputFile(const Options& opts) _hasSectionRelocations(opts.outputKind() == Options::kObjectFile), _hasSplitSegInfo(opts.sharedRegionEligible()), _hasFunctionStartsInfo(opts.addFunctionStarts()), + _hasDataInCodeInfo(opts.addDataInCodeInfo()), + _hasDependentDRInfo(opts.needsDependentDRInfo()), _hasDynamicSymbolTable(true), _hasLocalRelocations(!opts.makeCompressedDyldInfo()), _hasExternalRelocations(!opts.makeCompressedDyldInfo()), @@ -109,7 +112,9 @@ OutputFile::OutputFile(const Options& opts) _weakBindingInfoAtom(NULL), _exportInfoAtom(NULL), _splitSegInfoAtom(NULL), - _functionStartsAtom(NULL) + _functionStartsAtom(NULL), + _dataInCodeAtom(NULL), + _dependentDRInfoAtom(NULL) { } @@ -179,10 +184,14 @@ bool OutputFile::findSegment(ld::Internal& state, uint64_t addr, uint64_t* start void OutputFile::assignAtomAddresses(ld::Internal& state) { + const bool log = false; + if ( log ) fprintf(stderr, "assignAtomAddresses()\n"); for (std::vector::iterator sit = state.sections.begin(); sit != state.sections.end(); ++sit) { ld::Internal::FinalSection* sect = *sit; + if ( log ) fprintf(stderr, " section=%s/%s\n", sect->segmentName(), sect->sectionName()); for (std::vector::iterator ait = sect->atoms.begin(); ait != sect->atoms.end(); ++ait) { const ld::Atom* atom = *ait; + if ( log ) fprintf(stderr, " atom=%p, name=%s\n", atom, atom->name()); switch ( sect-> type() ) { case ld::Section::typeImportProxies: // want finalAddress() of all proxy atoms to be zero @@ -239,6 +248,18 @@ void OutputFile::updateLINKEDITAddresses(ld::Internal& state) _functionStartsAtom->encode(); } + if ( _options.addDataInCodeInfo() ) { + // build data-in-code info + assert(_dataInCodeAtom != NULL); + _dataInCodeAtom->encode(); + } + + if ( _options.needsDependentDRInfo() ) { + // build dependent dylib DR info + assert(_dependentDRInfoAtom != NULL); + _dependentDRInfoAtom->encode(); + } + // build classic symbol table assert(_symbolTableAtom != NULL); _symbolTableAtom->encode(); @@ -320,10 +341,25 @@ void OutputFile::setSectionSizesAndAlignments(ld::Internal& state) uint64_t offset = 0; for (std::vector::iterator ait = sect->atoms.begin(); ait != sect->atoms.end(); ++ait) { const ld::Atom* atom = *ait; - if ( atom->alignment().powerOf2 > maxAlignment ) - maxAlignment = atom->alignment().powerOf2; + bool pagePerAtom = false; + uint32_t atomAlignmentPowerOf2 = atom->alignment().powerOf2; + if ( _options.pageAlignDataAtoms() && ( strcmp(atom->section().segmentName(), "__DATA") == 0) ) { + switch ( atom->section().type() ) { + case ld::Section::typeUnclassified: + case ld::Section::typeTentativeDefs: + case ld::Section::typeZeroFill: + pagePerAtom = true; + if ( atomAlignmentPowerOf2 < 12 ) + atomAlignmentPowerOf2 = 12; + break; + default: + break; + } + } + if ( atomAlignmentPowerOf2 > maxAlignment ) + maxAlignment = atomAlignmentPowerOf2; // calculate section offset for this atom - uint64_t alignment = 1 << atom->alignment().powerOf2; + uint64_t alignment = 1 << atomAlignmentPowerOf2; uint64_t currentModulus = (offset % alignment); uint64_t requiredModulus = atom->alignment().modulus; if ( currentModulus != requiredModulus ) { @@ -336,6 +372,9 @@ void OutputFile::setSectionSizesAndAlignments(ld::Internal& state) if ( sect->type() != ld::Section::typeLinkEdit ) { (const_cast(atom))->setSectionOffset(offset); offset += atom->size(); + if ( pagePerAtom ) { + offset = (offset + 4095) & (-4096); // round up to end of page + } } if ( (atom->scope() == ld::Atom::scopeGlobal) && (atom->definition() == ld::Atom::definitionRegular) @@ -498,6 +537,8 @@ void OutputFile::assignFileOffsets(ld::Internal& state) // second pass, assign section address to sections in segments that are contiguous with previous segment address = floatingAddressStart; lastSegName = ""; + ld::Internal::FinalSection* overlappingFixedSection = NULL; + ld::Internal::FinalSection* overlappingFlowSection = NULL; if ( log ) fprintf(stderr, "Regular layout segments:\n"); for (std::vector::iterator it = state.sections.begin(); it != state.sections.end(); ++it) { ld::Internal::FinalSection* sect = *it; @@ -532,14 +573,48 @@ void OutputFile::assignFileOffsets(ld::Internal& state) && (_options.outputKind() != Options::kStaticExecutable) ) throwf("section %s (address=0x%08llX, size=%llu) would make the output executable exceed available address range", sect->sectionName(), address, sect->size); + + // sanity check it does not overlap a fixed address segment + for (std::vector::iterator sit = state.sections.begin(); sit != state.sections.end(); ++sit) { + ld::Internal::FinalSection* otherSect = *sit; + if ( ! _options.hasCustomSegmentAddress(otherSect->segmentName()) ) + continue; + if ( sect->address > otherSect->address ) { + if ( (otherSect->address+otherSect->size) > sect->address ) { + overlappingFixedSection = otherSect; + overlappingFlowSection = sect; + } + } + else { + if ( (sect->address+sect->size) > otherSect->address ) { + overlappingFixedSection = otherSect; + overlappingFlowSection = sect; + } + } + } - if ( log ) fprintf(stderr, " address=0x%08llX, hidden=%d, alignment=%02d, padBytes=%d, section=%s,%s\n", - sect->address, sect->isSectionHidden(), sect->alignment, sect->alignmentPaddingBytes, + if ( log ) fprintf(stderr, " address=0x%08llX, size=0x%08llX, hidden=%d, alignment=%02d, padBytes=%d, section=%s,%s\n", + sect->address, sect->size, sect->isSectionHidden(), sect->alignment, sect->alignmentPaddingBytes, sect->segmentName(), sect->sectionName()); // update running totals if ( !sect->isSectionHidden() || hiddenSectionsOccupyAddressSpace ) address += sect->size; } + if ( overlappingFixedSection != NULL ) { + fprintf(stderr, "Section layout:\n"); + for (std::vector::iterator it = state.sections.begin(); it != state.sections.end(); ++it) { + ld::Internal::FinalSection* sect = *it; + if ( sect->isSectionHidden() ) + continue; + fprintf(stderr, " address:0x%08llX, alignment:2^%d, size:0x%08llX, padBytes:%d, section:%s/%s\n", + sect->address, sect->alignment, sect->size, sect->alignmentPaddingBytes, + sect->segmentName(), sect->sectionName()); + + } + throwf("Section (%s/%s) overlaps fixed address section (%s/%s)", + overlappingFlowSection->segmentName(), overlappingFlowSection->sectionName(), + overlappingFixedSection->segmentName(), overlappingFixedSection->sectionName()); + } // third pass, assign section file offsets @@ -551,6 +626,9 @@ void OutputFile::assignFileOffsets(ld::Internal& state) if ( hasZeroForFileOffset(sect) ) { // fileoff of zerofill sections is moot, but historically it is set to zero sect->fileOffset = 0; + + // align file offset with address layout + fileOffset += sect->alignmentPaddingBytes; } else { // page align file offset at start of each segment @@ -581,7 +659,7 @@ void OutputFile::assignFileOffsets(ld::Internal& state) for (std::vector::iterator it = state.sections.begin(); it != state.sections.end(); ++it) { ld::Internal::FinalSection* sect = *it; if ( strcmp(sect->segmentName(), "__TEXT") == 0 ) { - _encryptedTEXTendOffset = pageAlign(sect->fileOffset); + _encryptedTEXTendOffset = pageAlign(sect->fileOffset + sect->size); } } } @@ -659,6 +737,11 @@ uint64_t OutputFile::addressOf(const ld::Internal& state, const ld::Fixup* fixup return (*target)->finalAddress(); case ld::Fixup::bindingsIndirectlyBound: *target = state.indirectBindingTable[fixup->u.bindingIndex]; + #ifndef NDEBUG + if ( ! (*target)->finalAddressMode() ) { + throwf("reference to symbol (which has not been assigned an address) %s", (*target)->name()); + } + #endif return (*target)->finalAddress(); } throw "unexpected binding"; @@ -773,16 +856,49 @@ void OutputFile::rangeCheckBranch32(int64_t displacement, ld::Internal& state, c printSectionLayout(state); const ld::Atom* target; - throwf("32-bit branch out of range (%lld max is +/-4GB): from %s (0x%08llX) to %s (0x%08llX)", + throwf("32-bit branch out of range (%lld max is +/-2GB): from %s (0x%08llX) to %s (0x%08llX)", displacement, atom->name(), atom->finalAddress(), referenceTargetAtomName(state, fixup), addressOf(state, fixup, &target)); } } + +void OutputFile::rangeCheckAbsolute32(int64_t displacement, ld::Internal& state, const ld::Atom* atom, const ld::Fixup* fixup) +{ + const int64_t fourGigLimit = 0xFFFFFFFF; + if ( displacement > fourGigLimit ) { + // cannot enforce 32-bit range checks on 32-bit archs because assembler loses sign information + // .long _foo - 0xC0000000 + // is encoded in mach-o the same as: + // .long _foo + 0x40000000 + // so if _foo lays out to 0xC0000100, the first is ok, but the second is not. + if ( (_options.architecture() == CPU_TYPE_ARM) || (_options.architecture() == CPU_TYPE_I386) ) { + // Unlikely userland code does funky stuff like this, so warn for them, but not warn for -preload + if ( _options.outputKind() != Options::kPreload ) { + warning("32-bit absolute address out of range (0x%08llX max is 4GB): from %s + 0x%08X (0x%08llX) to 0x%08llX", + displacement, atom->name(), fixup->offsetInAtom, atom->finalAddress(), displacement); + } + return; + } + // show layout of final image + printSectionLayout(state); + + const ld::Atom* target; + if ( fixup->binding == ld::Fixup::bindingNone ) + throwf("32-bit absolute address out of range (0x%08llX max is 4GB): from %s + 0x%08X (0x%08llX) to 0x%08llX", + displacement, atom->name(), fixup->offsetInAtom, atom->finalAddress(), displacement); + else + throwf("32-bit absolute address out of range (0x%08llX max is 4GB): from %s + 0x%08X (0x%08llX) to %s (0x%08llX)", + displacement, atom->name(), fixup->offsetInAtom, atom->finalAddress(), referenceTargetAtomName(state, fixup), + addressOf(state, fixup, &target)); + } +} + + void OutputFile::rangeCheckRIP32(int64_t displacement, ld::Internal& state, const ld::Atom* atom, const ld::Fixup* fixup) { const int64_t twoGigLimit = 0x7FFFFFFF; - if ( (displacement > twoGigLimit) || (displacement < (-twoGigLimit)) ) { + if ( (displacement > twoGigLimit) || (displacement < (-twoGigLimit)) ) { // show layout of final image printSectionLayout(state); @@ -822,8 +938,8 @@ void OutputFile::rangeCheckARMBranch24(int64_t displacement, ld::Internal& state void OutputFile::rangeCheckThumbBranch22(int64_t displacement, ld::Internal& state, const ld::Atom* atom, const ld::Fixup* fixup) { - // armv7 supports a larger displacement - if ( _options.preferSubArchitecture() && (_options.subArchitecture() == CPU_SUBTYPE_ARM_V7) ) { + // thumb2 supports a larger displacement + if ( _options.preferSubArchitecture() && _options.archSupportsThumb2() ) { if ( (displacement > 16777214LL) || (displacement < (-16777216LL)) ) { // show layout of final image printSectionLayout(state); @@ -847,33 +963,6 @@ void OutputFile::rangeCheckThumbBranch22(int64_t displacement, ld::Internal& sta } } -void OutputFile::rangeCheckPPCBranch24(int64_t displacement, ld::Internal& state, const ld::Atom* atom, const ld::Fixup* fixup) -{ - const int64_t bl_eightMegLimit = 0x00FFFFFF; - if ( (displacement > bl_eightMegLimit) || (displacement < (-bl_eightMegLimit)) ) { - // show layout of final image - printSectionLayout(state); - - const ld::Atom* target; - throwf("bl PPC branch out of range (%lld max is +/-16MB): from %s (0x%08llX) to %s (0x%08llX)", - displacement, atom->name(), atom->finalAddress(), referenceTargetAtomName(state, fixup), - addressOf(state, fixup, &target)); - } -} - -void OutputFile::rangeCheckPPCBranch14(int64_t displacement, ld::Internal& state, const ld::Atom* atom, const ld::Fixup* fixup) -{ - const int64_t b_sixtyFourKiloLimit = 0x0000FFFF; - if ( (displacement > b_sixtyFourKiloLimit) || (displacement < (-b_sixtyFourKiloLimit)) ) { - // show layout of final image - printSectionLayout(state); - - const ld::Atom* target; - throwf("bcc PPC branch out of range (%lld max is +/-64KB): from %s (0x%08llX) to %s (0x%08llX)", - displacement, atom->name(), atom->finalAddress(), referenceTargetAtomName(state, fixup), - addressOf(state, fixup, &target)); - } -} @@ -905,7 +994,6 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld:: int64_t delta; uint32_t instruction; uint32_t newInstruction; - uint16_t instructionLowHalf; bool is_bl; bool is_blx; bool is_b; @@ -967,6 +1055,7 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld:: set32LE(fixUpLocation, (get32LE(fixUpLocation) & 0xFF000000) | (accumulator & 0x00FFFFFF) ); break; case ld::Fixup::kindStoreLittleEndian32: + rangeCheckAbsolute32(accumulator, state, atom, fit); set32LE(fixUpLocation, accumulator); break; case ld::Fixup::kindStoreLittleEndian64: @@ -979,6 +1068,7 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld:: set32BE(fixUpLocation, (get32BE(fixUpLocation) & 0xFF000000) | (accumulator & 0x00FFFFFF) ); break; case ld::Fixup::kindStoreBigEndian32: + rangeCheckAbsolute32(accumulator, state, atom, fit); set32BE(fixUpLocation, accumulator); break; case ld::Fixup::kindStoreBigEndian64: @@ -1092,41 +1182,6 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld:: } set32LE(fixUpLocation, newInstruction); break; - case ld::Fixup::kindStorePPCBranch14: - delta = accumulator - (atom->finalAddress() + fit->offsetInAtom); - rangeCheckPPCBranch14(delta, state, atom, fit); - instruction = get32BE(fixUpLocation); - newInstruction = (instruction & 0xFFFF0003) | ((uint32_t)delta & 0x0000FFFC); - set32BE(fixUpLocation, newInstruction); - break; - case ld::Fixup::kindStorePPCPicLow14: - case ld::Fixup::kindStorePPCAbsLow14: - instruction = get32BE(fixUpLocation); - if ( (accumulator & 0x3) != 0 ) - throwf("bad offset (0x%08X) for lo14 instruction pic-base fix-up", (uint32_t)accumulator); - newInstruction = (instruction & 0xFFFF0003) | (accumulator & 0xFFFC); - set32BE(fixUpLocation, newInstruction); - break; - case ld::Fixup::kindStorePPCAbsLow16: - case ld::Fixup::kindStorePPCPicLow16: - instruction = get32BE(fixUpLocation); - newInstruction = (instruction & 0xFFFF0000) | (accumulator & 0xFFFF); - set32BE(fixUpLocation, newInstruction); - break; - case ld::Fixup::kindStorePPCAbsHigh16AddLow: - case ld::Fixup::kindStorePPCPicHigh16AddLow: - instructionLowHalf = (accumulator >> 16) & 0xFFFF; - if ( accumulator & 0x00008000 ) - ++instructionLowHalf; - instruction = get32BE(fixUpLocation); - newInstruction = (instruction & 0xFFFF0000) | instructionLowHalf; - set32BE(fixUpLocation, newInstruction); - break; - case ld::Fixup::kindStorePPCAbsHigh16: - instruction = get32BE(fixUpLocation); - newInstruction = (instruction & 0xFFFF0000) | ((accumulator >> 16) & 0xFFFF); - set32BE(fixUpLocation, newInstruction); - break; case ld::Fixup::kindDtraceExtra: break; case ld::Fixup::kindStoreX86DtraceCallSiteNop: @@ -1149,18 +1204,6 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld:: fixUpLocation[3] = 0x90; // 1-byte nop } break; - case ld::Fixup::kindStorePPCDtraceCallSiteNop: - if ( _options.outputKind() != Options::kObjectFile ) { - // change call site to a NOP - set32BE(fixUpLocation, 0x60000000); - } - break; - case ld::Fixup::kindStorePPCDtraceIsEnableSiteClear: - if ( _options.outputKind() != Options::kObjectFile ) { - // change call site to a li r3,0 - set32BE(fixUpLocation, 0x38600000); - } - break; case ld::Fixup::kindStoreARMDtraceCallSiteNop: if ( _options.outputKind() != Options::kObjectFile ) { // change call site to a NOP @@ -1191,6 +1234,13 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld:: assert(fit->binding == ld::Fixup::bindingDirectlyBound); accumulator = this->lazyBindingInfoOffsetForLazyPointerAddress(fit->u.target->finalAddress()); break; + case ld::Fixup::kindDataInCodeStartData: + case ld::Fixup::kindDataInCodeStartJT8: + case ld::Fixup::kindDataInCodeStartJT16: + case ld::Fixup::kindDataInCodeStartJT32: + case ld::Fixup::kindDataInCodeStartJTA32: + case ld::Fixup::kindDataInCodeEnd: + break; case ld::Fixup::kindStoreTargetAddressLittleEndian32: accumulator = addressOf(state, fit, &toTarget); thumbTarget = targetIsThumb(state, fit); @@ -1198,6 +1248,7 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld:: accumulator |= 1; if ( fit->contentAddendOnly ) accumulator = 0; + rangeCheckAbsolute32(accumulator, state, atom, fit); set32LE(fixUpLocation, accumulator); break; case ld::Fixup::kindStoreTargetAddressLittleEndian64: @@ -1287,6 +1338,7 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld:: // Make sure we are calling arm with bl, thumb with blx is_bl = ((instruction & 0xFF000000) == 0xEB000000); is_blx = ((instruction & 0xFE000000) == 0xFA000000); + is_b = !is_blx && ((instruction & 0x0F000000) == 0x0A000000); if ( is_bl && thumbTarget ) { uint32_t opcode = 0xFA000000; uint32_t disp = (uint32_t)(delta >> 2) & 0x00FFFFFF; @@ -1298,6 +1350,13 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld:: uint32_t disp = (uint32_t)(delta >> 2) & 0x00FFFFFF; newInstruction = opcode | disp; } + else if ( is_b && thumbTarget ) { + if ( fit->contentDetlaToAddendOnly ) + newInstruction = (instruction & 0xFF000000) | ((uint32_t)(delta >> 2) & 0x00FFFFFF); + else + throwf("no pc-rel bx arm instruction. Can't fix up branch to %s in %s", + referenceTargetAtomName(state, fit), atom->name()); + } else if ( !is_bl && !is_blx && thumbTarget ) { throwf("don't know how to convert instruction %x referencing %s to thumb", instruction, referenceTargetAtomName(state, fit)); @@ -1324,14 +1383,14 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld:: // Since blx cannot have the low bit set, set bit[1] of the target to // bit[1] of the base address, so that the difference is a multiple of // 4 bytes. - if ( !thumbTarget ) { + if ( !thumbTarget && !fit->contentDetlaToAddendOnly ) { accumulator &= -3ULL; accumulator |= ((atom->finalAddress() + fit->offsetInAtom ) & 2LL); } // The pc added will be +4 from the pc delta = accumulator - (atom->finalAddress() + fit->offsetInAtom + 4); rangeCheckThumbBranch22(delta, state, atom, fit); - if ( _options.preferSubArchitecture() && _options.subArchitecture() == CPU_SUBTYPE_ARM_V7 ) { + if ( _options.preferSubArchitecture() && _options.archSupportsThumb2() ) { // The instruction is really two instructions: // The lower 16 bits are the first instruction, which contains the high // 11 bits of the displacement. @@ -1357,12 +1416,13 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld:: instruction = 0xC000F000; // keep blx } else if ( is_b ) { - if ( !thumbTarget ) - throwf("don't know how to convert instruction %x referencing %s to arm", - instruction, referenceTargetAtomName(state, fit)); instruction = 0x9000F000; // keep b + if ( !thumbTarget && !fit->contentDetlaToAddendOnly ) { + throwf("armv7 has no pc-rel bx thumb instruction. Can't fix up branch to %s in %s", + referenceTargetAtomName(state, fit), atom->name()); + } } - else if ( is_b ) { + else { if ( !thumbTarget ) throwf("don't know how to convert branch instruction %x referencing %s to bx", instruction, referenceTargetAtomName(state, fit)); @@ -1389,10 +1449,13 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld:: else if ( is_blx && thumbTarget ) { instruction = 0xF800F000; } - else if ( !is_bl && !is_blx && !thumbTarget ) { - throwf("don't know how to convert instruction %x referencing %s to arm", - instruction, referenceTargetAtomName(state, fit)); - } + else if ( is_b ) { + instruction = 0x9000F000; // keep b + if ( !thumbTarget && !fit->contentDetlaToAddendOnly ) { + throwf("armv6 has no pc-rel bx thumb instruction. Can't fix up branch to %s in %s", + referenceTargetAtomName(state, fit), atom->name()); + } + } else { instruction = instruction & 0xF800F800; } @@ -1440,38 +1503,27 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld:: set32LE(fixUpLocation, newInstruction); } break; - case ld::Fixup::kindStoreTargetAddressPPCBranch24: - accumulator = addressOf(state, fit, &toTarget); - if ( fit->contentDetlaToAddendOnly ) - accumulator = 0; - // fall into kindStorePPCBranch24 case - case ld::Fixup::kindStorePPCBranch24: - delta = accumulator - (atom->finalAddress() + fit->offsetInAtom); - rangeCheckPPCBranch24(delta, state, atom, fit); - instruction = get32BE(fixUpLocation); - newInstruction = (instruction & 0xFC000003) | ((uint32_t)delta & 0x03FFFFFC); - set32BE(fixUpLocation, newInstruction); - break; } } } -void OutputFile::copyNoOps(uint8_t* from, uint8_t* to) +void OutputFile::copyNoOps(uint8_t* from, uint8_t* to, bool thumb) { switch ( _options.architecture() ) { - case CPU_TYPE_POWERPC: - for (uint8_t* p=from; p < to; p += 4) - OSWriteBigInt32((uint32_t*)p, 0, 0x60000000); - break; case CPU_TYPE_I386: case CPU_TYPE_X86_64: for (uint8_t* p=from; p < to; ++p) *p = 0x90; break; case CPU_TYPE_ARM: - // fixme: need thumb nop? - for (uint8_t* p=from; p < to; p += 4) - OSWriteBigInt32((uint32_t*)p, 0, 0xe1a00000); + if ( thumb ) { + for (uint8_t* p=from; p < to; p += 2) + OSWriteLittleInt16((uint16_t*)p, 0, 0x46c0); + } + else { + for (uint8_t* p=from; p < to; p += 4) + OSWriteLittleInt32((uint32_t*)p, 0, 0xe1a00000); + } break; default: for (uint8_t* p=from; p < to; ++p) @@ -1513,35 +1565,8 @@ bool OutputFile::hasZeroForFileOffset(const ld::Section* sect) return false; } - -void OutputFile::writeOutputFile(ld::Internal& state) +void OutputFile::writeAtoms(ld::Internal& state, uint8_t* wholeBuffer) { - // for UNIX conformance, error if file exists and is not writable - if ( (access(_options.outputFilePath(), F_OK) == 0) && (access(_options.outputFilePath(), W_OK) == -1) ) - throwf("can't write output file: %s", _options.outputFilePath()); - - int permissions = 0777; - if ( _options.outputKind() == Options::kObjectFile ) - permissions = 0666; - // Calling unlink first assures the file is gone so that open creates it with correct permissions - // It also handles the case where __options.outputFilePath() file is not writable but its directory is - // And it means we don't have to truncate the file when done writing (in case new is smaller than old) - // Lastly, only delete existing file if it is a normal file (e.g. not /dev/null). - struct stat stat_buf; - if ( (stat(_options.outputFilePath(), &stat_buf) != -1) && (stat_buf.st_mode & S_IFREG) ) - (void)unlink(_options.outputFilePath()); - - // try to allocate buffer for entire output file content - uint8_t* wholeBuffer = (uint8_t*)calloc(_fileSize, 1); - if ( wholeBuffer == NULL ) - throwf("can't create buffer of %llu bytes for output", _fileSize); - - if ( _options.UUIDMode() == Options::kUUIDRandom ) { - uint8_t bits[16]; - ::uuid_generate_random(bits); - _headersAndLoadCommandAtom->setUUID(bits); - } - // have each atom write itself uint64_t fileOffsetOfEndOfLastAtom = 0; uint64_t mhAddress = 0; @@ -1555,6 +1580,7 @@ void OutputFile::writeOutputFile(ld::Internal& state) const bool sectionUsesNops = (sect->type() == ld::Section::typeCode); //fprintf(stderr, "file offset=0x%08llX, section %s\n", sect->fileOffset, sect->sectionName()); std::vector& atoms = sect->atoms; + bool lastAtomWasThumb = false; for (std::vector::iterator ait = atoms.begin(); ait != atoms.end(); ++ait) { const ld::Atom* atom = *ait; if ( atom->definition() == ld::Atom::definitionProxy ) @@ -1563,7 +1589,7 @@ void OutputFile::writeOutputFile(ld::Internal& state) uint64_t fileOffset = atom->finalAddress() - sect->address + sect->fileOffset; // check for alignment padding between atoms if ( (fileOffset != fileOffsetOfEndOfLastAtom) && lastAtomUsesNoOps ) { - this->copyNoOps(&wholeBuffer[fileOffsetOfEndOfLastAtom], &wholeBuffer[fileOffset]); + this->copyNoOps(&wholeBuffer[fileOffsetOfEndOfLastAtom], &wholeBuffer[fileOffset], lastAtomWasThumb); } // copy atom content atom->copyRawContent(&wholeBuffer[fileOffset]); @@ -1571,6 +1597,7 @@ void OutputFile::writeOutputFile(ld::Internal& state) this->applyFixUps(state, mhAddress, atom, &wholeBuffer[fileOffset]); fileOffsetOfEndOfLastAtom = fileOffset+atom->size(); lastAtomUsesNoOps = sectionUsesNops; + lastAtomWasThumb = atom->isThumb(); } catch (const char* msg) { if ( atom->file() != NULL ) @@ -1580,74 +1607,153 @@ void OutputFile::writeOutputFile(ld::Internal& state) } } } - - // compute UUID - if ( _options.UUIDMode() == Options::kUUIDContent ) { - const bool log = false; - if ( (_options.outputKind() != Options::kObjectFile) || state.someObjectFileHasDwarf ) { - uint8_t digest[CC_MD5_DIGEST_LENGTH]; - uint32_t stabsStringsOffsetStart; - uint32_t tabsStringsOffsetEnd; - uint32_t stabsOffsetStart; - uint32_t stabsOffsetEnd; - if ( _symbolTableAtom->hasStabs(stabsStringsOffsetStart, tabsStringsOffsetEnd, stabsOffsetStart, stabsOffsetEnd) ) { - // find two areas of file that are stabs info and should not contribute to checksum - uint64_t stringPoolFileOffset = 0; - uint64_t symbolTableFileOffset = 0; - for (std::vector::iterator sit = state.sections.begin(); sit != state.sections.end(); ++sit) { - ld::Internal::FinalSection* sect = *sit; - if ( sect->type() == ld::Section::typeLinkEdit ) { - if ( strcmp(sect->sectionName(), "__string_pool") == 0 ) - stringPoolFileOffset = sect->fileOffset; - else if ( strcmp(sect->sectionName(), "__symbol_table") == 0 ) - symbolTableFileOffset = sect->fileOffset; - } +} + + +void OutputFile::computeContentUUID(ld::Internal& state, uint8_t* wholeBuffer) +{ + const bool log = false; + if ( (_options.outputKind() != Options::kObjectFile) || state.someObjectFileHasDwarf ) { + uint8_t digest[CC_MD5_DIGEST_LENGTH]; + uint32_t stabsStringsOffsetStart; + uint32_t tabsStringsOffsetEnd; + uint32_t stabsOffsetStart; + uint32_t stabsOffsetEnd; + if ( _symbolTableAtom->hasStabs(stabsStringsOffsetStart, tabsStringsOffsetEnd, stabsOffsetStart, stabsOffsetEnd) ) { + // find two areas of file that are stabs info and should not contribute to checksum + uint64_t stringPoolFileOffset = 0; + uint64_t symbolTableFileOffset = 0; + for (std::vector::iterator sit = state.sections.begin(); sit != state.sections.end(); ++sit) { + ld::Internal::FinalSection* sect = *sit; + if ( sect->type() == ld::Section::typeLinkEdit ) { + if ( strcmp(sect->sectionName(), "__string_pool") == 0 ) + stringPoolFileOffset = sect->fileOffset; + else if ( strcmp(sect->sectionName(), "__symbol_table") == 0 ) + symbolTableFileOffset = sect->fileOffset; } - uint64_t firstStabNlistFileOffset = symbolTableFileOffset + stabsOffsetStart; - uint64_t lastStabNlistFileOffset = symbolTableFileOffset + stabsOffsetEnd; - uint64_t firstStabStringFileOffset = stringPoolFileOffset + stabsStringsOffsetStart; - uint64_t lastStabStringFileOffset = stringPoolFileOffset + tabsStringsOffsetEnd; - if ( log ) fprintf(stderr, "firstStabNlistFileOffset=0x%08llX\n", firstStabNlistFileOffset); - if ( log ) fprintf(stderr, "lastStabNlistFileOffset=0x%08llX\n", lastStabNlistFileOffset); - if ( log ) fprintf(stderr, "firstStabStringFileOffset=0x%08llX\n", firstStabStringFileOffset); - if ( log ) fprintf(stderr, "lastStabStringFileOffset=0x%08llX\n", lastStabStringFileOffset); - assert(firstStabNlistFileOffset <= firstStabStringFileOffset); - - CC_MD5_CTX md5state; - CC_MD5_Init(&md5state); - // checksum everything up to first stabs nlist - if ( log ) fprintf(stderr, "checksum 0x%08X -> 0x%08llX\n", 0, firstStabNlistFileOffset); - CC_MD5_Update(&md5state, &wholeBuffer[0], firstStabNlistFileOffset); - // checkusm everything after last stabs nlist and up to first stabs string - if ( log ) fprintf(stderr, "checksum 0x%08llX -> 0x%08llX\n", lastStabNlistFileOffset, firstStabStringFileOffset); - CC_MD5_Update(&md5state, &wholeBuffer[lastStabNlistFileOffset], firstStabStringFileOffset-lastStabNlistFileOffset); - // checksum everything after last stabs string to end of file - if ( log ) fprintf(stderr, "checksum 0x%08llX -> 0x%08llX\n", lastStabStringFileOffset, _fileSize); - CC_MD5_Update(&md5state, &wholeBuffer[lastStabStringFileOffset], _fileSize-lastStabStringFileOffset); - CC_MD5_Final(digest, &md5state); - if ( log ) fprintf(stderr, "uuid=%02X, %02X, %02X, %02X, %02X, %02X, %02X, %02X\n", digest[0], digest[1], digest[2], - digest[3], digest[4], digest[5], digest[6], digest[7]); } - else { - CC_MD5(wholeBuffer, _fileSize, digest); - } - // LC_UUID uuids should conform to RFC 4122 UUID version 4 & UUID version 5 formats - digest[6] = ( digest[6] & 0x0F ) | ( 3 << 4 ); - digest[8] = ( digest[8] & 0x3F ) | 0x80; - // update buffer with new UUID - _headersAndLoadCommandAtom->setUUID(digest); - _headersAndLoadCommandAtom->recopyUUIDCommand(); + uint64_t firstStabNlistFileOffset = symbolTableFileOffset + stabsOffsetStart; + uint64_t lastStabNlistFileOffset = symbolTableFileOffset + stabsOffsetEnd; + uint64_t firstStabStringFileOffset = stringPoolFileOffset + stabsStringsOffsetStart; + uint64_t lastStabStringFileOffset = stringPoolFileOffset + tabsStringsOffsetEnd; + if ( log ) fprintf(stderr, "firstStabNlistFileOffset=0x%08llX\n", firstStabNlistFileOffset); + if ( log ) fprintf(stderr, "lastStabNlistFileOffset=0x%08llX\n", lastStabNlistFileOffset); + if ( log ) fprintf(stderr, "firstStabStringFileOffset=0x%08llX\n", firstStabStringFileOffset); + if ( log ) fprintf(stderr, "lastStabStringFileOffset=0x%08llX\n", lastStabStringFileOffset); + assert(firstStabNlistFileOffset <= firstStabStringFileOffset); + + CC_MD5_CTX md5state; + CC_MD5_Init(&md5state); + // checksum everything up to first stabs nlist + if ( log ) fprintf(stderr, "checksum 0x%08X -> 0x%08llX\n", 0, firstStabNlistFileOffset); + CC_MD5_Update(&md5state, &wholeBuffer[0], firstStabNlistFileOffset); + // checkusm everything after last stabs nlist and up to first stabs string + if ( log ) fprintf(stderr, "checksum 0x%08llX -> 0x%08llX\n", lastStabNlistFileOffset, firstStabStringFileOffset); + CC_MD5_Update(&md5state, &wholeBuffer[lastStabNlistFileOffset], firstStabStringFileOffset-lastStabNlistFileOffset); + // checksum everything after last stabs string to end of file + if ( log ) fprintf(stderr, "checksum 0x%08llX -> 0x%08llX\n", lastStabStringFileOffset, _fileSize); + CC_MD5_Update(&md5state, &wholeBuffer[lastStabStringFileOffset], _fileSize-lastStabStringFileOffset); + CC_MD5_Final(digest, &md5state); + if ( log ) fprintf(stderr, "uuid=%02X, %02X, %02X, %02X, %02X, %02X, %02X, %02X\n", digest[0], digest[1], digest[2], + digest[3], digest[4], digest[5], digest[6], digest[7]); + } + else { + CC_MD5(wholeBuffer, _fileSize, digest); + } + // LC_UUID uuids should conform to RFC 4122 UUID version 4 & UUID version 5 formats + digest[6] = ( digest[6] & 0x0F ) | ( 3 << 4 ); + digest[8] = ( digest[8] & 0x3F ) | 0x80; + // update buffer with new UUID + _headersAndLoadCommandAtom->setUUID(digest); + _headersAndLoadCommandAtom->recopyUUIDCommand(); + } +} + + +void OutputFile::writeOutputFile(ld::Internal& state) +{ + // for UNIX conformance, error if file exists and is not writable + if ( (access(_options.outputFilePath(), F_OK) == 0) && (access(_options.outputFilePath(), W_OK) == -1) ) + throwf("can't write output file: %s", _options.outputFilePath()); + + mode_t permissions = 0777; + if ( _options.outputKind() == Options::kObjectFile ) + permissions = 0666; + mode_t umask = ::umask(0); + ::umask(umask); // put back the original umask + permissions &= ~umask; + // Calling unlink first assures the file is gone so that open creates it with correct permissions + // It also handles the case where __options.outputFilePath() file is not writable but its directory is + // And it means we don't have to truncate the file when done writing (in case new is smaller than old) + // Lastly, only delete existing file if it is a normal file (e.g. not /dev/null). + struct stat stat_buf; + bool outputIsRegularFile = true; + if ( stat(_options.outputFilePath(), &stat_buf) != -1 ) { + if (stat_buf.st_mode & S_IFREG) { + (void)unlink(_options.outputFilePath()); + } else { + outputIsRegularFile = false; + } + } + + int fd; + // Construct a temporary path of the form {outputFilePath}.ld_XXXXXX + const char filenameTemplate[] = ".ld_XXXXXX"; + char tmpOutput[PATH_MAX]; + uint8_t *wholeBuffer; + if (outputIsRegularFile) { + strcpy(tmpOutput, _options.outputFilePath()); + // If the path is too long to add a suffix for a temporary name then + // just fall back to using the output path. + if (strlen(tmpOutput)+strlen(filenameTemplate) < PATH_MAX) { + strcat(tmpOutput, filenameTemplate); + fd = mkstemp(tmpOutput); + } else { + fd = open(tmpOutput, O_RDWR|O_CREAT, permissions); } + if ( fd == -1 ) + throwf("can't open output file for writing: %s, errno=%d", tmpOutput, errno); + ftruncate(fd, _fileSize); + + wholeBuffer = (uint8_t *)mmap(NULL, _fileSize, PROT_WRITE|PROT_READ, MAP_SHARED, fd, 0); + if ( wholeBuffer == MAP_FAILED ) + throwf("can't create buffer of %llu bytes for output", _fileSize); + } else { + fd = open(_options.outputFilePath(), O_WRONLY); + if ( fd == -1 ) + throwf("can't open output file for writing: %s, errno=%d", _options.outputFilePath(), errno); + // try to allocate buffer for entire output file content + wholeBuffer = (uint8_t*)calloc(_fileSize, 1); + if ( wholeBuffer == NULL ) + throwf("can't create buffer of %llu bytes for output", _fileSize); + } + + if ( _options.UUIDMode() == Options::kUUIDRandom ) { + uint8_t bits[16]; + ::uuid_generate_random(bits); + _headersAndLoadCommandAtom->setUUID(bits); } - // write whole output file in one chunk - int fd = open(_options.outputFilePath(), O_CREAT | O_WRONLY | O_TRUNC, permissions); - if ( fd == -1 ) - throwf("can't open output file for writing: %s, errno=%d", _options.outputFilePath(), errno); - if ( ::pwrite(fd, wholeBuffer, _fileSize, 0) == -1 ) - throwf("can't write to output file: %s, errno=%d", _options.outputFilePath(), errno); - close(fd); - free(wholeBuffer); + writeAtoms(state, wholeBuffer); + + // compute UUID + if ( _options.UUIDMode() == Options::kUUIDContent ) + computeContentUUID(state, wholeBuffer); + + if (outputIsRegularFile) { + if ( ::chmod(tmpOutput, permissions) == -1 ) { + unlink(tmpOutput); + throwf("can't set permissions on output file: %s, errno=%d", tmpOutput, errno); + } + if ( ::rename(tmpOutput, _options.outputFilePath()) == -1 && strcmp(tmpOutput, _options.outputFilePath()) != 0) { + unlink(tmpOutput); + throwf("can't move output file in place, errno=%d", errno); + } + } else { + if ( ::write(fd, wholeBuffer, _fileSize) == -1 ) { + throwf("can't write to output file: %s, errno=%d", _options.outputFilePath(), errno); + } + } } struct AtomByNameSorter @@ -1658,6 +1764,19 @@ struct AtomByNameSorter } }; +class NotInSet +{ +public: + NotInSet(const std::set& theSet) : _set(theSet) {} + + bool operator()(const ld::Atom* atom) const { + return ( _set.count(atom) == 0 ); + } +private: + const std::set& _set; +}; + + void OutputFile::buildSymbolTable(ld::Internal& state) { unsigned int machoSectionIndex = 0; @@ -1747,27 +1866,6 @@ void OutputFile::buildSymbolTable(ld::Internal& state) continue; } - // Add command line options to control symbol weak-def bit on exported symbols - if ( _options.hasWeakBitTweaks() && (atom->definition() == ld::Atom::definitionRegular) ) { - const char* name = atom->name(); - if ( atom->scope() == ld::Atom::scopeGlobal ) { - if ( atom->combine() == ld::Atom::combineNever ) { - if ( _options.forceWeak(name) ) - (const_cast(atom))->setCombine(ld::Atom::combineByName); - } - else if ( atom->combine() == ld::Atom::combineByName ) { - if ( _options.forceNotWeak(name) ) - (const_cast(atom))->setCombine(ld::Atom::combineNever); - } - } - else { - if ( _options.forceWeakNonWildCard(name) ) - warning("cannot force to be weak, non-external symbol %s", name); - else if ( _options.forceNotWeakNonWildcard(name) ) - warning("cannot force to be not-weak, non-external symbol %s", name); - } - } - switch ( atom->scope() ) { case ld::Atom::scopeTranslationUnit: if ( _options.keepLocalSymbol(atom->name()) ) { @@ -1800,7 +1898,11 @@ void OutputFile::buildSymbolTable(ld::Internal& state) } } else { - if ( _options.keepLocalSymbol(atom->name()) ) + if ( _options.keepLocalSymbol(atom->name()) ) + _localAtoms.push_back(atom); + // ld should never have a symbol in the non-lazy indirect symbol table with index 0 + // this works by making __mh_execute_header be a local symbol which takes symbol index 0 + else if ( (atom->symbolTableInclusion() == ld::Atom::symbolTableInAndNeverStrip) && !_options.makeCompressedDyldInfo() ) _localAtoms.push_back(atom); else (const_cast(atom))->setSymbolTableInclusion(ld::Atom::symbolTableNotIn); @@ -1810,15 +1912,41 @@ void OutputFile::buildSymbolTable(ld::Internal& state) } } + // ld adds undefined symbol from .exp file to binary + if ( (_options.outputKind() == Options::kKextBundle) && _options.hasExportRestrictList() ) { + // search for referenced undefines + std::set referencedProxyAtoms; + for (std::vector::iterator sit=state.sections.begin(); sit != state.sections.end(); ++sit) { + ld::Internal::FinalSection* sect = *sit; + for (std::vector::iterator ait=sect->atoms.begin(); ait != sect->atoms.end(); ++ait) { + const ld::Atom* atom = *ait; + for (ld::Fixup::iterator fit = atom->fixupsBegin(), end=atom->fixupsEnd(); fit != end; ++fit) { + switch ( fit->binding ) { + case ld::Fixup::bindingsIndirectlyBound: + referencedProxyAtoms.insert(state.indirectBindingTable[fit->u.bindingIndex]); + break; + case ld::Fixup::bindingDirectlyBound: + referencedProxyAtoms.insert(fit->u.target); + break; + default: + break; + } + } + } + } + // remove any unreferenced _importedAtoms + _importedAtoms.erase(std::remove_if(_importedAtoms.begin(), _importedAtoms.end(), NotInSet(referencedProxyAtoms)), _importedAtoms.end()); + } + // sort by name std::sort(_exportedAtoms.begin(), _exportedAtoms.end(), AtomByNameSorter()); std::sort(_importedAtoms.begin(), _importedAtoms.end(), AtomByNameSorter()); - } void OutputFile::addPreloadLinkEdit(ld::Internal& state) { switch ( _options.architecture() ) { +#if SUPPORT_ARCH_i386 case CPU_TYPE_I386: if ( _hasLocalRelocations ) { _localRelocsAtom = new LocalRelocationsAtom(_options, state, *this); @@ -1837,6 +1965,8 @@ void OutputFile::addPreloadLinkEdit(ld::Internal& state) stringPoolSection = state.addAtom(*_stringPoolAtom); } break; +#endif +#if SUPPORT_ARCH_x86_64 case CPU_TYPE_X86_64: if ( _hasLocalRelocations ) { _localRelocsAtom = new LocalRelocationsAtom(_options, state, *this); @@ -1855,6 +1985,8 @@ void OutputFile::addPreloadLinkEdit(ld::Internal& state) stringPoolSection = state.addAtom(*_stringPoolAtom); } break; +#endif +#if SUPPORT_ARCH_arm_any case CPU_TYPE_ARM: if ( _hasLocalRelocations ) { _localRelocsAtom = new LocalRelocationsAtom(_options, state, *this); @@ -1873,6 +2005,7 @@ void OutputFile::addPreloadLinkEdit(ld::Internal& state) stringPoolSection = state.addAtom(*_stringPoolAtom); } break; +#endif default: throw "architecture not supported for -preload"; } @@ -1887,54 +2020,7 @@ void OutputFile::addLinkEdit(ld::Internal& state) return addPreloadLinkEdit(state); switch ( _options.architecture() ) { - case CPU_TYPE_POWERPC: - if ( _hasSectionRelocations ) { - _sectionsRelocationsAtom = new SectionRelocationsAtom(_options, state, *this); - sectionRelocationsSection = state.addAtom(*_sectionsRelocationsAtom); - } - if ( _hasDyldInfo ) { - _rebasingInfoAtom = new RebaseInfoAtom(_options, state, *this); - rebaseSection = state.addAtom(*_rebasingInfoAtom); - - _bindingInfoAtom = new BindingInfoAtom(_options, state, *this); - bindingSection = state.addAtom(*_bindingInfoAtom); - - _weakBindingInfoAtom = new WeakBindingInfoAtom(_options, state, *this); - weakBindingSection = state.addAtom(*_weakBindingInfoAtom); - - _lazyBindingInfoAtom = new LazyBindingInfoAtom(_options, state, *this); - lazyBindingSection = state.addAtom(*_lazyBindingInfoAtom); - - _exportInfoAtom = new ExportInfoAtom(_options, state, *this); - exportSection = state.addAtom(*_exportInfoAtom); - } - if ( _hasLocalRelocations ) { - _localRelocsAtom = new LocalRelocationsAtom(_options, state, *this); - localRelocationsSection = state.addAtom(*_localRelocsAtom); - } - if ( _hasSplitSegInfo ) { - _splitSegInfoAtom = new SplitSegInfoAtom(_options, state, *this); - splitSegInfoSection = state.addAtom(*_splitSegInfoAtom); - } - if ( _hasFunctionStartsInfo ) { - _functionStartsAtom = new FunctionStartsAtom(_options, state, *this); - functionStartsSection = state.addAtom(*_functionStartsAtom); - } - if ( _hasSymbolTable ) { - _symbolTableAtom = new SymbolTableAtom(_options, state, *this); - symbolTableSection = state.addAtom(*_symbolTableAtom); - } - if ( _hasExternalRelocations ) { - _externalRelocsAtom = new ExternalRelocationsAtom(_options, state, *this); - externalRelocationsSection = state.addAtom(*_externalRelocsAtom); - } - if ( _hasSymbolTable ) { - _indirectSymbolTableAtom = new IndirectSymbolTableAtom(_options, state, *this); - indirectSymbolTableSection = state.addAtom(*_indirectSymbolTableAtom); - _stringPoolAtom = new StringPoolAtom(_options, state, *this, 4); - stringPoolSection = state.addAtom(*_stringPoolAtom); - } - break; +#if SUPPORT_ARCH_i386 case CPU_TYPE_I386: if ( _hasSectionRelocations ) { _sectionsRelocationsAtom = new SectionRelocationsAtom(_options, state, *this); @@ -1968,6 +2054,14 @@ void OutputFile::addLinkEdit(ld::Internal& state) _functionStartsAtom = new FunctionStartsAtom(_options, state, *this); functionStartsSection = state.addAtom(*_functionStartsAtom); } + if ( _hasDataInCodeInfo ) { + _dataInCodeAtom = new DataInCodeAtom(_options, state, *this); + dataInCodeSection = state.addAtom(*_dataInCodeAtom); + } + if ( _hasDependentDRInfo ) { + _dependentDRInfoAtom = new DependentDRAtom(_options, state, *this); + dependentDRsSection = state.addAtom(*_dependentDRInfoAtom); + } if ( _hasSymbolTable ) { _symbolTableAtom = new SymbolTableAtom(_options, state, *this); symbolTableSection = state.addAtom(*_symbolTableAtom); @@ -1983,6 +2077,8 @@ void OutputFile::addLinkEdit(ld::Internal& state) stringPoolSection = state.addAtom(*_stringPoolAtom); } break; +#endif +#if SUPPORT_ARCH_x86_64 case CPU_TYPE_X86_64: if ( _hasSectionRelocations ) { _sectionsRelocationsAtom = new SectionRelocationsAtom(_options, state, *this); @@ -2016,6 +2112,14 @@ void OutputFile::addLinkEdit(ld::Internal& state) _functionStartsAtom = new FunctionStartsAtom(_options, state, *this); functionStartsSection = state.addAtom(*_functionStartsAtom); } + if ( _hasDataInCodeInfo ) { + _dataInCodeAtom = new DataInCodeAtom(_options, state, *this); + dataInCodeSection = state.addAtom(*_dataInCodeAtom); + } + if ( _hasDependentDRInfo ) { + _dependentDRInfoAtom = new DependentDRAtom(_options, state, *this); + dependentDRsSection = state.addAtom(*_dependentDRInfoAtom); + } if ( _hasSymbolTable ) { _symbolTableAtom = new SymbolTableAtom(_options, state, *this); symbolTableSection = state.addAtom(*_symbolTableAtom); @@ -2031,6 +2135,8 @@ void OutputFile::addLinkEdit(ld::Internal& state) stringPoolSection = state.addAtom(*_stringPoolAtom); } break; +#endif +#if SUPPORT_ARCH_arm_any case CPU_TYPE_ARM: if ( _hasSectionRelocations ) { _sectionsRelocationsAtom = new SectionRelocationsAtom(_options, state, *this); @@ -2064,6 +2170,14 @@ void OutputFile::addLinkEdit(ld::Internal& state) _functionStartsAtom = new FunctionStartsAtom(_options, state, *this); functionStartsSection = state.addAtom(*_functionStartsAtom); } + if ( _hasDataInCodeInfo ) { + _dataInCodeAtom = new DataInCodeAtom(_options, state, *this); + dataInCodeSection = state.addAtom(*_dataInCodeAtom); + } + if ( _hasDependentDRInfo ) { + _dependentDRInfoAtom = new DependentDRAtom(_options, state, *this); + dependentDRsSection = state.addAtom(*_dependentDRInfoAtom); + } if ( _hasSymbolTable ) { _symbolTableAtom = new SymbolTableAtom(_options, state, *this); symbolTableSection = state.addAtom(*_symbolTableAtom); @@ -2079,54 +2193,7 @@ void OutputFile::addLinkEdit(ld::Internal& state) stringPoolSection = state.addAtom(*_stringPoolAtom); } break; - case CPU_TYPE_POWERPC64: - if ( _hasSectionRelocations ) { - _sectionsRelocationsAtom = new SectionRelocationsAtom(_options, state, *this); - sectionRelocationsSection = state.addAtom(*_sectionsRelocationsAtom); - } - if ( _hasDyldInfo ) { - _rebasingInfoAtom = new RebaseInfoAtom(_options, state, *this); - rebaseSection = state.addAtom(*_rebasingInfoAtom); - - _bindingInfoAtom = new BindingInfoAtom(_options, state, *this); - bindingSection = state.addAtom(*_bindingInfoAtom); - - _weakBindingInfoAtom = new WeakBindingInfoAtom(_options, state, *this); - weakBindingSection = state.addAtom(*_weakBindingInfoAtom); - - _lazyBindingInfoAtom = new LazyBindingInfoAtom(_options, state, *this); - lazyBindingSection = state.addAtom(*_lazyBindingInfoAtom); - - _exportInfoAtom = new ExportInfoAtom(_options, state, *this); - exportSection = state.addAtom(*_exportInfoAtom); - } - if ( _hasLocalRelocations ) { - _localRelocsAtom = new LocalRelocationsAtom(_options, state, *this); - localRelocationsSection = state.addAtom(*_localRelocsAtom); - } - if ( _hasSplitSegInfo ) { - _splitSegInfoAtom = new SplitSegInfoAtom(_options, state, *this); - splitSegInfoSection = state.addAtom(*_splitSegInfoAtom); - } - if ( _hasFunctionStartsInfo ) { - _functionStartsAtom = new FunctionStartsAtom(_options, state, *this); - functionStartsSection = state.addAtom(*_functionStartsAtom); - } - if ( _hasSymbolTable ) { - _symbolTableAtom = new SymbolTableAtom(_options, state, *this); - symbolTableSection = state.addAtom(*_symbolTableAtom); - } - if ( _hasExternalRelocations ) { - _externalRelocsAtom = new ExternalRelocationsAtom(_options, state, *this); - externalRelocationsSection = state.addAtom(*_externalRelocsAtom); - } - if ( _hasSymbolTable ) { - _indirectSymbolTableAtom = new IndirectSymbolTableAtom(_options, state, *this); - indirectSymbolTableSection = state.addAtom(*_indirectSymbolTableAtom); - _stringPoolAtom = new StringPoolAtom(_options, state, *this, 4); - stringPoolSection = state.addAtom(*_stringPoolAtom); - } - break; +#endif default: throw "unknown architecture"; } @@ -2135,26 +2202,24 @@ void OutputFile::addLinkEdit(ld::Internal& state) void OutputFile::addLoadCommands(ld::Internal& state) { switch ( _options.architecture() ) { +#if SUPPORT_ARCH_x86_64 case CPU_TYPE_X86_64: _headersAndLoadCommandAtom = new HeaderAndLoadCommandsAtom(_options, state, *this); headerAndLoadCommandsSection = state.addAtom(*_headersAndLoadCommandAtom); break; +#endif +#if SUPPORT_ARCH_arm_any case CPU_TYPE_ARM: _headersAndLoadCommandAtom = new HeaderAndLoadCommandsAtom(_options, state, *this); headerAndLoadCommandsSection = state.addAtom(*_headersAndLoadCommandAtom); break; +#endif +#if SUPPORT_ARCH_i386 case CPU_TYPE_I386: _headersAndLoadCommandAtom = new HeaderAndLoadCommandsAtom(_options, state, *this); headerAndLoadCommandsSection = state.addAtom(*_headersAndLoadCommandAtom); break; - case CPU_TYPE_POWERPC: - _headersAndLoadCommandAtom = new HeaderAndLoadCommandsAtom(_options, state, *this); - headerAndLoadCommandsSection = state.addAtom(*_headersAndLoadCommandAtom); - break; - case CPU_TYPE_POWERPC64: - _headersAndLoadCommandAtom = new HeaderAndLoadCommandsAtom(_options, state, *this); - headerAndLoadCommandsSection = state.addAtom(*_headersAndLoadCommandAtom); - break; +#endif default: throw "unknown architecture"; } @@ -2310,18 +2375,12 @@ bool OutputFile::isPcRelStore(ld::Fixup::Kind kind) case ld::Fixup::kindStoreARMBranch24: case ld::Fixup::kindStoreThumbBranch22: case ld::Fixup::kindStoreARMLoad12: - case ld::Fixup::kindStorePPCBranch24: - case ld::Fixup::kindStorePPCBranch14: - case ld::Fixup::kindStorePPCPicLow14: - case ld::Fixup::kindStorePPCPicLow16: - case ld::Fixup::kindStorePPCPicHigh16AddLow: case ld::Fixup::kindStoreTargetAddressX86PCRel32: case ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoad: case ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoadNowLEA: case ld::Fixup::kindStoreTargetAddressARMBranch24: case ld::Fixup::kindStoreTargetAddressThumbBranch22: case ld::Fixup::kindStoreTargetAddressARMLoad12: - case ld::Fixup::kindStoreTargetAddressPPCBranch24: return true; case ld::Fixup::kindStoreTargetAddressX86BranchPCRel32: return (_options.outputKind() != Options::kKextBundle); @@ -2367,10 +2426,11 @@ bool OutputFile::setsTarget(ld::Fixup::Kind kind) case ld::Fixup::kindStoreTargetAddressX86BranchPCRel32: case ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoad: case ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoadNowLEA: + case ld::Fixup::kindStoreTargetAddressX86PCRel32TLVLoad: + case ld::Fixup::kindStoreTargetAddressX86Abs32TLVLoad: case ld::Fixup::kindStoreTargetAddressARMBranch24: case ld::Fixup::kindStoreTargetAddressThumbBranch22: case ld::Fixup::kindStoreTargetAddressARMLoad12: - case ld::Fixup::kindStoreTargetAddressPPCBranch24: return true; case ld::Fixup::kindStoreX86DtraceCallSiteNop: case ld::Fixup::kindStoreX86DtraceIsEnableSiteClear: @@ -2378,8 +2438,6 @@ bool OutputFile::setsTarget(ld::Fixup::Kind kind) case ld::Fixup::kindStoreARMDtraceIsEnableSiteClear: case ld::Fixup::kindStoreThumbDtraceCallSiteNop: case ld::Fixup::kindStoreThumbDtraceIsEnableSiteClear: - case ld::Fixup::kindStorePPCDtraceCallSiteNop: - case ld::Fixup::kindStorePPCDtraceIsEnableSiteClear: return (_options.outputKind() == Options::kObjectFile); default: break; @@ -2538,7 +2596,15 @@ void OutputFile::generateLinkEditInfo(ld::Internal& state) } assert(minusTarget != NULL); break; - default: + case ld::Fixup::kindDataInCodeStartData: + case ld::Fixup::kindDataInCodeStartJT8: + case ld::Fixup::kindDataInCodeStartJT16: + case ld::Fixup::kindDataInCodeStartJT32: + case ld::Fixup::kindDataInCodeStartJTA32: + case ld::Fixup::kindDataInCodeEnd: + hasDataInCode = true; + break; + default: break; } if ( this->isStore(fit->kind) ) { @@ -2583,7 +2649,8 @@ void OutputFile::noteTextReloc(const ld::Atom* atom, const ld::Atom* target) if ( _options.warnAboutTextRelocs() ) warning("text reloc in %s to %s", atom->name(), target->name()); } - else if ( _options.positionIndependentExecutable() && (_options.iphoneOSVersionMin() >= ld::iPhone4_3) ) { + else if ( _options.positionIndependentExecutable() && (_options.outputKind() == Options::kDynamicExecutable) + && ((_options.iOSVersionMin() >= ld::iOS_4_3) || (_options.macosxVersionMin() >= ld::mac10_7)) ) { if ( ! this->pieDisabled ) { warning("PIE disabled. Absolute addressing (perhaps -mdynamic-no-pic) not allowed in code signed PIE, " "but used in %s from %s. " @@ -2592,8 +2659,11 @@ void OutputFile::noteTextReloc(const ld::Atom* atom, const ld::Atom* target) } this->pieDisabled = true; } + else if ( (target->scope() == ld::Atom::scopeGlobal) && (target->combine() == ld::Atom::combineByName) ) { + throwf("illegal text-relocoation (direct reference) to (global,weak) %s in %s from %s in %s", target->name(), target->file()->path(), atom->name(), atom->file()->path()); + } else { - throwf("illegal text reloc to %s from %s in %s", target->name(), target->file()->path(), atom->name()); + throwf("illegal text-relocation to %s in %s from %s in %s", target->name(), target->file()->path(), atom->name(), atom->file()->path()); } } @@ -2608,8 +2678,23 @@ void OutputFile::addDyldInfo(ld::Internal& state, ld::Internal::FinalSection* s // no need to rebase or bind PCRel stores if ( this->isPcRelStore(fixupWithStore->kind) ) { // as long as target is in same linkage unit - if ( (target == NULL) || (target->definition() != ld::Atom::definitionProxy) ) + if ( (target == NULL) || (target->definition() != ld::Atom::definitionProxy) ) { + // make sure target is not global and weak + if ( (target->scope() == ld::Atom::scopeGlobal) && (target->combine() == ld::Atom::combineByName) && (target->definition() == ld::Atom::definitionRegular)) { + if ( (atom->section().type() == ld::Section::typeCFI) + || (atom->section().type() == ld::Section::typeDtraceDOF) + || (atom->section().type() == ld::Section::typeUnwindInfo) ) { + // ok for __eh_frame and __uwind_info to use pointer diffs to global weak symbols + return; + } + // Have direct reference to weak-global. This should be an indrect reference + const char* demangledName = strdup(_options.demangleSymbol(atom->name())); + warning("direct access in %s to global weak symbol %s means the weak symbol cannot be overridden at runtime. " + "This was likely caused by different translation units being compiled with different visibility settings.", + demangledName, _options.demangleSymbol(target->name())); + } return; + } } // no need to rebase or bind PIC internal pointer diff @@ -2623,13 +2708,19 @@ void OutputFile::addDyldInfo(ld::Internal& state, ld::Internal::FinalSection* s return; } - // make sure target is not global and weak - if ( (target->scope() == ld::Atom::scopeGlobal) && (target->combine() == ld::Atom::combineByName) - && (atom->section().type() != ld::Section::typeCFI) - && (atom->section().type() != ld::Section::typeDtraceDOF) - && (atom->section().type() != ld::Section::typeUnwindInfo) ) { - // ok for __eh_frame and __uwind_info to use pointer diffs to global weak symbols - throwf("bad codegen, pointer diff in %s to global weak symbol %s", atom->name(), target->name()); + // check if target of pointer-diff is global and weak + if ( (target->scope() == ld::Atom::scopeGlobal) && (target->combine() == ld::Atom::combineByName) && (target->definition() == ld::Atom::definitionRegular) ) { + if ( (atom->section().type() == ld::Section::typeCFI) + || (atom->section().type() == ld::Section::typeDtraceDOF) + || (atom->section().type() == ld::Section::typeUnwindInfo) ) { + // ok for __eh_frame and __uwind_info to use pointer diffs to global weak symbols + return; + } + // Have direct reference to weak-global. This should be an indrect reference + const char* demangledName = strdup(_options.demangleSymbol(atom->name())); + warning("direct access in %s to global weak symbol %s means the weak symbol cannot be overridden at runtime. " + "This was likely caused by different translation units being compiled with different visibility settings.", + demangledName, _options.demangleSymbol(target->name())); } return; } @@ -2651,7 +2742,7 @@ void OutputFile::addDyldInfo(ld::Internal& state, ld::Internal::FinalSection* s uint8_t rebaseType = REBASE_TYPE_POINTER; uint8_t type = BIND_TYPE_POINTER; const ld::dylib::File* dylib = dynamic_cast(target->file()); - bool weak_import = ((dylib != NULL) && (fixupWithTarget->weakImport || dylib->willBeWeakLinked())); + bool weak_import = (fixupWithTarget->weakImport || ((dylib != NULL) && dylib->forcedWeakLinked())); uint64_t address = atom->finalAddress() + fixupWithTarget->offsetInAtom; uint64_t addend = targetAddend - minusTargetAddend; @@ -2747,6 +2838,22 @@ void OutputFile::addDyldInfo(ld::Internal& state, ld::Internal::FinalSection* s sect->hasLocalRelocs = true; // so dyld knows to change permissions on __TEXT segment rebaseType = REBASE_TYPE_TEXT_ABSOLUTE32; } + if ( (addend != 0) && _options.sharedRegionEligible() ) { + // make sure the addend does not cause the pointer to point outside the target's segment + // if it does, update_dyld_shared_cache will not be able to put this dylib into the shared cache + uint64_t targetAddress = target->finalAddress(); + for (std::vector::iterator sit = state.sections.begin(); sit != state.sections.end(); ++sit) { + ld::Internal::FinalSection* sct = *sit; + uint64_t sctEnd = (sct->address+sct->size); + if ( (sct->address <= targetAddress) && (targetAddress < sctEnd) ) { + if ( (targetAddress+addend) > sctEnd ) { + warning("data symbol %s from %s has pointer to %s + 0x%08llX. " + "That large of an addend may disable %s from being put in the dyld shared cache.", + atom->name(), atom->file()->path(), target->name(), addend, _options.installPath() ); + } + } + } + } _rebaseInfo.push_back(RebaseInfo(rebaseType, address)); } if ( needsBinding ) { @@ -2764,10 +2871,6 @@ void OutputFile::addDyldInfo(ld::Internal& state, ld::Internal::FinalSection* s } if ( needsWeakBinding ) _weakBindingInfo.push_back(BindingInfo(type, 0, target->name(), false, address, addend)); - - // record if weak imported - if ( weak_import && (target->definition() == ld::Atom::definitionProxy) ) - (const_cast(target))->setWeakImported(); } @@ -2780,14 +2883,20 @@ void OutputFile::addClassicRelocs(ld::Internal& state, ld::Internal::FinalSectio return; // non-lazy-pointer section is encoded in indirect symbol table - not using relocations - if ( (sect->type() == ld::Section::typeNonLazyPointer) && (_options.outputKind() != Options::kKextBundle) ) { - assert(target != NULL); - assert(fixupWithTarget != NULL); - // record if weak imported - const ld::dylib::File* dylib = dynamic_cast(target->file()); - if ( (dylib != NULL) && (fixupWithTarget->weakImport || dylib->willBeWeakLinked()) ) - (const_cast(target))->setWeakImported(); - return; + if ( sect->type() == ld::Section::typeNonLazyPointer ) { + // except kexts and static pie which *do* use relocations + switch (_options.outputKind()) { + case Options::kKextBundle: + break; + case Options::kStaticExecutable: + if ( _options.positionIndependentExecutable() ) + break; + // else fall into default case + default: + assert(target != NULL); + assert(fixupWithTarget != NULL); + return; + } } // no need to rebase or bind PCRel stores @@ -2828,12 +2937,7 @@ void OutputFile::addClassicRelocs(ld::Internal& state, ld::Internal::FinalSectio switch ( fixupWithStore->kind ) { case ld::Fixup::kindLazyTarget: - { - // lazy pointers don't need relocs, but might need weak_import bit set - const ld::dylib::File* dylib = dynamic_cast(target->file()); - if ( (dylib != NULL) && (fixupWithTarget->weakImport || dylib->willBeWeakLinked()) ) - (const_cast(target))->setWeakImported(); - } + // lazy pointers don't need relocs break; case ld::Fixup::kindStoreLittleEndian32: case ld::Fixup::kindStoreLittleEndian64: @@ -2893,9 +2997,6 @@ void OutputFile::addClassicRelocs(ld::Internal& state, ld::Internal::FinalSectio _externalRelocsAtom->addExternalPointerReloc(relocAddress, target); sect->hasExternalRelocs = true; fixupWithTarget->contentAddendOnly = true; - // record if weak imported - if ( (dylib != NULL) && (fixupWithTarget->weakImport || dylib->willBeWeakLinked()) ) - (const_cast(target))->setWeakImported(); } else if ( needsLocalReloc ) { assert(target != NULL); @@ -2905,25 +3006,6 @@ void OutputFile::addClassicRelocs(ld::Internal& state, ld::Internal::FinalSectio sect->hasLocalRelocs = true; } break; - case ld::Fixup::kindStorePPCAbsLow14: - case ld::Fixup::kindStorePPCAbsLow16: - case ld::Fixup::kindStorePPCAbsHigh16AddLow: - case ld::Fixup::kindStorePPCAbsHigh16: - { - assert(target != NULL); - if ( target->definition() == ld::Atom::definitionProxy ) - throwf("half word text relocs not supported in %s", atom->name()); - if ( _options.outputSlidable() ) { - if ( inReadOnlySeg ) - noteTextReloc(atom, target); - uint32_t machoSectionIndex = (target->definition() == ld::Atom::definitionAbsolute) - ? R_ABS : target->machoSection(); - _localRelocsAtom->addTextReloc(relocAddress, fixupWithTarget->kind, - target->finalAddress(), machoSectionIndex); - sect->hasLocalRelocs = true; - } - } - break; case ld::Fixup::kindStoreTargetAddressX86BranchPCRel32: if ( _options.outputKind() == Options::kKextBundle ) { assert(target != NULL); @@ -2933,6 +3015,21 @@ void OutputFile::addClassicRelocs(ld::Internal& state, ld::Internal::FinalSectio } } break; + + case ld::Fixup::kindStoreARMLow16: + case ld::Fixup::kindStoreThumbLow16: + // no way to encode rebasing of binding for these instructions + if ( _options.outputSlidable() || (target->definition() == ld::Atom::definitionProxy) ) + throwf("no supported runtime lo16 relocation in %s from %s to %s", atom->name(), atom->file()->path(), target->name()); + break; + + case ld::Fixup::kindStoreARMHigh16: + case ld::Fixup::kindStoreThumbHigh16: + // no way to encode rebasing of binding for these instructions + if ( _options.outputSlidable() || (target->definition() == ld::Atom::definitionProxy) ) + throwf("no supported runtime hi16 relocation in %s from %s to %s", atom->name(), atom->file()->path(), target->name()); + break; + default: break; } @@ -2945,6 +3042,27 @@ bool OutputFile::useExternalSectionReloc(const ld::Atom* atom, const ld::Atom* t // x86_64 uses external relocations for everthing that has a symbol return ( target->symbolTableInclusion() != ld::Atom::symbolTableNotIn ); } + + // support arm branch interworking in -r mode + if ( (_options.architecture() == CPU_TYPE_ARM) && (_options.outputKind() == Options::kObjectFile) ) { + if ( atom->isThumb() != target->isThumb() ) { + switch ( fixupWithTarget->kind ) { + // have branch that switches mode, then might be 'b' not 'bl' + // Force external relocation, since no way to do local reloc for 'b' + case ld::Fixup::kindStoreTargetAddressThumbBranch22 : + case ld::Fixup::kindStoreTargetAddressARMBranch24: + return true; + default: + break; + } + } + } + + if ( (_options.architecture() == CPU_TYPE_I386) && (_options.outputKind() == Options::kObjectFile) ) { + if ( target->contentType() == ld::Atom::typeTLV ) + return true; + } + // most architectures use external relocations only for references // to a symbol in another translation unit or for references to "weak symbols" or tentative definitions assert(target != NULL); @@ -3001,7 +3119,15 @@ void OutputFile::addSectionRelocs(ld::Internal& state, ld::Internal::FinalSectio // pc-rel instructions are funny. If the target is _foo+8 and _foo is // external, then the pc-rel instruction *evalutates* to the address 8. if ( targetUsesExternalReloc ) { - if ( isPcRelStore(fixupWithStore->kind) ) { + // TLV support for i386 acts like RIP relative addressing + // The addend is the offset from the PICBase to the end of the instruction + if ( (_options.architecture() == CPU_TYPE_I386) + && (_options.outputKind() == Options::kObjectFile) + && (fixupWithStore->kind == ld::Fixup::kindStoreX86PCRel32TLVLoad) ) { + fixupWithTarget->contentAddendOnly = true; + fixupWithStore->contentAddendOnly = true; + } + else if ( isPcRelStore(fixupWithStore->kind) ) { fixupWithTarget->contentDetlaToAddendOnly = true; fixupWithStore->contentDetlaToAddendOnly = true; } @@ -3035,27 +3161,30 @@ void OutputFile::makeSplitSegInfo(ld::Internal& state) for (std::vector::iterator ait = sect->atoms.begin(); ait != sect->atoms.end(); ++ait) { const ld::Atom* atom = *ait; const ld::Atom* target = NULL; + const ld::Atom* fromTarget = NULL; + uint64_t accumulator = 0; + bool thumbTarget; bool hadSubtract = false; for (ld::Fixup::iterator fit = atom->fixupsBegin(), end=atom->fixupsEnd(); fit != end; ++fit) { if ( fit->firstInCluster() ) target = NULL; - if ( fit->kind == ld::Fixup::kindSubtractTargetAddress ) { - hadSubtract = true; - continue; + if ( this->setsTarget(fit->kind) ) { + accumulator = addressOf(state, fit, &target); + thumbTarget = targetIsThumb(state, fit); + if ( thumbTarget ) + accumulator |= 1; } - switch ( fit->binding ) { - case ld::Fixup::bindingNone: - case ld::Fixup::bindingByNameUnbound: + switch ( fit->kind ) { + case ld::Fixup::kindSubtractTargetAddress: + accumulator -= addressOf(state, fit, &fromTarget); + hadSubtract = true; break; - case ld::Fixup::bindingByContentBound: - case ld::Fixup::bindingDirectlyBound: - target = fit->u.target; + case ld::Fixup::kindAddAddend: + accumulator += fit->u.addend; break; - case ld::Fixup::bindingsIndirectlyBound: - target = state.indirectBindingTable[fit->u.bindingIndex]; + case ld::Fixup::kindSubtractAddend: + accumulator -= fit->u.addend; break; - } - switch ( fit->kind ) { case ld::Fixup::kindStoreBigEndian32: case ld::Fixup::kindStoreLittleEndian32: case ld::Fixup::kindStoreLittleEndian64: @@ -3065,6 +3194,7 @@ void OutputFile::makeSplitSegInfo(ld::Internal& state) // there is also a text reloc which update_dyld_shared_cache will use. if ( ! hadSubtract ) break; + // fall through case ld::Fixup::kindStoreX86PCRel32: case ld::Fixup::kindStoreX86PCRel32_1: case ld::Fixup::kindStoreX86PCRel32_2: @@ -3072,15 +3202,30 @@ void OutputFile::makeSplitSegInfo(ld::Internal& state) case ld::Fixup::kindStoreX86PCRel32GOTLoad: case ld::Fixup::kindStoreX86PCRel32GOTLoadNowLEA: case ld::Fixup::kindStoreX86PCRel32GOT: - case ld::Fixup::kindStorePPCPicHigh16AddLow: case ld::Fixup::kindStoreTargetAddressX86PCRel32: case ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoad: case ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoadNowLEA: + case ld::Fixup::kindStoreARMLow16: + case ld::Fixup::kindStoreThumbLow16: assert(target != NULL); if ( strcmp(sect->segmentName(), target->section().segmentName()) != 0 ) { _splitSegInfos.push_back(SplitSegInfoEntry(atom->finalAddress()+fit->offsetInAtom,fit->kind)); } break; + case ld::Fixup::kindStoreARMHigh16: + case ld::Fixup::kindStoreThumbHigh16: + assert(target != NULL); + if ( strcmp(sect->segmentName(), target->section().segmentName()) != 0 ) { + // hi16 needs to know upper 4-bits of low16 to compute carry + uint32_t extra = (accumulator >> 12) & 0xF; + _splitSegInfos.push_back(SplitSegInfoEntry(atom->finalAddress()+fit->offsetInAtom,fit->kind, extra)); + } + break; + case ld::Fixup::kindSetTargetImageOffset: + accumulator = addressOf(state, fit, &target); + assert(target != NULL); + hadSubtract = true; + break; default: break; } @@ -3107,8 +3252,8 @@ void OutputFile::writeMapFile(ld::Internal& state) // uuid[8], uuid[9], uuid[10], uuid[11], uuid[12], uuid[13], uuid[14], uuid[15]); //} // write table of object files - std::map readerToOrdinal; - std::map ordinalToReader; + std::map readerToOrdinal; + std::map ordinalToReader; std::map readerToFileOrdinal; for (std::vector::iterator sit = state.sections.begin(); sit != state.sections.end(); ++sit) { ld::Internal::FinalSection* sect = *sit; @@ -3119,8 +3264,8 @@ void OutputFile::writeMapFile(ld::Internal& state) const ld::File* reader = atom->file(); if ( reader == NULL ) continue; - uint32_t readerOrdinal = reader->ordinal(); - std::map::iterator pos = readerToOrdinal.find(reader); + ld::File::Ordinal readerOrdinal = reader->ordinal(); + std::map::iterator pos = readerToOrdinal.find(reader); if ( pos == readerToOrdinal.end() ) { readerToOrdinal[reader] = readerOrdinal; ordinalToReader[readerOrdinal] = reader; @@ -3129,13 +3274,10 @@ void OutputFile::writeMapFile(ld::Internal& state) } fprintf(mapFile, "# Object files:\n"); fprintf(mapFile, "[%3u] %s\n", 0, "linker synthesized"); - uint32_t fileIndex = 0; - readerToFileOrdinal[NULL] = fileIndex++; - for(std::map::iterator it = ordinalToReader.begin(); it != ordinalToReader.end(); ++it) { - if ( it->first != 0 ) { - fprintf(mapFile, "[%3u] %s\n", fileIndex, it->second->path()); - readerToFileOrdinal[it->second] = fileIndex++; - } + uint32_t fileIndex = 1; + for(std::map::iterator it = ordinalToReader.begin(); it != ordinalToReader.end(); ++it) { + fprintf(mapFile, "[%3u] %s\n", fileIndex, it->second->path()); + readerToFileOrdinal[it->second] = fileIndex++; } // write table of sections fprintf(mapFile, "# Sections:\n"); @@ -3212,14 +3354,14 @@ public: bool operator()(const ld::Atom* left, const ld::Atom* right) const { // first sort by reader - uint32_t leftFileOrdinal = left->file()->ordinal(); - uint32_t rightFileOrdinal = right->file()->ordinal(); + ld::File::Ordinal leftFileOrdinal = left->file()->ordinal(); + ld::File::Ordinal rightFileOrdinal = right->file()->ordinal(); if ( leftFileOrdinal!= rightFileOrdinal) return (leftFileOrdinal < rightFileOrdinal); // then sort by atom objectAddress - uint64_t leftAddr = left->objectAddress(); - uint64_t rightAddr = right->objectAddress(); + uint64_t leftAddr = left->finalAddress(); + uint64_t rightAddr = right->finalAddress(); return leftAddr < rightAddr; } }; @@ -3313,15 +3455,21 @@ void OutputFile::synthesizeDebugNotes(ld::Internal& state) const ld::Atom* atom = *it; const ld::File* atomFile = atom->file(); const ld::relocatable::File* atomObjFile = dynamic_cast(atomFile); - const char* newDirPath; - const char* newFilename; - //fprintf(stderr, "debug note for %s\n", atom->getDisplayName()); - if ( atom->translationUnitSource(&newDirPath, &newFilename) ) { + //fprintf(stderr, "debug note for %s\n", atom->name()); + const char* newPath = atom->translationUnitSource(); + if ( newPath != NULL ) { + const char* newDirPath; + const char* newFilename; + const char* lastSlash = strrchr(newPath, '/'); + if ( lastSlash == NULL ) + continue; + newFilename = lastSlash+1; + char* temp = strdup(newPath); + newDirPath = temp; + // gdb like directory SO's to end in '/', but dwarf DW_AT_comp_dir usually does not have trailing '/' + temp[lastSlash-newPath+1] = '\0'; // need SO's whenever the translation unit source file changes - if ( newFilename != filename ) { - // gdb like directory SO's to end in '/', but dwarf DW_AT_comp_dir usually does not have trailing '/' - if ( (newDirPath != NULL) && (strlen(newDirPath) > 1 ) && (newDirPath[strlen(newDirPath)-1] != '/') ) - asprintf((char**)&newDirPath, "%s/", newDirPath); + if ( (filename == NULL) || (strcmp(newFilename,filename) != 0) ) { if ( filename != NULL ) { // translation unit change, emit ending SO ld::relocatable::File::Stab endFileStab; @@ -3370,7 +3518,7 @@ void OutputFile::synthesizeDebugNotes(ld::Internal& state) // add the source file path to seenFiles so it does not show up in SOLs seenFiles.insert(newFilename); char* fullFilePath; - asprintf(&fullFilePath, "%s/%s", newDirPath, newFilename); + asprintf(&fullFilePath, "%s%s", newDirPath, newFilename); // add both leaf path and full path seenFiles.insert(fullFilePath); }