1 //===- lib/ReaderWriter/MachO/File.h ----------------------------*- C++ -*-===// 2 // 3 // The LLVM Linker 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 10 #ifndef LLD_READER_WRITER_MACHO_FILE_H 11 #define LLD_READER_WRITER_MACHO_FILE_H 12 13 #include "Atoms.h" 14 #include "DebugInfo.h" 15 #include "MachONormalizedFile.h" 16 #include "lld/Core/SharedLibraryFile.h" 17 #include "lld/Core/Simple.h" 18 #include "llvm/ADT/DenseMap.h" 19 #include "llvm/ADT/StringMap.h" 20 #include "llvm/Support/Format.h" 21 #include <unordered_map> 22 23 namespace lld { 24 namespace mach_o { 25 26 using lld::mach_o::normalized::Section; 27 28 class MachOFile : public SimpleFile { 29 public: 30 31 /// Real file constructor - for on-disk files. MachOFile(std::unique_ptr<MemoryBuffer> mb,MachOLinkingContext * ctx)32 MachOFile(std::unique_ptr<MemoryBuffer> mb, MachOLinkingContext *ctx) 33 : SimpleFile(mb->getBufferIdentifier(), File::kindMachObject), 34 _mb(std::move(mb)), _ctx(ctx) {} 35 36 /// Dummy file constructor - for virtual files. MachOFile(StringRef path)37 MachOFile(StringRef path) 38 : SimpleFile(path, File::kindMachObject) {} 39 addDefinedAtom(StringRef name,Atom::Scope scope,DefinedAtom::ContentType type,DefinedAtom::Merge merge,uint64_t sectionOffset,uint64_t contentSize,bool thumb,bool noDeadStrip,bool copyRefs,const Section * inSection)40 void addDefinedAtom(StringRef name, Atom::Scope scope, 41 DefinedAtom::ContentType type, DefinedAtom::Merge merge, 42 uint64_t sectionOffset, uint64_t contentSize, bool thumb, 43 bool noDeadStrip, bool copyRefs, 44 const Section *inSection) { 45 assert(sectionOffset+contentSize <= inSection->content.size()); 46 ArrayRef<uint8_t> content = inSection->content.slice(sectionOffset, 47 contentSize); 48 if (copyRefs) { 49 // Make a copy of the atom's name and content that is owned by this file. 50 name = name.copy(allocator()); 51 content = content.copy(allocator()); 52 } 53 DefinedAtom::Alignment align( 54 inSection->alignment, 55 sectionOffset % inSection->alignment); 56 auto *atom = 57 new (allocator()) MachODefinedAtom(*this, name, scope, type, merge, 58 thumb, noDeadStrip, content, align); 59 addAtomForSection(inSection, atom, sectionOffset); 60 } 61 addDefinedAtomInCustomSection(StringRef name,Atom::Scope scope,DefinedAtom::ContentType type,DefinedAtom::Merge merge,bool thumb,bool noDeadStrip,uint64_t sectionOffset,uint64_t contentSize,StringRef sectionName,bool copyRefs,const Section * inSection)62 void addDefinedAtomInCustomSection(StringRef name, Atom::Scope scope, 63 DefinedAtom::ContentType type, DefinedAtom::Merge merge, 64 bool thumb, bool noDeadStrip, uint64_t sectionOffset, 65 uint64_t contentSize, StringRef sectionName, 66 bool copyRefs, const Section *inSection) { 67 assert(sectionOffset+contentSize <= inSection->content.size()); 68 ArrayRef<uint8_t> content = inSection->content.slice(sectionOffset, 69 contentSize); 70 if (copyRefs) { 71 // Make a copy of the atom's name and content that is owned by this file. 72 name = name.copy(allocator()); 73 content = content.copy(allocator()); 74 sectionName = sectionName.copy(allocator()); 75 } 76 DefinedAtom::Alignment align( 77 inSection->alignment, 78 sectionOffset % inSection->alignment); 79 auto *atom = 80 new (allocator()) MachODefinedCustomSectionAtom(*this, name, scope, type, 81 merge, thumb, 82 noDeadStrip, content, 83 sectionName, align); 84 addAtomForSection(inSection, atom, sectionOffset); 85 } 86 addZeroFillDefinedAtom(StringRef name,Atom::Scope scope,uint64_t sectionOffset,uint64_t size,bool noDeadStrip,bool copyRefs,const Section * inSection)87 void addZeroFillDefinedAtom(StringRef name, Atom::Scope scope, 88 uint64_t sectionOffset, uint64_t size, 89 bool noDeadStrip, bool copyRefs, 90 const Section *inSection) { 91 if (copyRefs) { 92 // Make a copy of the atom's name and content that is owned by this file. 93 name = name.copy(allocator()); 94 } 95 DefinedAtom::Alignment align( 96 inSection->alignment, 97 sectionOffset % inSection->alignment); 98 99 DefinedAtom::ContentType type = DefinedAtom::typeUnknown; 100 switch (inSection->type) { 101 case llvm::MachO::S_ZEROFILL: 102 type = DefinedAtom::typeZeroFill; 103 break; 104 case llvm::MachO::S_THREAD_LOCAL_ZEROFILL: 105 type = DefinedAtom::typeTLVInitialZeroFill; 106 break; 107 default: 108 llvm_unreachable("Unrecognized zero-fill section"); 109 } 110 111 auto *atom = 112 new (allocator()) MachODefinedAtom(*this, name, scope, type, size, 113 noDeadStrip, align); 114 addAtomForSection(inSection, atom, sectionOffset); 115 } 116 addUndefinedAtom(StringRef name,bool copyRefs)117 void addUndefinedAtom(StringRef name, bool copyRefs) { 118 if (copyRefs) { 119 // Make a copy of the atom's name that is owned by this file. 120 name = name.copy(allocator()); 121 } 122 auto *atom = new (allocator()) SimpleUndefinedAtom(*this, name); 123 addAtom(*atom); 124 _undefAtoms[name] = atom; 125 } 126 addTentativeDefAtom(StringRef name,Atom::Scope scope,uint64_t size,DefinedAtom::Alignment align,bool copyRefs)127 void addTentativeDefAtom(StringRef name, Atom::Scope scope, uint64_t size, 128 DefinedAtom::Alignment align, bool copyRefs) { 129 if (copyRefs) { 130 // Make a copy of the atom's name that is owned by this file. 131 name = name.copy(allocator()); 132 } 133 auto *atom = 134 new (allocator()) MachOTentativeDefAtom(*this, name, scope, size, align); 135 addAtom(*atom); 136 _undefAtoms[name] = atom; 137 } 138 139 /// Search this file for an the atom from 'section' that covers 140 /// 'offsetInSect'. Returns nullptr is no atom found. 141 MachODefinedAtom *findAtomCoveringAddress(const Section §ion, 142 uint64_t offsetInSect, 143 uint32_t *foundOffsetAtom=nullptr) { 144 const auto &pos = _sectionAtoms.find(§ion); 145 if (pos == _sectionAtoms.end()) 146 return nullptr; 147 const auto &vec = pos->second; 148 assert(offsetInSect < section.content.size()); 149 // Vector of atoms for section are already sorted, so do binary search. 150 const auto &atomPos = std::lower_bound(vec.begin(), vec.end(), offsetInSect, 151 [offsetInSect](const SectionOffsetAndAtom &ao, 152 uint64_t targetAddr) -> bool { 153 // Each atom has a start offset of its slice of the 154 // section's content. This compare function must return true 155 // iff the atom's range is before the offset being searched for. 156 uint64_t atomsEndOffset = ao.offset+ao.atom->rawContent().size(); 157 return (atomsEndOffset <= offsetInSect); 158 }); 159 if (atomPos == vec.end()) 160 return nullptr; 161 if (foundOffsetAtom) 162 *foundOffsetAtom = offsetInSect - atomPos->offset; 163 return atomPos->atom; 164 } 165 166 /// Searches this file for an UndefinedAtom named 'name'. Returns 167 /// nullptr is no such atom found. findUndefAtom(StringRef name)168 const lld::Atom *findUndefAtom(StringRef name) { 169 auto pos = _undefAtoms.find(name); 170 if (pos == _undefAtoms.end()) 171 return nullptr; 172 return pos->second; 173 } 174 175 typedef std::function<void (MachODefinedAtom* atom)> DefinedAtomVisitor; 176 eachDefinedAtom(DefinedAtomVisitor vistor)177 void eachDefinedAtom(DefinedAtomVisitor vistor) { 178 for (auto §AndAtoms : _sectionAtoms) { 179 for (auto &offAndAtom : sectAndAtoms.second) { 180 vistor(offAndAtom.atom); 181 } 182 } 183 } 184 185 typedef std::function<void(MachODefinedAtom *atom, uint64_t offset)> 186 SectionAtomVisitor; 187 eachAtomInSection(const Section & section,SectionAtomVisitor visitor)188 void eachAtomInSection(const Section §ion, SectionAtomVisitor visitor) { 189 auto pos = _sectionAtoms.find(§ion); 190 if (pos == _sectionAtoms.end()) 191 return; 192 auto vec = pos->second; 193 194 for (auto &offAndAtom : vec) 195 visitor(offAndAtom.atom, offAndAtom.offset); 196 } 197 arch()198 MachOLinkingContext::Arch arch() const { return _arch; } setArch(MachOLinkingContext::Arch arch)199 void setArch(MachOLinkingContext::Arch arch) { _arch = arch; } 200 OS()201 MachOLinkingContext::OS OS() const { return _os; } setOS(MachOLinkingContext::OS os)202 void setOS(MachOLinkingContext::OS os) { _os = os; } 203 objcConstraint()204 MachOLinkingContext::ObjCConstraint objcConstraint() const { 205 return _objcConstraint; 206 } setObjcConstraint(MachOLinkingContext::ObjCConstraint v)207 void setObjcConstraint(MachOLinkingContext::ObjCConstraint v) { 208 _objcConstraint = v; 209 } 210 minVersion()211 uint32_t minVersion() const { return _minVersion; } setMinVersion(uint32_t v)212 void setMinVersion(uint32_t v) { _minVersion = v; } 213 minVersionLoadCommandKind()214 LoadCommandType minVersionLoadCommandKind() const { 215 return _minVersionLoadCommandKind; 216 } setMinVersionLoadCommandKind(LoadCommandType v)217 void setMinVersionLoadCommandKind(LoadCommandType v) { 218 _minVersionLoadCommandKind = v; 219 } 220 swiftVersion()221 uint32_t swiftVersion() const { return _swiftVersion; } setSwiftVersion(uint32_t v)222 void setSwiftVersion(uint32_t v) { _swiftVersion = v; } 223 subsectionsViaSymbols()224 bool subsectionsViaSymbols() const { 225 return _flags & llvm::MachO::MH_SUBSECTIONS_VIA_SYMBOLS; 226 } setFlags(normalized::FileFlags v)227 void setFlags(normalized::FileFlags v) { _flags = v; } 228 229 /// Methods for support type inquiry through isa, cast, and dyn_cast: classof(const File * F)230 static inline bool classof(const File *F) { 231 return F->kind() == File::kindMachObject; 232 } 233 setDebugInfo(std::unique_ptr<DebugInfo> debugInfo)234 void setDebugInfo(std::unique_ptr<DebugInfo> debugInfo) { 235 _debugInfo = std::move(debugInfo); 236 } 237 debugInfo()238 DebugInfo* debugInfo() const { return _debugInfo.get(); } takeDebugInfo()239 std::unique_ptr<DebugInfo> takeDebugInfo() { return std::move(_debugInfo); } 240 241 protected: doParse()242 std::error_code doParse() override { 243 // Convert binary file to normalized mach-o. 244 auto normFile = normalized::readBinary(_mb, _ctx->arch()); 245 if (auto ec = normFile.takeError()) 246 return llvm::errorToErrorCode(std::move(ec)); 247 // Convert normalized mach-o to atoms. 248 if (auto ec = normalized::normalizedObjectToAtoms(this, **normFile, false)) 249 return llvm::errorToErrorCode(std::move(ec)); 250 return std::error_code(); 251 } 252 253 private: 254 struct SectionOffsetAndAtom { uint64_t offset; MachODefinedAtom *atom; }; 255 addAtomForSection(const Section * inSection,MachODefinedAtom * atom,uint64_t sectionOffset)256 void addAtomForSection(const Section *inSection, MachODefinedAtom* atom, 257 uint64_t sectionOffset) { 258 SectionOffsetAndAtom offAndAtom; 259 offAndAtom.offset = sectionOffset; 260 offAndAtom.atom = atom; 261 _sectionAtoms[inSection].push_back(offAndAtom); 262 addAtom(*atom); 263 } 264 265 typedef llvm::DenseMap<const normalized::Section *, 266 std::vector<SectionOffsetAndAtom>> SectionToAtoms; 267 typedef llvm::StringMap<const lld::Atom *> NameToAtom; 268 269 std::unique_ptr<MemoryBuffer> _mb; 270 MachOLinkingContext *_ctx; 271 SectionToAtoms _sectionAtoms; 272 NameToAtom _undefAtoms; 273 MachOLinkingContext::Arch _arch = MachOLinkingContext::arch_unknown; 274 MachOLinkingContext::OS _os = MachOLinkingContext::OS::unknown; 275 uint32_t _minVersion = 0; 276 LoadCommandType _minVersionLoadCommandKind = (LoadCommandType)0; 277 MachOLinkingContext::ObjCConstraint _objcConstraint = 278 MachOLinkingContext::objc_unknown; 279 uint32_t _swiftVersion = 0; 280 normalized::FileFlags _flags = llvm::MachO::MH_SUBSECTIONS_VIA_SYMBOLS; 281 std::unique_ptr<DebugInfo> _debugInfo; 282 }; 283 284 class MachODylibFile : public SharedLibraryFile { 285 public: MachODylibFile(std::unique_ptr<MemoryBuffer> mb,MachOLinkingContext * ctx)286 MachODylibFile(std::unique_ptr<MemoryBuffer> mb, MachOLinkingContext *ctx) 287 : SharedLibraryFile(mb->getBufferIdentifier()), 288 _mb(std::move(mb)), _ctx(ctx) {} 289 MachODylibFile(StringRef path)290 MachODylibFile(StringRef path) : SharedLibraryFile(path) {} 291 exports(StringRef name)292 OwningAtomPtr<SharedLibraryAtom> exports(StringRef name) const override { 293 // Pass down _installName so that if this requested symbol 294 // is re-exported through this dylib, the SharedLibraryAtom's loadName() 295 // is this dylib installName and not the implementation dylib's. 296 // NOTE: isData is not needed for dylibs (it matters for static libs). 297 return exports(name, _installName); 298 } 299 300 /// Adds symbol name that this dylib exports. The corresponding 301 /// SharedLibraryAtom is created lazily (since most symbols are not used). addExportedSymbol(StringRef name,bool weakDef,bool copyRefs)302 void addExportedSymbol(StringRef name, bool weakDef, bool copyRefs) { 303 if (copyRefs) { 304 name = name.copy(allocator()); 305 } 306 AtomAndFlags info(weakDef); 307 _nameToAtom[name] = info; 308 } 309 addReExportedDylib(StringRef dylibPath)310 void addReExportedDylib(StringRef dylibPath) { 311 _reExportedDylibs.emplace_back(dylibPath); 312 } 313 installName()314 StringRef installName() const { return _installName; } currentVersion()315 uint32_t currentVersion() { return _currentVersion; } compatVersion()316 uint32_t compatVersion() { return _compatVersion; } 317 setInstallName(StringRef name)318 void setInstallName(StringRef name) { _installName = name; } setCompatVersion(uint32_t version)319 void setCompatVersion(uint32_t version) { _compatVersion = version; } setCurrentVersion(uint32_t version)320 void setCurrentVersion(uint32_t version) { _currentVersion = version; } 321 322 typedef std::function<MachODylibFile *(StringRef)> FindDylib; 323 loadReExportedDylibs(FindDylib find)324 void loadReExportedDylibs(FindDylib find) { 325 for (ReExportedDylib &entry : _reExportedDylibs) { 326 entry.file = find(entry.path); 327 } 328 } 329 getDSOName()330 StringRef getDSOName() const override { return _installName; } 331 doParse()332 std::error_code doParse() override { 333 // Convert binary file to normalized mach-o. 334 auto normFile = normalized::readBinary(_mb, _ctx->arch()); 335 if (auto ec = normFile.takeError()) 336 return llvm::errorToErrorCode(std::move(ec)); 337 // Convert normalized mach-o to atoms. 338 if (auto ec = normalized::normalizedDylibToAtoms(this, **normFile, false)) 339 return llvm::errorToErrorCode(std::move(ec)); 340 return std::error_code(); 341 } 342 343 private: exports(StringRef name,StringRef installName)344 OwningAtomPtr<SharedLibraryAtom> exports(StringRef name, 345 StringRef installName) const { 346 // First, check if requested symbol is directly implemented by this dylib. 347 auto entry = _nameToAtom.find(name); 348 if (entry != _nameToAtom.end()) { 349 // FIXME: Make this map a set and only used in assert builds. 350 // Note, its safe to assert here as the resolver is the only client of 351 // this API and it only requests exports for undefined symbols. 352 // If we return from here we are no longer undefined so we should never 353 // get here again. 354 assert(!entry->second.atom && "Duplicate shared library export"); 355 bool weakDef = entry->second.weakDef; 356 auto *atom = new (allocator()) MachOSharedLibraryAtom(*this, name, 357 installName, 358 weakDef); 359 entry->second.atom = atom; 360 return atom; 361 } 362 363 // Next, check if symbol is implemented in some re-exported dylib. 364 for (const ReExportedDylib &dylib : _reExportedDylibs) { 365 assert(dylib.file); 366 auto atom = dylib.file->exports(name, installName); 367 if (atom.get()) 368 return atom; 369 } 370 371 // Symbol not exported or re-exported by this dylib. 372 return nullptr; 373 } 374 375 struct ReExportedDylib { ReExportedDylibReExportedDylib376 ReExportedDylib(StringRef p) : path(p), file(nullptr) { } 377 StringRef path; 378 MachODylibFile *file; 379 }; 380 381 struct AtomAndFlags { AtomAndFlagsAtomAndFlags382 AtomAndFlags() : atom(nullptr), weakDef(false) { } AtomAndFlagsAtomAndFlags383 AtomAndFlags(bool weak) : atom(nullptr), weakDef(weak) { } 384 const SharedLibraryAtom *atom; 385 bool weakDef; 386 }; 387 388 std::unique_ptr<MemoryBuffer> _mb; 389 MachOLinkingContext *_ctx; 390 StringRef _installName; 391 uint32_t _currentVersion; 392 uint32_t _compatVersion; 393 std::vector<ReExportedDylib> _reExportedDylibs; 394 mutable std::unordered_map<StringRef, AtomAndFlags> _nameToAtom; 395 }; 396 397 } // end namespace mach_o 398 } // end namespace lld 399 400 #endif // LLD_READER_WRITER_MACHO_FILE_H 401