diff options
Diffstat (limited to 'lib/ReaderWriter/MachO/MachONormalizedFileToAtoms.cpp')
| -rw-r--r-- | lib/ReaderWriter/MachO/MachONormalizedFileToAtoms.cpp | 608 |
1 files changed, 430 insertions, 178 deletions
diff --git a/lib/ReaderWriter/MachO/MachONormalizedFileToAtoms.cpp b/lib/ReaderWriter/MachO/MachONormalizedFileToAtoms.cpp index f9499b603214..fc760a3eddd0 100644 --- a/lib/ReaderWriter/MachO/MachONormalizedFileToAtoms.cpp +++ b/lib/ReaderWriter/MachO/MachONormalizedFileToAtoms.cpp @@ -88,6 +88,8 @@ const MachORelocatableSectionToAtomType sectsToAtomType[] = { ENTRY("__DATA", "__thread_data", S_THREAD_LOCAL_REGULAR, typeTLVInitialData), ENTRY("__DATA", "__thread_bss", S_THREAD_LOCAL_ZEROFILL, typeTLVInitialZeroFill), + ENTRY("__DATA", "__objc_imageinfo", S_REGULAR, typeObjCImageInfo), + ENTRY("__DATA", "__objc_catlist", S_REGULAR, typeObjC2CategoryList), ENTRY("", "", S_INTERPOSING, typeInterposingTuples), ENTRY("__LD", "__compact_unwind", S_REGULAR, typeCompactUnwindInfo), @@ -180,6 +182,8 @@ void sectionParseInfo(DefinedAtom::ContentType atomType, atomizeCU), ENTRY(typeGOT, 4, scopeLinkageUnit, mergeByContent, atomizePointerSize), + ENTRY(typeObjC2CategoryList, 4, scopeTranslationUnit, mergeByContent, + atomizePointerSize), ENTRY(typeUnknown, 1, scopeGlobal, mergeNo, atomizeAtSymbols) }; @@ -265,11 +269,11 @@ void atomFromSymbol(DefinedAtom::ContentType atomType, const Section §ion, } } -std::error_code processSymboledSection(DefinedAtom::ContentType atomType, - const Section §ion, - const NormalizedFile &normalizedFile, - MachOFile &file, bool scatterable, - bool copyRefs) { +llvm::Error processSymboledSection(DefinedAtom::ContentType atomType, + const Section §ion, + const NormalizedFile &normalizedFile, + MachOFile &file, bool scatterable, + bool copyRefs) { // Find section's index. uint32_t sectIndex = 1; for (auto § : normalizedFile.sections) { @@ -316,7 +320,7 @@ std::error_code processSymboledSection(DefinedAtom::ContentType atomType, // If section has no symbols and no content, there are no atoms. if (symbols.empty() && section.content.empty()) - return std::error_code(); + return llvm::Error(); if (symbols.empty()) { // Section has no symbols, put all content in one anoymous atom. @@ -360,22 +364,22 @@ std::error_code processSymboledSection(DefinedAtom::ContentType atomType, file.eachAtomInSection(section, [&](MachODefinedAtom *atom, uint64_t offset)->void { if (prevAtom) - prevAtom->addReference(0, Reference::kindLayoutAfter, atom, 0, + prevAtom->addReference(Reference::KindNamespace::all, Reference::KindArch::all, - Reference::KindNamespace::all); + Reference::kindLayoutAfter, 0, atom, 0); prevAtom = atom; }); } - return std::error_code(); + return llvm::Error(); } -std::error_code processSection(DefinedAtom::ContentType atomType, - const Section §ion, - bool customSectionName, - const NormalizedFile &normalizedFile, - MachOFile &file, bool scatterable, - bool copyRefs) { +llvm::Error processSection(DefinedAtom::ContentType atomType, + const Section §ion, + bool customSectionName, + const NormalizedFile &normalizedFile, + MachOFile &file, bool scatterable, + bool copyRefs) { const bool is64 = MachOLinkingContext::is64Bit(normalizedFile.arch); const bool isBig = MachOLinkingContext::isBigEndian(normalizedFile.arch); @@ -388,12 +392,13 @@ std::error_code processSection(DefinedAtom::ContentType atomType, // Validate section size. if ((section.content.size() % sizeMultiple) != 0) - return make_dynamic_error_code(Twine("Section ") + section.segmentName - + "/" + section.sectionName - + " has size (" - + Twine(section.content.size()) - + ") which is not a multiple of " - + Twine(sizeMultiple) ); + return llvm::make_error<GenericError>(Twine("Section ") + + section.segmentName + + "/" + section.sectionName + + " has size (" + + Twine(section.content.size()) + + ") which is not a multiple of " + + Twine(sizeMultiple)); if (atomizeModel == atomizeAtSymbols) { // Break section up into atoms each with a fixed size. @@ -435,13 +440,13 @@ std::error_code processSection(DefinedAtom::ContentType atomType, // Break section up into dwarf unwind CFIs (FDE or CIE). size = read32(§ion.content[offset], isBig) + 4; if (offset+size > section.content.size()) { - return make_dynamic_error_code(Twine(Twine("Section ") - + section.segmentName - + "/" + section.sectionName - + " is malformed. Size of CFI " - "starting at offset (" - + Twine(offset) - + ") is past end of section.")); + return llvm::make_error<GenericError>(Twine("Section ") + + section.segmentName + + "/" + section.sectionName + + " is malformed. Size of CFI " + "starting at offset (" + + Twine(offset) + + ") is past end of section."); } break; case atomizeCU: @@ -456,10 +461,11 @@ std::error_code processSection(DefinedAtom::ContentType atomType, break; } if (size == 0) { - return make_dynamic_error_code(Twine("Section ") + section.segmentName - + "/" + section.sectionName - + " is malformed. The last atom is " - "not zero terminated."); + return llvm::make_error<GenericError>(Twine("Section ") + + section.segmentName + + "/" + section.sectionName + + " is malformed. The last atom " + "is not zero terminated."); } if (customSectionName) { // Mach-O needs a segment and section name. Concatentate those two @@ -477,7 +483,7 @@ std::error_code processSection(DefinedAtom::ContentType atomType, offset += size; } } - return std::error_code(); + return llvm::Error(); } const Section* findSectionCoveringAddress(const NormalizedFile &normalizedFile, @@ -509,23 +515,23 @@ findAtomCoveringAddress(const NormalizedFile &normalizedFile, MachOFile &file, // Walks all relocations for a section in a normalized .o file and // creates corresponding lld::Reference objects. -std::error_code convertRelocs(const Section §ion, - const NormalizedFile &normalizedFile, - bool scatterable, - MachOFile &file, - ArchHandler &handler) { +llvm::Error convertRelocs(const Section §ion, + const NormalizedFile &normalizedFile, + bool scatterable, + MachOFile &file, + ArchHandler &handler) { // Utility function for ArchHandler to find atom by its address. auto atomByAddr = [&] (uint32_t sectIndex, uint64_t addr, const lld::Atom **atom, Reference::Addend *addend) - -> std::error_code { + -> llvm::Error { if (sectIndex > normalizedFile.sections.size()) - return make_dynamic_error_code(Twine("out of range section " + return llvm::make_error<GenericError>(Twine("out of range section " "index (") + Twine(sectIndex) + ")"); const Section *sect = nullptr; if (sectIndex == 0) { sect = findSectionCoveringAddress(normalizedFile, addr); if (!sect) - return make_dynamic_error_code(Twine("address (" + Twine(addr) + return llvm::make_error<GenericError>(Twine("address (" + Twine(addr) + ") is not in any section")); } else { sect = &normalizedFile.sections[sectIndex-1]; @@ -534,12 +540,12 @@ std::error_code convertRelocs(const Section §ion, uint64_t offsetInSect = addr - sect->address; *atom = file.findAtomCoveringAddress(*sect, offsetInSect, &offsetInTarget); *addend = offsetInTarget; - return std::error_code(); + return llvm::Error(); }; // Utility function for ArchHandler to find atom by its symbol index. auto atomBySymbol = [&] (uint32_t symbolIndex, const lld::Atom **result) - -> std::error_code { + -> llvm::Error { // Find symbol from index. const Symbol *sym = nullptr; uint32_t numLocal = normalizedFile.localSymbols.size(); @@ -552,13 +558,13 @@ std::error_code convertRelocs(const Section §ion, } else if (symbolIndex < numLocal+numGlobal+numUndef) { sym = &normalizedFile.undefinedSymbols[symbolIndex-numLocal-numGlobal]; } else { - return make_dynamic_error_code(Twine("symbol index (") + return llvm::make_error<GenericError>(Twine("symbol index (") + Twine(symbolIndex) + ") out of range"); } // Find atom from symbol. if ((sym->type & N_TYPE) == N_SECT) { if (sym->sect > normalizedFile.sections.size()) - return make_dynamic_error_code(Twine("symbol section index (") + return llvm::make_error<GenericError>(Twine("symbol section index (") + Twine(sym->sect) + ") out of range "); const Section &symSection = normalizedFile.sections[sym->sect-1]; uint64_t targetOffsetInSect = sym->value - symSection.address; @@ -566,19 +572,19 @@ std::error_code convertRelocs(const Section §ion, targetOffsetInSect); if (target) { *result = target; - return std::error_code(); + return llvm::Error(); } - return make_dynamic_error_code("no atom found for defined symbol"); + return llvm::make_error<GenericError>("no atom found for defined symbol"); } else if ((sym->type & N_TYPE) == N_UNDF) { const lld::Atom *target = file.findUndefAtom(sym->name); if (target) { *result = target; - return std::error_code(); + return llvm::Error(); } - return make_dynamic_error_code("no undefined atom found for sym"); + return llvm::make_error<GenericError>("no undefined atom found for sym"); } else { // Search undefs - return make_dynamic_error_code("no atom found for symbol"); + return llvm::make_error<GenericError>("no atom found for symbol"); } }; @@ -589,7 +595,8 @@ std::error_code convertRelocs(const Section §ion, const Relocation &reloc = *it; // Find atom this relocation is in. if (reloc.offset > section.content.size()) - return make_dynamic_error_code(Twine("r_address (") + Twine(reloc.offset) + return llvm::make_error<GenericError>( + Twine("r_address (") + Twine(reloc.offset) + ") is larger than section size (" + Twine(section.content.size()) + ")"); uint32_t offsetInAtom; @@ -602,68 +609,74 @@ std::error_code convertRelocs(const Section §ion, const lld::Atom *target = nullptr; Reference::Addend addend = 0; Reference::KindValue kind; - std::error_code relocErr; if (handler.isPairedReloc(reloc)) { // Handle paired relocations together. const Relocation &reloc2 = *++it; - relocErr = handler.getPairReferenceInfo( + auto relocErr = handler.getPairReferenceInfo( reloc, reloc2, inAtom, offsetInAtom, fixupAddress, isBig, scatterable, atomByAddr, atomBySymbol, &kind, &target, &addend); if (relocErr) { - return make_dynamic_error_code( - Twine("bad relocation (") + relocErr.message() - + ") in section " - + section.segmentName + "/" + section.sectionName - + " (r1_address=" + Twine::utohexstr(reloc.offset) - + ", r1_type=" + Twine(reloc.type) - + ", r1_extern=" + Twine(reloc.isExtern) - + ", r1_length=" + Twine((int)reloc.length) - + ", r1_pcrel=" + Twine(reloc.pcRel) - + (!reloc.scattered ? (Twine(", r1_symbolnum=") - + Twine(reloc.symbol)) - : (Twine(", r1_scattered=1, r1_value=") - + Twine(reloc.value))) - + ")" - + ", (r2_address=" + Twine::utohexstr(reloc2.offset) - + ", r2_type=" + Twine(reloc2.type) - + ", r2_extern=" + Twine(reloc2.isExtern) - + ", r2_length=" + Twine((int)reloc2.length) - + ", r2_pcrel=" + Twine(reloc2.pcRel) - + (!reloc2.scattered ? (Twine(", r2_symbolnum=") - + Twine(reloc2.symbol)) - : (Twine(", r2_scattered=1, r2_value=") - + Twine(reloc2.value))) - + ")" ); + return handleErrors(std::move(relocErr), + [&](std::unique_ptr<GenericError> GE) { + return llvm::make_error<GenericError>( + Twine("bad relocation (") + GE->getMessage() + + ") in section " + + section.segmentName + "/" + section.sectionName + + " (r1_address=" + Twine::utohexstr(reloc.offset) + + ", r1_type=" + Twine(reloc.type) + + ", r1_extern=" + Twine(reloc.isExtern) + + ", r1_length=" + Twine((int)reloc.length) + + ", r1_pcrel=" + Twine(reloc.pcRel) + + (!reloc.scattered ? (Twine(", r1_symbolnum=") + + Twine(reloc.symbol)) + : (Twine(", r1_scattered=1, r1_value=") + + Twine(reloc.value))) + + ")" + + ", (r2_address=" + Twine::utohexstr(reloc2.offset) + + ", r2_type=" + Twine(reloc2.type) + + ", r2_extern=" + Twine(reloc2.isExtern) + + ", r2_length=" + Twine((int)reloc2.length) + + ", r2_pcrel=" + Twine(reloc2.pcRel) + + (!reloc2.scattered ? (Twine(", r2_symbolnum=") + + Twine(reloc2.symbol)) + : (Twine(", r2_scattered=1, r2_value=") + + Twine(reloc2.value))) + + ")" ); + }); } } else { // Use ArchHandler to convert relocation record into information // needed to instantiate an lld::Reference object. - relocErr = handler.getReferenceInfo( + auto relocErr = handler.getReferenceInfo( reloc, inAtom, offsetInAtom, fixupAddress, isBig, atomByAddr, atomBySymbol, &kind, &target, &addend); if (relocErr) { - return make_dynamic_error_code( - Twine("bad relocation (") + relocErr.message() - + ") in section " - + section.segmentName + "/" + section.sectionName - + " (r_address=" + Twine::utohexstr(reloc.offset) - + ", r_type=" + Twine(reloc.type) - + ", r_extern=" + Twine(reloc.isExtern) - + ", r_length=" + Twine((int)reloc.length) - + ", r_pcrel=" + Twine(reloc.pcRel) - + (!reloc.scattered ? (Twine(", r_symbolnum=") + Twine(reloc.symbol)) - : (Twine(", r_scattered=1, r_value=") - + Twine(reloc.value))) - + ")" ); + return handleErrors(std::move(relocErr), + [&](std::unique_ptr<GenericError> GE) { + return llvm::make_error<GenericError>( + Twine("bad relocation (") + GE->getMessage() + + ") in section " + + section.segmentName + "/" + section.sectionName + + " (r_address=" + Twine::utohexstr(reloc.offset) + + ", r_type=" + Twine(reloc.type) + + ", r_extern=" + Twine(reloc.isExtern) + + ", r_length=" + Twine((int)reloc.length) + + ", r_pcrel=" + Twine(reloc.pcRel) + + (!reloc.scattered ? (Twine(", r_symbolnum=") + Twine(reloc.symbol)) + : (Twine(", r_scattered=1, r_value=") + + Twine(reloc.value))) + + ")" ); + }); } } // Instantiate an lld::Reference object and add to its atom. - inAtom->addReference(offsetInAtom, kind, target, addend, - handler.kindArch()); + inAtom->addReference(Reference::KindNamespace::mach_o, + handler.kindArch(), + kind, offsetInAtom, target, addend); } - return std::error_code(); + return llvm::Error(); } bool isDebugInfoSection(const Section §ion) { @@ -684,44 +697,81 @@ static int64_t readSPtr(bool is64, bool isBig, const uint8_t *addr) { struct CIEInfo { bool _augmentationDataPresent = false; - bool _mayHaveLSDA = false; + bool _mayHaveEH = false; + uint32_t _offsetOfLSDA = ~0U; + uint32_t _offsetOfPersonality = ~0U; + uint32_t _offsetOfFDEPointerEncoding = ~0U; + uint32_t _augmentationDataLength = ~0U; }; typedef llvm::DenseMap<const MachODefinedAtom*, CIEInfo> CIEInfoMap; -static std::error_code processAugmentationString(const uint8_t *augStr, - CIEInfo &cieInfo, - unsigned *len = nullptr) { +static llvm::Error processAugmentationString(const uint8_t *augStr, + CIEInfo &cieInfo, + unsigned &len) { if (augStr[0] == '\0') { - if (len) - *len = 1; - return std::error_code(); + len = 1; + return llvm::Error(); } if (augStr[0] != 'z') - return make_dynamic_error_code("expected 'z' at start of augmentation " - "string"); + return llvm::make_error<GenericError>("expected 'z' at start of " + "augmentation string"); cieInfo._augmentationDataPresent = true; uint64_t idx = 1; + uint32_t offsetInAugmentationData = 0; while (augStr[idx] != '\0') { if (augStr[idx] == 'L') { - cieInfo._mayHaveLSDA = true; + cieInfo._offsetOfLSDA = offsetInAugmentationData; + // This adds a single byte to the augmentation data. + ++offsetInAugmentationData; + ++idx; + continue; + } + if (augStr[idx] == 'P') { + cieInfo._offsetOfPersonality = offsetInAugmentationData; + // This adds a single byte to the augmentation data for the encoding, + // then a number of bytes for the pointer data. + // FIXME: We are assuming 4 is correct here for the pointer size as we + // always currently use delta32ToGOT. + offsetInAugmentationData += 5; ++idx; - } else + continue; + } + if (augStr[idx] == 'R') { + cieInfo._offsetOfFDEPointerEncoding = offsetInAugmentationData; + // This adds a single byte to the augmentation data. + ++offsetInAugmentationData; ++idx; + continue; + } + if (augStr[idx] == 'e') { + if (augStr[idx + 1] != 'h') + return llvm::make_error<GenericError>("expected 'eh' in " + "augmentation string"); + cieInfo._mayHaveEH = true; + idx += 2; + continue; + } + ++idx; } - if (len) - *len = idx + 1; - return std::error_code(); + cieInfo._augmentationDataLength = offsetInAugmentationData; + + len = idx + 1; + return llvm::Error(); } -static std::error_code processCIE(const NormalizedFile &normalizedFile, - MachODefinedAtom *atom, - CIEInfoMap &cieInfos) { +static llvm::Error processCIE(const NormalizedFile &normalizedFile, + MachOFile &file, + mach_o::ArchHandler &handler, + const Section *ehFrameSection, + MachODefinedAtom *atom, + uint64_t offset, + CIEInfoMap &cieInfos) { const bool isBig = MachOLinkingContext::isBigEndian(normalizedFile.arch); const uint8_t *frameData = atom->rawContent().data(); @@ -734,31 +784,170 @@ static std::error_code processCIE(const NormalizedFile &normalizedFile, uint64_t versionField = cieIDField + sizeof(uint32_t); uint64_t augmentationStringField = versionField + sizeof(uint8_t); + unsigned augmentationStringLength = 0; if (auto err = processAugmentationString(frameData + augmentationStringField, - cieInfo)) + cieInfo, augmentationStringLength)) return err; + if (cieInfo._offsetOfPersonality != ~0U) { + // If we have augmentation data for the personality function, then we may + // need to implicitly generate its relocation. + + // Parse the EH Data field which is pointer sized. + uint64_t EHDataField = augmentationStringField + augmentationStringLength; + const bool is64 = MachOLinkingContext::is64Bit(normalizedFile.arch); + unsigned EHDataFieldSize = (cieInfo._mayHaveEH ? (is64 ? 8 : 4) : 0); + + // Parse Code Align Factor which is a ULEB128. + uint64_t CodeAlignField = EHDataField + EHDataFieldSize; + unsigned lengthFieldSize = 0; + llvm::decodeULEB128(frameData + CodeAlignField, &lengthFieldSize); + + // Parse Data Align Factor which is a SLEB128. + uint64_t DataAlignField = CodeAlignField + lengthFieldSize; + llvm::decodeSLEB128(frameData + DataAlignField, &lengthFieldSize); + + // Parse Return Address Register which is a byte. + uint64_t ReturnAddressField = DataAlignField + lengthFieldSize; + + // Parse the augmentation length which is a ULEB128. + uint64_t AugmentationLengthField = ReturnAddressField + 1; + uint64_t AugmentationLength = + llvm::decodeULEB128(frameData + AugmentationLengthField, + &lengthFieldSize); + + if (AugmentationLength != cieInfo._augmentationDataLength) + return llvm::make_error<GenericError>("CIE augmentation data length " + "mismatch"); + + // Get the start address of the augmentation data. + uint64_t AugmentationDataField = AugmentationLengthField + lengthFieldSize; + + // Parse the personality function from the augmentation data. + uint64_t PersonalityField = + AugmentationDataField + cieInfo._offsetOfPersonality; + + // Parse the personality encoding. + // FIXME: Verify that this is a 32-bit pcrel offset. + uint64_t PersonalityFunctionField = PersonalityField + 1; + + if (atom->begin() != atom->end()) { + // If we have an explicit relocation, then make sure it matches this + // offset as this is where we'd expect it to be applied to. + DefinedAtom::reference_iterator CurrentRef = atom->begin(); + if (CurrentRef->offsetInAtom() != PersonalityFunctionField) + return llvm::make_error<GenericError>("CIE personality reloc at " + "wrong offset"); + + if (++CurrentRef != atom->end()) + return llvm::make_error<GenericError>("CIE contains too many relocs"); + } else { + // Implicitly generate the personality function reloc. It's assumed to + // be a delta32 offset to a GOT entry. + // FIXME: Parse the encoding and check this. + int32_t funcDelta = read32(frameData + PersonalityFunctionField, isBig); + uint64_t funcAddress = ehFrameSection->address + offset + + PersonalityFunctionField; + funcAddress += funcDelta; + + const MachODefinedAtom *func = nullptr; + Reference::Addend addend; + func = findAtomCoveringAddress(normalizedFile, file, funcAddress, + &addend); + atom->addReference(Reference::KindNamespace::mach_o, handler.kindArch(), + handler.unwindRefToPersonalityFunctionKind(), + PersonalityFunctionField, func, addend); + } + } else if (atom->begin() != atom->end()) { + // Otherwise, we expect there to be no relocations in this atom as the only + // relocation would have been to the personality function. + return llvm::make_error<GenericError>("unexpected relocation in CIE"); + } + + cieInfos[atom] = std::move(cieInfo); - return std::error_code(); + return llvm::Error(); } -static std::error_code processFDE(const NormalizedFile &normalizedFile, - MachOFile &file, - mach_o::ArchHandler &handler, - const Section *ehFrameSection, - MachODefinedAtom *atom, - uint64_t offset, - const CIEInfoMap &cieInfos) { +static llvm::Error processFDE(const NormalizedFile &normalizedFile, + MachOFile &file, + mach_o::ArchHandler &handler, + const Section *ehFrameSection, + MachODefinedAtom *atom, + uint64_t offset, + const CIEInfoMap &cieInfos) { const bool isBig = MachOLinkingContext::isBigEndian(normalizedFile.arch); const bool is64 = MachOLinkingContext::is64Bit(normalizedFile.arch); // Compiler wasn't lazy and actually told us what it meant. + // Unfortunately, the compiler may not have generated references for all of + // [cie, func, lsda] and so we still need to parse the FDE and add references + // for any the compiler didn't generate. if (atom->begin() != atom->end()) - return std::error_code(); + atom->sortReferences(); + + DefinedAtom::reference_iterator CurrentRef = atom->begin(); + + // This helper returns the reference (if one exists) at the offset we are + // currently processing. It automatically increments the ref iterator if we + // do return a ref, and throws an error if we pass over a ref without + // comsuming it. + auto currentRefGetter = [&CurrentRef, + &atom](uint64_t Offset)->const Reference* { + // If there are no more refs found, then we are done. + if (CurrentRef == atom->end()) + return nullptr; + + const Reference *Ref = *CurrentRef; + + // If we haven't reached the offset for this reference, then return that + // we don't yet have a reference to process. + if (Offset < Ref->offsetInAtom()) + return nullptr; + + // If the offset is equal, then we want to process this ref. + if (Offset == Ref->offsetInAtom()) { + ++CurrentRef; + return Ref; + } + + // The current ref is at an offset which is earlier than the current + // offset, then we failed to consume it when we should have. In this case + // throw an error. + llvm::report_fatal_error("Skipped reference when processing FDE"); + }; + + // Helper to either get the reference at this current location, and verify + // that it is of the expected type, or add a reference of that type. + // Returns the reference target. + auto verifyOrAddReference = [&](uint64_t targetAddress, + Reference::KindValue refKind, + uint64_t refAddress, + bool allowsAddend)->const Atom* { + if (auto *ref = currentRefGetter(refAddress)) { + // The compiler already emitted a relocation for the CIE ref. This should + // have been converted to the correct type of reference in + // get[Pair]ReferenceInfo(). + assert(ref->kindValue() == refKind && + "Incorrect EHFrame reference kind"); + return ref->target(); + } + Reference::Addend addend; + auto *target = findAtomCoveringAddress(normalizedFile, file, + targetAddress, &addend); + atom->addReference(Reference::KindNamespace::mach_o, handler.kindArch(), + refKind, refAddress, target, addend); + + if (!allowsAddend) + assert(!addend && "EHFrame reference cannot have addend"); + return target; + }; + + const uint8_t *startFrameData = atom->rawContent().data(); + const uint8_t *frameData = startFrameData; - const uint8_t *frameData = atom->rawContent().data(); uint32_t size = read32(frameData, isBig); uint64_t cieFieldInFDE = size == 0xffffffffU ? sizeof(uint32_t) + sizeof(uint64_t) @@ -770,13 +959,11 @@ static std::error_code processFDE(const NormalizedFile &normalizedFile, uint64_t cieAddress = ehFrameSection->address + offset + cieFieldInFDE; cieAddress -= cieDelta; - Reference::Addend addend; - const MachODefinedAtom *cie = - findAtomCoveringAddress(normalizedFile, file, cieAddress, &addend); - atom->addReference(cieFieldInFDE, handler.unwindRefToCIEKind(), cie, - addend, handler.kindArch()); - - assert(cie && cie->contentType() == DefinedAtom::typeCFI && !addend && + auto *cieRefTarget = verifyOrAddReference(cieAddress, + handler.unwindRefToCIEKind(), + cieFieldInFDE, false); + const MachODefinedAtom *cie = dyn_cast<MachODefinedAtom>(cieRefTarget); + assert(cie && cie->contentType() == DefinedAtom::typeCFI && "FDE's CIE field does not point at the start of a CIE."); const CIEInfo &cieInfo = cieInfos.find(cie)->second; @@ -792,10 +979,9 @@ static std::error_code processFDE(const NormalizedFile &normalizedFile, uint64_t rangeStart = ehFrameSection->address + offset + rangeFieldInFDE; rangeStart += functionFromFDE; - const Atom *func = - findAtomCoveringAddress(normalizedFile, file, rangeStart, &addend); - atom->addReference(rangeFieldInFDE, handler.unwindRefToFunctionKind(), - func, addend, handler.kindArch()); + verifyOrAddReference(rangeStart, + handler.unwindRefToFunctionKind(), + rangeFieldInFDE, true); // Handle the augmentation data if there is any. if (cieInfo._augmentationDataPresent) { @@ -807,7 +993,7 @@ static std::error_code processFDE(const NormalizedFile &normalizedFile, llvm::decodeULEB128(frameData + augmentationDataLengthFieldInFDE, &lengthFieldSize); - if (cieInfo._mayHaveLSDA && augmentationDataLength > 0) { + if (cieInfo._offsetOfLSDA != ~0U && augmentationDataLength > 0) { // Look at the augmentation data field. uint64_t augmentationDataFieldInFDE = @@ -818,20 +1004,19 @@ static std::error_code processFDE(const NormalizedFile &normalizedFile, uint64_t lsdaStart = ehFrameSection->address + offset + augmentationDataFieldInFDE + lsdaFromFDE; - const Atom *lsda = - findAtomCoveringAddress(normalizedFile, file, lsdaStart, &addend); - atom->addReference(augmentationDataFieldInFDE, - handler.unwindRefToFunctionKind(), - lsda, addend, handler.kindArch()); + + verifyOrAddReference(lsdaStart, + handler.unwindRefToFunctionKind(), + augmentationDataFieldInFDE, true); } } - return std::error_code(); + return llvm::Error(); } -std::error_code addEHFrameReferences(const NormalizedFile &normalizedFile, - MachOFile &file, - mach_o::ArchHandler &handler) { +llvm::Error addEHFrameReferences(const NormalizedFile &normalizedFile, + MachOFile &file, + mach_o::ArchHandler &handler) { const Section *ehFrameSection = nullptr; for (auto §ion : normalizedFile.sections) @@ -843,9 +1028,9 @@ std::error_code addEHFrameReferences(const NormalizedFile &normalizedFile, // No __eh_frame so nothing to do. if (!ehFrameSection) - return std::error_code(); + return llvm::Error(); - std::error_code ehFrameErr; + llvm::Error ehFrameErr; CIEInfoMap cieInfos; file.eachAtomInSection(*ehFrameSection, @@ -858,7 +1043,8 @@ std::error_code addEHFrameReferences(const NormalizedFile &normalizedFile, const bool isBig = MachOLinkingContext::isBigEndian(normalizedFile.arch); if (ArchHandler::isDwarfCIE(isBig, atom)) - ehFrameErr = processCIE(normalizedFile, atom, cieInfos); + ehFrameErr = processCIE(normalizedFile, file, handler, ehFrameSection, + atom, offset, cieInfos); else ehFrameErr = processFDE(normalizedFile, file, handler, ehFrameSection, atom, offset, cieInfos); @@ -867,24 +1053,66 @@ std::error_code addEHFrameReferences(const NormalizedFile &normalizedFile, return ehFrameErr; } +llvm::Error parseObjCImageInfo(const Section §, + const NormalizedFile &normalizedFile, + MachOFile &file) { + + // struct objc_image_info { + // uint32_t version; // initially 0 + // uint32_t flags; + // }; + + ArrayRef<uint8_t> content = sect.content; + if (content.size() != 8) + return llvm::make_error<GenericError>(sect.segmentName + "/" + + sect.sectionName + + " in file " + file.path() + + " should be 8 bytes in size"); + + const bool isBig = MachOLinkingContext::isBigEndian(normalizedFile.arch); + uint32_t version = read32(content.data(), isBig); + if (version) + return llvm::make_error<GenericError>(sect.segmentName + "/" + + sect.sectionName + + " in file " + file.path() + + " should have version=0"); + + uint32_t flags = read32(content.data() + 4, isBig); + if (flags & (MachOLinkingContext::objc_supports_gc | + MachOLinkingContext::objc_gc_only)) + return llvm::make_error<GenericError>(sect.segmentName + "/" + + sect.sectionName + + " in file " + file.path() + + " uses GC. This is not supported"); + + if (flags & MachOLinkingContext::objc_retainReleaseForSimulator) + file.setObjcConstraint(MachOLinkingContext::objc_retainReleaseForSimulator); + else + file.setObjcConstraint(MachOLinkingContext::objc_retainRelease); + + file.setSwiftVersion((flags >> 8) & 0xFF); + + return llvm::Error(); +} + /// Converts normalized mach-o file into an lld::File and lld::Atoms. -ErrorOr<std::unique_ptr<lld::File>> +llvm::Expected<std::unique_ptr<lld::File>> objectToAtoms(const NormalizedFile &normalizedFile, StringRef path, bool copyRefs) { std::unique_ptr<MachOFile> file(new MachOFile(path)); - if (std::error_code ec = normalizedObjectToAtoms( - file.get(), normalizedFile, copyRefs)) - return ec; + if (auto ec = normalizedObjectToAtoms(file.get(), normalizedFile, copyRefs)) + return std::move(ec); return std::unique_ptr<File>(std::move(file)); } -ErrorOr<std::unique_ptr<lld::File>> +llvm::Expected<std::unique_ptr<lld::File>> dylibToAtoms(const NormalizedFile &normalizedFile, StringRef path, bool copyRefs) { // Instantiate SharedLibraryFile object. std::unique_ptr<MachODylibFile> file(new MachODylibFile(path)); - normalizedDylibToAtoms(file.get(), normalizedFile, copyRefs); + if (auto ec = normalizedDylibToAtoms(file.get(), normalizedFile, copyRefs)) + return std::move(ec); return std::unique_ptr<File>(std::move(file)); } @@ -892,7 +1120,12 @@ dylibToAtoms(const NormalizedFile &normalizedFile, StringRef path, namespace normalized { -std::error_code +static bool isObjCImageInfo(const Section §) { + return (sect.segmentName == "__OBJC" && sect.sectionName == "__image_info") || + (sect.segmentName == "__DATA" && sect.sectionName == "__objc_imageinfo"); +} + +llvm::Error normalizedObjectToAtoms(MachOFile *file, const NormalizedFile &normalizedFile, bool copyRefs) { @@ -905,12 +1138,23 @@ normalizedObjectToAtoms(MachOFile *file, DEBUG(llvm::dbgs() << "Creating atoms: "; sect.dump()); if (isDebugInfoSection(sect)) continue; + + + // If the file contains an objc_image_info struct, then we should parse the + // ObjC flags and Swift version. + if (isObjCImageInfo(sect)) { + if (auto ec = parseObjCImageInfo(sect, normalizedFile, *file)) + return ec; + // We then skip adding atoms for this section as we use the ObjCPass to + // re-emit this data after it has been aggregated for all files. + continue; + } + bool customSectionName; DefinedAtom::ContentType atomType = atomTypeFromSection(sect, customSectionName); - if (std::error_code ec = - processSection(atomType, sect, customSectionName, normalizedFile, - *file, scatterable, copyRefs)) + if (auto ec = processSection(atomType, sect, customSectionName, + normalizedFile, *file, scatterable, copyRefs)) return ec; } // Create atoms from undefined symbols. @@ -931,9 +1175,9 @@ normalizedObjectToAtoms(MachOFile *file, for (auto § : normalizedFile.sections) { if (isDebugInfoSection(sect)) continue; - if (std::error_code ec = convertRelocs(sect, normalizedFile, scatterable, - *file, *handler)) - return ec; + if (llvm::Error ec = convertRelocs(sect, normalizedFile, scatterable, + *file, *handler)) + return ec; } // Add additional arch-specific References @@ -945,7 +1189,7 @@ normalizedObjectToAtoms(MachOFile *file, // providing unwind info for) and itself (FDE -> CIE). These aren't // represented in the relocations on some architectures, so we have to add // them back in manually there. - if (std::error_code ec = addEHFrameReferences(normalizedFile, *file, *handler)) + if (auto ec = addEHFrameReferences(normalizedFile, *file, *handler)) return ec; // Process mach-o data-in-code regions array. That information is encoded in @@ -955,25 +1199,26 @@ normalizedObjectToAtoms(MachOFile *file, ++nextIndex; const Section* s = findSectionCoveringAddress(normalizedFile, entry.offset); if (!s) { - return make_dynamic_error_code(Twine("LC_DATA_IN_CODE address (" - + Twine(entry.offset) - + ") is not in any section")); + return llvm::make_error<GenericError>(Twine("LC_DATA_IN_CODE address (" + + Twine(entry.offset) + + ") is not in any section")); } uint64_t offsetInSect = entry.offset - s->address; uint32_t offsetInAtom; MachODefinedAtom *atom = file->findAtomCoveringAddress(*s, offsetInSect, &offsetInAtom); if (offsetInAtom + entry.length > atom->size()) { - return make_dynamic_error_code(Twine("LC_DATA_IN_CODE entry (offset=" - + Twine(entry.offset) - + ", length=" - + Twine(entry.length) - + ") crosses atom boundary.")); + return llvm::make_error<GenericError>(Twine("LC_DATA_IN_CODE entry " + "(offset=" + + Twine(entry.offset) + + ", length=" + + Twine(entry.length) + + ") crosses atom boundary.")); } // Add reference that marks start of data-in-code. - atom->addReference(offsetInAtom, - handler->dataInCodeTransitionStart(*atom), atom, - entry.kind, handler->kindArch()); + atom->addReference(Reference::KindNamespace::mach_o, handler->kindArch(), + handler->dataInCodeTransitionStart(*atom), + offsetInAtom, atom, entry.kind); // Peek at next entry, if it starts where this one ends, skip ending ref. if (nextIndex < normalizedFile.dataInCode.size()) { @@ -987,19 +1232,26 @@ normalizedObjectToAtoms(MachOFile *file, continue; // Add reference that marks end of data-in-code. - atom->addReference(offsetInAtom+entry.length, - handler->dataInCodeTransitionEnd(*atom), atom, 0, - handler->kindArch()); + atom->addReference(Reference::KindNamespace::mach_o, handler->kindArch(), + handler->dataInCodeTransitionEnd(*atom), + offsetInAtom+entry.length, atom, 0); } + // Cache some attributes on the file for use later. + file->setFlags(normalizedFile.flags); + file->setArch(normalizedFile.arch); + file->setOS(normalizedFile.os); + file->setMinVersion(normalizedFile.minOSverson); + file->setMinVersionLoadCommandKind(normalizedFile.minOSVersionKind); + // Sort references in each atom to their canonical order. for (const DefinedAtom* defAtom : file->defined()) { reinterpret_cast<const SimpleDefinedAtom*>(defAtom)->sortReferences(); } - return std::error_code(); + return llvm::Error(); } -std::error_code +llvm::Error normalizedDylibToAtoms(MachODylibFile *file, const NormalizedFile &normalizedFile, bool copyRefs) { @@ -1027,7 +1279,7 @@ normalizedDylibToAtoms(MachODylibFile *file, if (dep.kind == llvm::MachO::LC_REEXPORT_DYLIB) file->addReExportedDylib(dep.path); } - return std::error_code(); + return llvm::Error(); } void relocatableSectionInfoForContentType(DefinedAtom::ContentType atomType, @@ -1058,7 +1310,7 @@ void relocatableSectionInfoForContentType(DefinedAtom::ContentType atomType, llvm_unreachable("content type not yet supported"); } -ErrorOr<std::unique_ptr<lld::File>> +llvm::Expected<std::unique_ptr<lld::File>> normalizedToAtoms(const NormalizedFile &normalizedFile, StringRef path, bool copyRefs) { switch (normalizedFile.fileType) { |
