1 //===- lib/ReaderWriter/YAML/ReaderWriterYAML.cpp -------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "lld/Core/AbsoluteAtom.h"
10 #include "lld/Core/ArchiveLibraryFile.h"
11 #include "lld/Core/Atom.h"
12 #include "lld/Core/DefinedAtom.h"
13 #include "lld/Core/Error.h"
14 #include "lld/Core/File.h"
15 #include "lld/Core/LinkingContext.h"
16 #include "lld/Core/Reader.h"
17 #include "lld/Core/Reference.h"
18 #include "lld/Core/SharedLibraryAtom.h"
19 #include "lld/Core/Simple.h"
20 #include "lld/Core/UndefinedAtom.h"
21 #include "lld/Core/Writer.h"
22 #include "lld/ReaderWriter/YamlContext.h"
23 #include "llvm/ADT/ArrayRef.h"
24 #include "llvm/ADT/DenseMap.h"
25 #include "llvm/ADT/StringMap.h"
26 #include "llvm/ADT/StringRef.h"
27 #include "llvm/ADT/Twine.h"
28 #include "llvm/BinaryFormat/Magic.h"
29 #include "llvm/Support/Allocator.h"
30 #include "llvm/Support/Debug.h"
31 #include "llvm/Support/Error.h"
32 #include "llvm/Support/ErrorOr.h"
33 #include "llvm/Support/FileSystem.h"
34 #include "llvm/Support/Format.h"
35 #include "llvm/Support/MemoryBuffer.h"
36 #include "llvm/Support/YAMLTraits.h"
37 #include "llvm/Support/raw_ostream.h"
38 #include <cassert>
39 #include <cstdint>
40 #include <cstring>
41 #include <memory>
42 #include <string>
43 #include <system_error>
44 #include <vector>
45 
46 using llvm::file_magic;
47 using llvm::yaml::MappingTraits;
48 using llvm::yaml::ScalarEnumerationTraits;
49 using llvm::yaml::ScalarTraits;
50 using llvm::yaml::IO;
51 using llvm::yaml::SequenceTraits;
52 using llvm::yaml::DocumentListTraits;
53 
54 using namespace lld;
55 
56 /// The conversion of Atoms to and from YAML uses LLVM's YAML I/O.  This
57 /// file just defines template specializations on the lld types which control
58 /// how the mapping is done to and from YAML.
59 
60 namespace {
61 
62 /// Used when writing yaml files.
63 /// In most cases, atoms names are unambiguous, so references can just
64 /// use the atom name as the target (e.g. target: foo).  But in a few
65 /// cases that does not work, so ref-names are added.  These are labels
66 /// used only in yaml.  The labels do not exist in the Atom model.
67 ///
68 /// One need for ref-names are when atoms have no user supplied name
69 /// (e.g. c-string literal).  Another case is when two object files with
70 /// identically named static functions are merged (ld -r) into one object file.
71 /// In that case referencing the function by name is ambiguous, so a unique
72 /// ref-name is added.
73 class RefNameBuilder {
74 public:
RefNameBuilder(const lld::File & file)75   RefNameBuilder(const lld::File &file)
76       : _collisionCount(0), _unnamedCounter(0) {
77     // visit all atoms
78     for (const lld::DefinedAtom *atom : file.defined()) {
79       // Build map of atoms names to detect duplicates
80       if (!atom->name().empty())
81         buildDuplicateNameMap(*atom);
82 
83       // Find references to unnamed atoms and create ref-names for them.
84       for (const lld::Reference *ref : *atom) {
85         // create refname for any unnamed reference target
86         const lld::Atom *target = ref->target();
87         if ((target != nullptr) && target->name().empty()) {
88           std::string storage;
89           llvm::raw_string_ostream buffer(storage);
90           buffer << llvm::format("L%03d", _unnamedCounter++);
91           StringRef newName = copyString(buffer.str());
92           _refNames[target] = std::string(newName);
93           DEBUG_WITH_TYPE("WriterYAML",
94                           llvm::dbgs() << "unnamed atom: creating ref-name: '"
95                                        << newName << "' ("
96                                        << (const void *)newName.data() << ", "
97                                        << newName.size() << ")\n");
98         }
99       }
100     }
101     for (const lld::UndefinedAtom *undefAtom : file.undefined()) {
102       buildDuplicateNameMap(*undefAtom);
103     }
104     for (const lld::SharedLibraryAtom *shlibAtom : file.sharedLibrary()) {
105       buildDuplicateNameMap(*shlibAtom);
106     }
107     for (const lld::AbsoluteAtom *absAtom : file.absolute()) {
108       if (!absAtom->name().empty())
109         buildDuplicateNameMap(*absAtom);
110     }
111   }
112 
buildDuplicateNameMap(const lld::Atom & atom)113   void buildDuplicateNameMap(const lld::Atom &atom) {
114     assert(!atom.name().empty());
115     NameToAtom::iterator pos = _nameMap.find(atom.name());
116     if (pos != _nameMap.end()) {
117       // Found name collision, give each a unique ref-name.
118       std::string Storage;
119       llvm::raw_string_ostream buffer(Storage);
120       buffer << atom.name() << llvm::format(".%03d", ++_collisionCount);
121       StringRef newName = copyString(buffer.str());
122       _refNames[&atom] = std::string(newName);
123       DEBUG_WITH_TYPE("WriterYAML",
124                       llvm::dbgs() << "name collision: creating ref-name: '"
125                                    << newName << "' ("
126                                    << (const void *)newName.data()
127                                    << ", " << newName.size() << ")\n");
128       const lld::Atom *prevAtom = pos->second;
129       AtomToRefName::iterator pos2 = _refNames.find(prevAtom);
130       if (pos2 == _refNames.end()) {
131         // Only create ref-name for previous if none already created.
132         std::string Storage2;
133         llvm::raw_string_ostream buffer2(Storage2);
134         buffer2 << prevAtom->name() << llvm::format(".%03d", ++_collisionCount);
135         StringRef newName2 = copyString(buffer2.str());
136         _refNames[prevAtom] = std::string(newName2);
137         DEBUG_WITH_TYPE("WriterYAML",
138                         llvm::dbgs() << "name collision: creating ref-name: '"
139                                      << newName2 << "' ("
140                                      << (const void *)newName2.data() << ", "
141                                      << newName2.size() << ")\n");
142       }
143     } else {
144       // First time we've seen this name, just add it to map.
145       _nameMap[atom.name()] = &atom;
146       DEBUG_WITH_TYPE("WriterYAML", llvm::dbgs()
147                                         << "atom name seen for first time: '"
148                                         << atom.name() << "' ("
149                                         << (const void *)atom.name().data()
150                                         << ", " << atom.name().size() << ")\n");
151     }
152   }
153 
hasRefName(const lld::Atom * atom)154   bool hasRefName(const lld::Atom *atom) { return _refNames.count(atom); }
155 
refName(const lld::Atom * atom)156   StringRef refName(const lld::Atom *atom) {
157     return _refNames.find(atom)->second;
158   }
159 
160 private:
161   typedef llvm::StringMap<const lld::Atom *> NameToAtom;
162   typedef llvm::DenseMap<const lld::Atom *, std::string> AtomToRefName;
163 
164   // Allocate a new copy of this string in _storage, so the strings
165   // can be freed when RefNameBuilder is destroyed.
copyString(StringRef str)166   StringRef copyString(StringRef str) {
167     char *s = _storage.Allocate<char>(str.size());
168     memcpy(s, str.data(), str.size());
169     return StringRef(s, str.size());
170   }
171 
172   unsigned int                         _collisionCount;
173   unsigned int                         _unnamedCounter;
174   NameToAtom                           _nameMap;
175   AtomToRefName                        _refNames;
176   llvm::BumpPtrAllocator               _storage;
177 };
178 
179 /// Used when reading yaml files to find the target of a reference
180 /// that could be a name or ref-name.
181 class RefNameResolver {
182 public:
183   RefNameResolver(const lld::File *file, IO &io);
184 
lookup(StringRef name) const185   const lld::Atom *lookup(StringRef name) const {
186     NameToAtom::const_iterator pos = _nameMap.find(name);
187     if (pos != _nameMap.end())
188       return pos->second;
189     _io.setError(Twine("no such atom name: ") + name);
190     return nullptr;
191   }
192 
193 private:
194   typedef llvm::StringMap<const lld::Atom *> NameToAtom;
195 
add(StringRef name,const lld::Atom * atom)196   void add(StringRef name, const lld::Atom *atom) {
197     if (_nameMap.count(name)) {
198       _io.setError(Twine("duplicate atom name: ") + name);
199     } else {
200       _nameMap[name] = atom;
201     }
202   }
203 
204   IO &_io;
205   NameToAtom _nameMap;
206 };
207 
208 /// Mapping of Atoms.
209 template <typename T> class AtomList {
210   using Ty = std::vector<OwningAtomPtr<T>>;
211 
212 public:
begin()213   typename Ty::iterator begin() { return _atoms.begin(); }
end()214   typename Ty::iterator end() { return _atoms.end(); }
215   Ty _atoms;
216 };
217 
218 /// Mapping of kind: field in yaml files.
219 enum FileKinds {
220   fileKindObjectAtoms, // atom based object file encoded in yaml
221   fileKindArchive,     // static archive library encoded in yaml
222   fileKindObjectMachO  // mach-o object files encoded in yaml
223 };
224 
225 struct ArchMember {
226   FileKinds         _kind;
227   StringRef         _name;
228   const lld::File  *_content;
229 };
230 
231 // The content bytes in a DefinedAtom are just uint8_t but we want
232 // special formatting, so define a strong type.
233 LLVM_YAML_STRONG_TYPEDEF(uint8_t, ImplicitHex8)
234 
235 // SharedLibraryAtoms have a bool canBeNull() method which we'd like to be
236 // more readable than just true/false.
237 LLVM_YAML_STRONG_TYPEDEF(bool, ShlibCanBeNull)
238 
239 // lld::Reference::Kind is a tuple of <namespace, arch, value>.
240 // For yaml, we just want one string that encapsulates the tuple.
241 struct RefKind {
242   Reference::KindNamespace  ns;
243   Reference::KindArch       arch;
244   Reference::KindValue      value;
245 };
246 
247 } // end anonymous namespace
248 
249 LLVM_YAML_IS_SEQUENCE_VECTOR(ArchMember)
250 LLVM_YAML_IS_SEQUENCE_VECTOR(const lld::Reference *)
251 // Always write DefinedAtoms content bytes as a flow sequence.
252 LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(ImplicitHex8)
253 
254 // for compatibility with gcc-4.7 in C++11 mode, add extra namespace
255 namespace llvm {
256 namespace yaml {
257 
258 // This is a custom formatter for RefKind
259 template <> struct ScalarTraits<RefKind> {
outputllvm::yaml::ScalarTraits260   static void output(const RefKind &kind, void *ctxt, raw_ostream &out) {
261     assert(ctxt != nullptr);
262     YamlContext *info = reinterpret_cast<YamlContext *>(ctxt);
263     assert(info->_registry);
264     StringRef str;
265     if (info->_registry->referenceKindToString(kind.ns, kind.arch, kind.value,
266                                                str))
267       out << str;
268     else
269       out << (int)(kind.ns) << "-" << (int)(kind.arch) << "-" << kind.value;
270   }
271 
inputllvm::yaml::ScalarTraits272   static StringRef input(StringRef scalar, void *ctxt, RefKind &kind) {
273     assert(ctxt != nullptr);
274     YamlContext *info = reinterpret_cast<YamlContext *>(ctxt);
275     assert(info->_registry);
276     if (info->_registry->referenceKindFromString(scalar, kind.ns, kind.arch,
277                                                  kind.value))
278       return StringRef();
279     return StringRef("unknown reference kind");
280   }
281 
mustQuotellvm::yaml::ScalarTraits282   static QuotingType mustQuote(StringRef) { return QuotingType::None; }
283 };
284 
285 template <> struct ScalarEnumerationTraits<lld::File::Kind> {
enumerationllvm::yaml::ScalarEnumerationTraits286   static void enumeration(IO &io, lld::File::Kind &value) {
287     io.enumCase(value, "error-object",   lld::File::kindErrorObject);
288     io.enumCase(value, "object",         lld::File::kindMachObject);
289     io.enumCase(value, "shared-library", lld::File::kindSharedLibrary);
290     io.enumCase(value, "static-library", lld::File::kindArchiveLibrary);
291   }
292 };
293 
294 template <> struct ScalarEnumerationTraits<lld::Atom::Scope> {
enumerationllvm::yaml::ScalarEnumerationTraits295   static void enumeration(IO &io, lld::Atom::Scope &value) {
296     io.enumCase(value, "global", lld::Atom::scopeGlobal);
297     io.enumCase(value, "hidden", lld::Atom::scopeLinkageUnit);
298     io.enumCase(value, "static", lld::Atom::scopeTranslationUnit);
299   }
300 };
301 
302 template <> struct ScalarEnumerationTraits<lld::DefinedAtom::SectionChoice> {
enumerationllvm::yaml::ScalarEnumerationTraits303   static void enumeration(IO &io, lld::DefinedAtom::SectionChoice &value) {
304     io.enumCase(value, "content", lld::DefinedAtom::sectionBasedOnContent);
305     io.enumCase(value, "custom",  lld::DefinedAtom::sectionCustomPreferred);
306     io.enumCase(value, "custom-required",
307                                  lld::DefinedAtom::sectionCustomRequired);
308   }
309 };
310 
311 template <> struct ScalarEnumerationTraits<lld::DefinedAtom::Interposable> {
enumerationllvm::yaml::ScalarEnumerationTraits312   static void enumeration(IO &io, lld::DefinedAtom::Interposable &value) {
313     io.enumCase(value, "no",           DefinedAtom::interposeNo);
314     io.enumCase(value, "yes",          DefinedAtom::interposeYes);
315     io.enumCase(value, "yes-and-weak", DefinedAtom::interposeYesAndRuntimeWeak);
316   }
317 };
318 
319 template <> struct ScalarEnumerationTraits<lld::DefinedAtom::Merge> {
enumerationllvm::yaml::ScalarEnumerationTraits320   static void enumeration(IO &io, lld::DefinedAtom::Merge &value) {
321     io.enumCase(value, "no",           lld::DefinedAtom::mergeNo);
322     io.enumCase(value, "as-tentative", lld::DefinedAtom::mergeAsTentative);
323     io.enumCase(value, "as-weak",      lld::DefinedAtom::mergeAsWeak);
324     io.enumCase(value, "as-addressed-weak",
325                                    lld::DefinedAtom::mergeAsWeakAndAddressUsed);
326     io.enumCase(value, "by-content",   lld::DefinedAtom::mergeByContent);
327     io.enumCase(value, "same-name-and-size",
328                 lld::DefinedAtom::mergeSameNameAndSize);
329     io.enumCase(value, "largest", lld::DefinedAtom::mergeByLargestSection);
330   }
331 };
332 
333 template <> struct ScalarEnumerationTraits<lld::DefinedAtom::DeadStripKind> {
enumerationllvm::yaml::ScalarEnumerationTraits334   static void enumeration(IO &io, lld::DefinedAtom::DeadStripKind &value) {
335     io.enumCase(value, "normal", lld::DefinedAtom::deadStripNormal);
336     io.enumCase(value, "never",  lld::DefinedAtom::deadStripNever);
337     io.enumCase(value, "always", lld::DefinedAtom::deadStripAlways);
338   }
339 };
340 
341 template <> struct ScalarEnumerationTraits<lld::DefinedAtom::DynamicExport> {
enumerationllvm::yaml::ScalarEnumerationTraits342   static void enumeration(IO &io, lld::DefinedAtom::DynamicExport &value) {
343     io.enumCase(value, "normal", lld::DefinedAtom::dynamicExportNormal);
344     io.enumCase(value, "always", lld::DefinedAtom::dynamicExportAlways);
345   }
346 };
347 
348 template <> struct ScalarEnumerationTraits<lld::DefinedAtom::CodeModel> {
enumerationllvm::yaml::ScalarEnumerationTraits349   static void enumeration(IO &io, lld::DefinedAtom::CodeModel &value) {
350     io.enumCase(value, "none", lld::DefinedAtom::codeNA);
351     io.enumCase(value, "mips-pic", lld::DefinedAtom::codeMipsPIC);
352     io.enumCase(value, "mips-micro", lld::DefinedAtom::codeMipsMicro);
353     io.enumCase(value, "mips-micro-pic", lld::DefinedAtom::codeMipsMicroPIC);
354     io.enumCase(value, "mips-16", lld::DefinedAtom::codeMips16);
355     io.enumCase(value, "arm-thumb", lld::DefinedAtom::codeARMThumb);
356     io.enumCase(value, "arm-a", lld::DefinedAtom::codeARM_a);
357     io.enumCase(value, "arm-d", lld::DefinedAtom::codeARM_d);
358     io.enumCase(value, "arm-t", lld::DefinedAtom::codeARM_t);
359   }
360 };
361 
362 template <>
363 struct ScalarEnumerationTraits<lld::DefinedAtom::ContentPermissions> {
enumerationllvm::yaml::ScalarEnumerationTraits364   static void enumeration(IO &io, lld::DefinedAtom::ContentPermissions &value) {
365     io.enumCase(value, "---",     lld::DefinedAtom::perm___);
366     io.enumCase(value, "r--",     lld::DefinedAtom::permR__);
367     io.enumCase(value, "r-x",     lld::DefinedAtom::permR_X);
368     io.enumCase(value, "rw-",     lld::DefinedAtom::permRW_);
369     io.enumCase(value, "rwx",     lld::DefinedAtom::permRWX);
370     io.enumCase(value, "rw-l",    lld::DefinedAtom::permRW_L);
371     io.enumCase(value, "unknown", lld::DefinedAtom::permUnknown);
372   }
373 };
374 
375 template <> struct ScalarEnumerationTraits<lld::DefinedAtom::ContentType> {
enumerationllvm::yaml::ScalarEnumerationTraits376   static void enumeration(IO &io, lld::DefinedAtom::ContentType &value) {
377     io.enumCase(value, "unknown",         DefinedAtom::typeUnknown);
378     io.enumCase(value, "code",            DefinedAtom::typeCode);
379     io.enumCase(value, "stub",            DefinedAtom::typeStub);
380     io.enumCase(value, "constant",        DefinedAtom::typeConstant);
381     io.enumCase(value, "data",            DefinedAtom::typeData);
382     io.enumCase(value, "quick-data",      DefinedAtom::typeDataFast);
383     io.enumCase(value, "zero-fill",       DefinedAtom::typeZeroFill);
384     io.enumCase(value, "zero-fill-quick", DefinedAtom::typeZeroFillFast);
385     io.enumCase(value, "const-data",      DefinedAtom::typeConstData);
386     io.enumCase(value, "got",             DefinedAtom::typeGOT);
387     io.enumCase(value, "resolver",        DefinedAtom::typeResolver);
388     io.enumCase(value, "branch-island",   DefinedAtom::typeBranchIsland);
389     io.enumCase(value, "branch-shim",     DefinedAtom::typeBranchShim);
390     io.enumCase(value, "stub-helper",     DefinedAtom::typeStubHelper);
391     io.enumCase(value, "c-string",        DefinedAtom::typeCString);
392     io.enumCase(value, "utf16-string",    DefinedAtom::typeUTF16String);
393     io.enumCase(value, "unwind-cfi",      DefinedAtom::typeCFI);
394     io.enumCase(value, "unwind-lsda",     DefinedAtom::typeLSDA);
395     io.enumCase(value, "const-4-byte",    DefinedAtom::typeLiteral4);
396     io.enumCase(value, "const-8-byte",    DefinedAtom::typeLiteral8);
397     io.enumCase(value, "const-16-byte",   DefinedAtom::typeLiteral16);
398     io.enumCase(value, "lazy-pointer",    DefinedAtom::typeLazyPointer);
399     io.enumCase(value, "lazy-dylib-pointer",
400                                           DefinedAtom::typeLazyDylibPointer);
401     io.enumCase(value, "cfstring",        DefinedAtom::typeCFString);
402     io.enumCase(value, "initializer-pointer",
403                                           DefinedAtom::typeInitializerPtr);
404     io.enumCase(value, "terminator-pointer",
405                                           DefinedAtom::typeTerminatorPtr);
406     io.enumCase(value, "c-string-pointer",DefinedAtom::typeCStringPtr);
407     io.enumCase(value, "objc-class-pointer",
408                                           DefinedAtom::typeObjCClassPtr);
409     io.enumCase(value, "objc-category-list",
410                                           DefinedAtom::typeObjC2CategoryList);
411     io.enumCase(value, "objc-image-info",
412                                           DefinedAtom::typeObjCImageInfo);
413     io.enumCase(value, "objc-method-list",
414                                           DefinedAtom::typeObjCMethodList);
415     io.enumCase(value, "objc-class1",     DefinedAtom::typeObjC1Class);
416     io.enumCase(value, "dtraceDOF",       DefinedAtom::typeDTraceDOF);
417     io.enumCase(value, "interposing-tuples",
418                                           DefinedAtom::typeInterposingTuples);
419     io.enumCase(value, "lto-temp",        DefinedAtom::typeTempLTO);
420     io.enumCase(value, "compact-unwind",  DefinedAtom::typeCompactUnwindInfo);
421     io.enumCase(value, "unwind-info",     DefinedAtom::typeProcessedUnwindInfo);
422     io.enumCase(value, "tlv-thunk",       DefinedAtom::typeThunkTLV);
423     io.enumCase(value, "tlv-data",        DefinedAtom::typeTLVInitialData);
424     io.enumCase(value, "tlv-zero-fill",   DefinedAtom::typeTLVInitialZeroFill);
425     io.enumCase(value, "tlv-initializer-ptr",
426                                           DefinedAtom::typeTLVInitializerPtr);
427     io.enumCase(value, "mach_header",     DefinedAtom::typeMachHeader);
428     io.enumCase(value, "dso_handle",      DefinedAtom::typeDSOHandle);
429     io.enumCase(value, "sectcreate",      DefinedAtom::typeSectCreate);
430   }
431 };
432 
433 template <> struct ScalarEnumerationTraits<lld::UndefinedAtom::CanBeNull> {
enumerationllvm::yaml::ScalarEnumerationTraits434   static void enumeration(IO &io, lld::UndefinedAtom::CanBeNull &value) {
435     io.enumCase(value, "never",       lld::UndefinedAtom::canBeNullNever);
436     io.enumCase(value, "at-runtime",  lld::UndefinedAtom::canBeNullAtRuntime);
437     io.enumCase(value, "at-buildtime",lld::UndefinedAtom::canBeNullAtBuildtime);
438   }
439 };
440 
441 template <> struct ScalarEnumerationTraits<ShlibCanBeNull> {
enumerationllvm::yaml::ScalarEnumerationTraits442   static void enumeration(IO &io, ShlibCanBeNull &value) {
443     io.enumCase(value, "never",      false);
444     io.enumCase(value, "at-runtime", true);
445   }
446 };
447 
448 template <>
449 struct ScalarEnumerationTraits<lld::SharedLibraryAtom::Type> {
enumerationllvm::yaml::ScalarEnumerationTraits450   static void enumeration(IO &io, lld::SharedLibraryAtom::Type &value) {
451     io.enumCase(value, "code",    lld::SharedLibraryAtom::Type::Code);
452     io.enumCase(value, "data",    lld::SharedLibraryAtom::Type::Data);
453     io.enumCase(value, "unknown", lld::SharedLibraryAtom::Type::Unknown);
454   }
455 };
456 
457 /// This is a custom formatter for lld::DefinedAtom::Alignment.  Values look
458 /// like:
459 ///     8           # 8-byte aligned
460 ///     7 mod 16    # 16-byte aligned plus 7 bytes
461 template <> struct ScalarTraits<lld::DefinedAtom::Alignment> {
outputllvm::yaml::ScalarTraits462   static void output(const lld::DefinedAtom::Alignment &value, void *ctxt,
463                      raw_ostream &out) {
464     if (value.modulus == 0) {
465       out << llvm::format("%d", value.value);
466     } else {
467       out << llvm::format("%d mod %d", value.modulus, value.value);
468     }
469   }
470 
inputllvm::yaml::ScalarTraits471   static StringRef input(StringRef scalar, void *ctxt,
472                          lld::DefinedAtom::Alignment &value) {
473     value.modulus = 0;
474     size_t modStart = scalar.find("mod");
475     if (modStart != StringRef::npos) {
476       StringRef modStr = scalar.slice(0, modStart);
477       modStr = modStr.rtrim();
478       unsigned int modulus;
479       if (modStr.getAsInteger(0, modulus)) {
480         return "malformed alignment modulus";
481       }
482       value.modulus = modulus;
483       scalar = scalar.drop_front(modStart + 3);
484       scalar = scalar.ltrim();
485     }
486     unsigned int power;
487     if (scalar.getAsInteger(0, power)) {
488       return "malformed alignment power";
489     }
490     value.value = power;
491     if (value.modulus >= power) {
492       return "malformed alignment, modulus too large for power";
493     }
494     return StringRef(); // returning empty string means success
495   }
496 
mustQuotellvm::yaml::ScalarTraits497   static QuotingType mustQuote(StringRef) { return QuotingType::None; }
498 };
499 
500 template <> struct ScalarEnumerationTraits<FileKinds> {
enumerationllvm::yaml::ScalarEnumerationTraits501   static void enumeration(IO &io, FileKinds &value) {
502     io.enumCase(value, "object",        fileKindObjectAtoms);
503     io.enumCase(value, "archive",       fileKindArchive);
504     io.enumCase(value, "object-mach-o", fileKindObjectMachO);
505   }
506 };
507 
508 template <> struct MappingTraits<ArchMember> {
mappingllvm::yaml::MappingTraits509   static void mapping(IO &io, ArchMember &member) {
510     io.mapOptional("kind",    member._kind, fileKindObjectAtoms);
511     io.mapOptional("name",    member._name);
512     io.mapRequired("content", member._content);
513   }
514 };
515 
516 // Declare that an AtomList is a yaml sequence.
517 template <typename T> struct SequenceTraits<AtomList<T> > {
sizellvm::yaml::SequenceTraits518   static size_t size(IO &io, AtomList<T> &seq) { return seq._atoms.size(); }
elementllvm::yaml::SequenceTraits519   static T *&element(IO &io, AtomList<T> &seq, size_t index) {
520     if (index >= seq._atoms.size())
521       seq._atoms.resize(index + 1);
522     return seq._atoms[index].get();
523   }
524 };
525 
526 // Declare that an AtomRange is a yaml sequence.
527 template <typename T> struct SequenceTraits<File::AtomRange<T> > {
sizellvm::yaml::SequenceTraits528   static size_t size(IO &io, File::AtomRange<T> &seq) { return seq.size(); }
elementllvm::yaml::SequenceTraits529   static T *&element(IO &io, File::AtomRange<T> &seq, size_t index) {
530     assert(io.outputting() && "AtomRange only used when outputting");
531     assert(index < seq.size() && "Out of range access");
532     return seq[index].get();
533   }
534 };
535 
536 // Used to allow DefinedAtom content bytes to be a flow sequence of
537 // two-digit hex numbers without the leading 0x (e.g. FF, 04, 0A)
538 template <> struct ScalarTraits<ImplicitHex8> {
outputllvm::yaml::ScalarTraits539   static void output(const ImplicitHex8 &val, void *, raw_ostream &out) {
540     uint8_t num = val;
541     out << llvm::format("%02X", num);
542   }
543 
inputllvm::yaml::ScalarTraits544   static StringRef input(StringRef str, void *, ImplicitHex8 &val) {
545     unsigned long long n;
546     if (getAsUnsignedInteger(str, 16, n))
547       return "invalid two-digit-hex number";
548     if (n > 0xFF)
549       return "out of range two-digit-hex number";
550     val = n;
551     return StringRef(); // returning empty string means success
552   }
553 
mustQuotellvm::yaml::ScalarTraits554   static QuotingType mustQuote(StringRef) { return QuotingType::None; }
555 };
556 
557 // YAML conversion for std::vector<const lld::File*>
558 template <> struct DocumentListTraits<std::vector<const lld::File *> > {
sizellvm::yaml::DocumentListTraits559   static size_t size(IO &io, std::vector<const lld::File *> &seq) {
560     return seq.size();
561   }
elementllvm::yaml::DocumentListTraits562   static const lld::File *&element(IO &io, std::vector<const lld::File *> &seq,
563                                    size_t index) {
564     if (index >= seq.size())
565       seq.resize(index + 1);
566     return seq[index];
567   }
568 };
569 
570 // YAML conversion for const lld::File*
571 template <> struct MappingTraits<const lld::File *> {
572   class NormArchiveFile : public lld::ArchiveLibraryFile {
573   public:
NormArchiveFile(IO & io)574     NormArchiveFile(IO &io) : ArchiveLibraryFile("") {}
575 
NormArchiveFile(IO & io,const lld::File * file)576     NormArchiveFile(IO &io, const lld::File *file)
577         : ArchiveLibraryFile(file->path()), _path(file->path()) {
578       // If we want to support writing archives, this constructor would
579       // need to populate _members.
580     }
581 
denormalize(IO & io)582     const lld::File *denormalize(IO &io) { return this; }
583 
defined() const584     const AtomRange<lld::DefinedAtom> defined() const override {
585       return _noDefinedAtoms;
586     }
587 
undefined() const588     const AtomRange<lld::UndefinedAtom> undefined() const override {
589       return _noUndefinedAtoms;
590     }
591 
sharedLibrary() const592     const AtomRange<lld::SharedLibraryAtom> sharedLibrary() const override {
593       return _noSharedLibraryAtoms;
594     }
595 
absolute() const596     const AtomRange<lld::AbsoluteAtom> absolute() const override {
597       return _noAbsoluteAtoms;
598     }
599 
clearAtoms()600     void clearAtoms() override {
601       _noDefinedAtoms.clear();
602       _noUndefinedAtoms.clear();
603       _noSharedLibraryAtoms.clear();
604       _noAbsoluteAtoms.clear();
605     }
606 
find(StringRef name)607     File *find(StringRef name) override {
608       for (const ArchMember &member : _members)
609         for (const lld::DefinedAtom *atom : member._content->defined())
610           if (name == atom->name())
611             return const_cast<File *>(member._content);
612       return nullptr;
613     }
614 
615     std::error_code
parseAllMembers(std::vector<std::unique_ptr<File>> & result)616     parseAllMembers(std::vector<std::unique_ptr<File>> &result) override {
617       return std::error_code();
618     }
619 
620     StringRef               _path;
621     std::vector<ArchMember> _members;
622   };
623 
624   class NormalizedFile : public lld::File {
625   public:
NormalizedFile(IO & io)626     NormalizedFile(IO &io)
627       : File("", kindNormalizedObject), _io(io), _rnb(nullptr),
628         _definedAtomsRef(_definedAtoms._atoms),
629         _undefinedAtomsRef(_undefinedAtoms._atoms),
630         _sharedLibraryAtomsRef(_sharedLibraryAtoms._atoms),
631         _absoluteAtomsRef(_absoluteAtoms._atoms) {}
632 
NormalizedFile(IO & io,const lld::File * file)633     NormalizedFile(IO &io, const lld::File *file)
634         : File(file->path(), kindNormalizedObject), _io(io),
635           _rnb(new RefNameBuilder(*file)), _path(file->path()),
636         _definedAtomsRef(file->defined()),
637         _undefinedAtomsRef(file->undefined()),
638         _sharedLibraryAtomsRef(file->sharedLibrary()),
639         _absoluteAtomsRef(file->absolute()) {
640     }
641 
~NormalizedFile()642     ~NormalizedFile() override {
643     }
644 
645     const lld::File *denormalize(IO &io);
646 
defined() const647     const AtomRange<lld::DefinedAtom> defined() const override {
648       return _definedAtomsRef;
649     }
650 
undefined() const651     const AtomRange<lld::UndefinedAtom> undefined() const override {
652       return _undefinedAtomsRef;
653     }
654 
sharedLibrary() const655     const AtomRange<lld::SharedLibraryAtom> sharedLibrary() const override {
656       return _sharedLibraryAtomsRef;
657     }
658 
absolute() const659     const AtomRange<lld::AbsoluteAtom> absolute() const override {
660       return _absoluteAtomsRef;
661     }
662 
clearAtoms()663     void clearAtoms() override {
664       _definedAtoms._atoms.clear();
665       _undefinedAtoms._atoms.clear();
666       _sharedLibraryAtoms._atoms.clear();
667       _absoluteAtoms._atoms.clear();
668     }
669 
670     // Allocate a new copy of this string in _storage, so the strings
671     // can be freed when File is destroyed.
copyString(StringRef str)672     StringRef copyString(StringRef str) {
673       char *s = _storage.Allocate<char>(str.size());
674       memcpy(s, str.data(), str.size());
675       return StringRef(s, str.size());
676     }
677 
678     IO                                  &_io;
679     std::unique_ptr<RefNameBuilder>      _rnb;
680     StringRef                            _path;
681     AtomList<lld::DefinedAtom>           _definedAtoms;
682     AtomList<lld::UndefinedAtom>         _undefinedAtoms;
683     AtomList<lld::SharedLibraryAtom>     _sharedLibraryAtoms;
684     AtomList<lld::AbsoluteAtom>          _absoluteAtoms;
685     AtomRange<lld::DefinedAtom>          _definedAtomsRef;
686     AtomRange<lld::UndefinedAtom>        _undefinedAtomsRef;
687     AtomRange<lld::SharedLibraryAtom>    _sharedLibraryAtomsRef;
688     AtomRange<lld::AbsoluteAtom>         _absoluteAtomsRef;
689     llvm::BumpPtrAllocator               _storage;
690   };
691 
mappingllvm::yaml::MappingTraits692   static void mapping(IO &io, const lld::File *&file) {
693     YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext());
694     assert(info != nullptr);
695     // Let any register tag handler process this.
696     if (info->_registry && info->_registry->handleTaggedDoc(io, file))
697       return;
698     // If no registered handler claims this tag and there is no tag,
699     // grandfather in as "!native".
700     if (io.mapTag("!native", true) || io.mapTag("tag:yaml.org,2002:map"))
701       mappingAtoms(io, file);
702   }
703 
mappingAtomsllvm::yaml::MappingTraits704   static void mappingAtoms(IO &io, const lld::File *&file) {
705     YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext());
706     MappingNormalizationHeap<NormalizedFile, const lld::File *>
707       keys(io, file, nullptr);
708     assert(info != nullptr);
709     info->_file = keys.operator->();
710 
711     io.mapOptional("path",                 keys->_path);
712 
713     if (io.outputting()) {
714       io.mapOptional("defined-atoms",        keys->_definedAtomsRef);
715       io.mapOptional("undefined-atoms",      keys->_undefinedAtomsRef);
716       io.mapOptional("shared-library-atoms", keys->_sharedLibraryAtomsRef);
717       io.mapOptional("absolute-atoms",       keys->_absoluteAtomsRef);
718     } else {
719       io.mapOptional("defined-atoms",        keys->_definedAtoms);
720       io.mapOptional("undefined-atoms",      keys->_undefinedAtoms);
721       io.mapOptional("shared-library-atoms", keys->_sharedLibraryAtoms);
722       io.mapOptional("absolute-atoms",       keys->_absoluteAtoms);
723     }
724   }
725 
mappingArchivellvm::yaml::MappingTraits726   static void mappingArchive(IO &io, const lld::File *&file) {
727     YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext());
728     MappingNormalizationHeap<NormArchiveFile, const lld::File *>
729       keys(io, file, &info->_file->allocator());
730 
731     io.mapOptional("path",    keys->_path);
732     io.mapOptional("members", keys->_members);
733   }
734 };
735 
736 // YAML conversion for const lld::Reference*
737 template <> struct MappingTraits<const lld::Reference *> {
738   class NormalizedReference : public lld::Reference {
739   public:
NormalizedReference(IO & io)740     NormalizedReference(IO &io)
741         : lld::Reference(lld::Reference::KindNamespace::all,
742                          lld::Reference::KindArch::all, 0),
743           _target(nullptr), _offset(0), _addend(0), _tag(0) {}
744 
NormalizedReference(IO & io,const lld::Reference * ref)745     NormalizedReference(IO &io, const lld::Reference *ref)
746         : lld::Reference(ref->kindNamespace(), ref->kindArch(),
747                          ref->kindValue()),
748           _target(nullptr), _targetName(targetName(io, ref)),
749           _offset(ref->offsetInAtom()), _addend(ref->addend()),
750           _tag(ref->tag()) {
751       _mappedKind.ns = ref->kindNamespace();
752       _mappedKind.arch = ref->kindArch();
753       _mappedKind.value = ref->kindValue();
754     }
755 
denormalize(IO & io)756     const lld::Reference *denormalize(IO &io) {
757       YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext());
758       assert(info != nullptr);
759       typedef MappingTraits<const lld::File *>::NormalizedFile NormalizedFile;
760       NormalizedFile *f = reinterpret_cast<NormalizedFile *>(info->_file);
761       if (!_targetName.empty())
762         _targetName = f->copyString(_targetName);
763       DEBUG_WITH_TYPE("WriterYAML", llvm::dbgs()
764                                         << "created Reference to name: '"
765                                         << _targetName << "' ("
766                                         << (const void *)_targetName.data()
767                                         << ", " << _targetName.size() << ")\n");
768       setKindNamespace(_mappedKind.ns);
769       setKindArch(_mappedKind.arch);
770       setKindValue(_mappedKind.value);
771       return this;
772     }
773 
774     void bind(const RefNameResolver &);
775     static StringRef targetName(IO &io, const lld::Reference *ref);
776 
offsetInAtom() const777     uint64_t offsetInAtom() const override { return _offset; }
target() const778     const lld::Atom *target() const override { return _target; }
addend() const779     Addend addend() const override { return _addend; }
setAddend(Addend a)780     void setAddend(Addend a) override { _addend = a; }
setTarget(const lld::Atom * a)781     void setTarget(const lld::Atom *a) override { _target = a; }
782 
783     const lld::Atom *_target;
784     StringRef        _targetName;
785     uint32_t         _offset;
786     Addend           _addend;
787     RefKind          _mappedKind;
788     uint32_t         _tag;
789   };
790 
mappingllvm::yaml::MappingTraits791   static void mapping(IO &io, const lld::Reference *&ref) {
792     YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext());
793     MappingNormalizationHeap<NormalizedReference, const lld::Reference *> keys(
794         io, ref, &info->_file->allocator());
795 
796     io.mapRequired("kind",   keys->_mappedKind);
797     io.mapOptional("offset", keys->_offset);
798     io.mapOptional("target", keys->_targetName);
799     io.mapOptional("addend", keys->_addend, (lld::Reference::Addend)0);
800     io.mapOptional("tag",    keys->_tag, 0u);
801   }
802 };
803 
804 // YAML conversion for const lld::DefinedAtom*
805 template <> struct MappingTraits<const lld::DefinedAtom *> {
806 
807   class NormalizedAtom : public lld::DefinedAtom {
808   public:
NormalizedAtom(IO & io)809     NormalizedAtom(IO &io)
810         : _file(fileFromContext(io)), _contentType(), _alignment(1) {
811       static uint32_t ordinalCounter = 1;
812       _ordinal = ordinalCounter++;
813     }
814 
NormalizedAtom(IO & io,const lld::DefinedAtom * atom)815     NormalizedAtom(IO &io, const lld::DefinedAtom *atom)
816         : _file(fileFromContext(io)), _name(atom->name()),
817           _scope(atom->scope()), _interpose(atom->interposable()),
818           _merge(atom->merge()), _contentType(atom->contentType()),
819           _alignment(atom->alignment()), _sectionChoice(atom->sectionChoice()),
820           _deadStrip(atom->deadStrip()), _dynamicExport(atom->dynamicExport()),
821           _codeModel(atom->codeModel()),
822           _permissions(atom->permissions()), _size(atom->size()),
823           _sectionName(atom->customSectionName()),
824           _sectionSize(atom->sectionSize()) {
825       for (const lld::Reference *r : *atom)
826         _references.push_back(r);
827       if (!atom->occupiesDiskSpace())
828         return;
829       ArrayRef<uint8_t> cont = atom->rawContent();
830       _content.reserve(cont.size());
831       for (uint8_t x : cont)
832         _content.push_back(x);
833     }
834 
835     ~NormalizedAtom() override = default;
836 
denormalize(IO & io)837     const lld::DefinedAtom *denormalize(IO &io) {
838       YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext());
839       assert(info != nullptr);
840       typedef MappingTraits<const lld::File *>::NormalizedFile NormalizedFile;
841       NormalizedFile *f = reinterpret_cast<NormalizedFile *>(info->_file);
842       if (!_name.empty())
843         _name = f->copyString(_name);
844       if (!_refName.empty())
845         _refName = f->copyString(_refName);
846       if (!_sectionName.empty())
847         _sectionName = f->copyString(_sectionName);
848       DEBUG_WITH_TYPE("WriterYAML",
849                       llvm::dbgs() << "created DefinedAtom named: '" << _name
850                                    << "' (" << (const void *)_name.data()
851                                    << ", " << _name.size() << ")\n");
852       return this;
853     }
854 
855     void bind(const RefNameResolver &);
856 
857     // Extract current File object from YAML I/O parsing context
fileFromContext(IO & io)858     const lld::File &fileFromContext(IO &io) {
859       YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext());
860       assert(info != nullptr);
861       assert(info->_file != nullptr);
862       return *info->_file;
863     }
864 
file() const865     const lld::File &file() const override { return _file; }
name() const866     StringRef name() const override { return _name; }
size() const867     uint64_t size() const override { return _size; }
scope() const868     Scope scope() const override { return _scope; }
interposable() const869     Interposable interposable() const override { return _interpose; }
merge() const870     Merge merge() const override { return _merge; }
contentType() const871     ContentType contentType() const override { return _contentType; }
alignment() const872     Alignment alignment() const override { return _alignment; }
sectionChoice() const873     SectionChoice sectionChoice() const override { return _sectionChoice; }
customSectionName() const874     StringRef customSectionName() const override { return _sectionName; }
sectionSize() const875     uint64_t sectionSize() const override { return _sectionSize; }
deadStrip() const876     DeadStripKind deadStrip() const override { return _deadStrip; }
dynamicExport() const877     DynamicExport dynamicExport() const override { return _dynamicExport; }
codeModel() const878     CodeModel codeModel() const override { return _codeModel; }
permissions() const879     ContentPermissions permissions() const override { return _permissions; }
rawContent() const880     ArrayRef<uint8_t> rawContent() const override {
881       if (!occupiesDiskSpace())
882         return ArrayRef<uint8_t>();
883       return ArrayRef<uint8_t>(
884           reinterpret_cast<const uint8_t *>(_content.data()), _content.size());
885     }
886 
ordinal() const887     uint64_t ordinal() const override { return _ordinal; }
888 
begin() const889     reference_iterator begin() const override {
890       uintptr_t index = 0;
891       const void *it = reinterpret_cast<const void *>(index);
892       return reference_iterator(*this, it);
893     }
end() const894     reference_iterator end() const override {
895       uintptr_t index = _references.size();
896       const void *it = reinterpret_cast<const void *>(index);
897       return reference_iterator(*this, it);
898     }
derefIterator(const void * it) const899     const lld::Reference *derefIterator(const void *it) const override {
900       uintptr_t index = reinterpret_cast<uintptr_t>(it);
901       assert(index < _references.size());
902       return _references[index];
903     }
incrementIterator(const void * & it) const904     void incrementIterator(const void *&it) const override {
905       uintptr_t index = reinterpret_cast<uintptr_t>(it);
906       ++index;
907       it = reinterpret_cast<const void *>(index);
908     }
909 
addReference(Reference::KindNamespace ns,Reference::KindArch arch,Reference::KindValue kindValue,uint64_t off,const Atom * target,Reference::Addend a)910     void addReference(Reference::KindNamespace ns,
911                       Reference::KindArch arch,
912                       Reference::KindValue kindValue, uint64_t off,
913                       const Atom *target, Reference::Addend a) override {
914       assert(target && "trying to create reference to nothing");
915       auto node = new (file().allocator()) SimpleReference(ns, arch, kindValue,
916                                                            off, target, a);
917       _references.push_back(node);
918     }
919 
920     const lld::File                    &_file;
921     StringRef                           _name;
922     StringRef                           _refName;
923     Scope                               _scope;
924     Interposable                        _interpose;
925     Merge                               _merge;
926     ContentType                         _contentType;
927     Alignment                           _alignment;
928     SectionChoice                       _sectionChoice;
929     DeadStripKind                       _deadStrip;
930     DynamicExport                       _dynamicExport;
931     CodeModel                           _codeModel;
932     ContentPermissions                  _permissions;
933     uint32_t                            _ordinal;
934     std::vector<ImplicitHex8>           _content;
935     uint64_t                            _size;
936     StringRef                           _sectionName;
937     uint64_t                            _sectionSize;
938     std::vector<const lld::Reference *> _references;
939   };
940 
mappingllvm::yaml::MappingTraits941   static void mapping(IO &io, const lld::DefinedAtom *&atom) {
942     YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext());
943     MappingNormalizationHeap<NormalizedAtom, const lld::DefinedAtom *> keys(
944         io, atom, &info->_file->allocator());
945     if (io.outputting()) {
946       // If writing YAML, check if atom needs a ref-name.
947       typedef MappingTraits<const lld::File *>::NormalizedFile NormalizedFile;
948       assert(info != nullptr);
949       NormalizedFile *f = reinterpret_cast<NormalizedFile *>(info->_file);
950       assert(f);
951       assert(f->_rnb);
952       if (f->_rnb->hasRefName(atom)) {
953         keys->_refName = f->_rnb->refName(atom);
954       }
955     }
956 
957     io.mapOptional("name",             keys->_name,    StringRef());
958     io.mapOptional("ref-name",         keys->_refName, StringRef());
959     io.mapOptional("scope",            keys->_scope,
960                                          DefinedAtom::scopeTranslationUnit);
961     io.mapOptional("type",             keys->_contentType,
962                                          DefinedAtom::typeCode);
963     io.mapOptional("content",          keys->_content);
964     io.mapOptional("size",             keys->_size, (uint64_t)keys->_content.size());
965     io.mapOptional("interposable",     keys->_interpose,
966                                          DefinedAtom::interposeNo);
967     io.mapOptional("merge",            keys->_merge, DefinedAtom::mergeNo);
968     io.mapOptional("alignment",        keys->_alignment,
969                                          DefinedAtom::Alignment(1));
970     io.mapOptional("section-choice",   keys->_sectionChoice,
971                                          DefinedAtom::sectionBasedOnContent);
972     io.mapOptional("section-name",     keys->_sectionName, StringRef());
973     io.mapOptional("section-size",     keys->_sectionSize, (uint64_t)0);
974     io.mapOptional("dead-strip",       keys->_deadStrip,
975                                          DefinedAtom::deadStripNormal);
976     io.mapOptional("dynamic-export",   keys->_dynamicExport,
977                                          DefinedAtom::dynamicExportNormal);
978     io.mapOptional("code-model",       keys->_codeModel, DefinedAtom::codeNA);
979     // default permissions based on content type
980     io.mapOptional("permissions",      keys->_permissions,
981                                          DefinedAtom::permissions(
982                                                           keys->_contentType));
983     io.mapOptional("references",       keys->_references);
984   }
985 };
986 
987 template <> struct MappingTraits<lld::DefinedAtom *> {
mappingllvm::yaml::MappingTraits988   static void mapping(IO &io, lld::DefinedAtom *&atom) {
989     const lld::DefinedAtom *atomPtr = atom;
990     MappingTraits<const lld::DefinedAtom *>::mapping(io, atomPtr);
991     atom = const_cast<lld::DefinedAtom *>(atomPtr);
992   }
993 };
994 
995 // YAML conversion for const lld::UndefinedAtom*
996 template <> struct MappingTraits<const lld::UndefinedAtom *> {
997   class NormalizedAtom : public lld::UndefinedAtom {
998   public:
NormalizedAtom(IO & io)999     NormalizedAtom(IO &io)
1000         : _file(fileFromContext(io)), _canBeNull(canBeNullNever) {}
1001 
NormalizedAtom(IO & io,const lld::UndefinedAtom * atom)1002     NormalizedAtom(IO &io, const lld::UndefinedAtom *atom)
1003         : _file(fileFromContext(io)), _name(atom->name()),
1004           _canBeNull(atom->canBeNull()) {}
1005 
1006     ~NormalizedAtom() override = default;
1007 
denormalize(IO & io)1008     const lld::UndefinedAtom *denormalize(IO &io) {
1009       YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext());
1010       assert(info != nullptr);
1011       typedef MappingTraits<const lld::File *>::NormalizedFile NormalizedFile;
1012       NormalizedFile *f = reinterpret_cast<NormalizedFile *>(info->_file);
1013       if (!_name.empty())
1014         _name = f->copyString(_name);
1015 
1016       DEBUG_WITH_TYPE("WriterYAML",
1017                       llvm::dbgs() << "created UndefinedAtom named: '" << _name
1018                       << "' (" << (const void *)_name.data() << ", "
1019                       << _name.size() << ")\n");
1020       return this;
1021     }
1022 
1023     // Extract current File object from YAML I/O parsing context
fileFromContext(IO & io)1024     const lld::File &fileFromContext(IO &io) {
1025       YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext());
1026       assert(info != nullptr);
1027       assert(info->_file != nullptr);
1028       return *info->_file;
1029     }
1030 
file() const1031     const lld::File &file() const override { return _file; }
name() const1032     StringRef name() const override { return _name; }
canBeNull() const1033     CanBeNull canBeNull() const override { return _canBeNull; }
1034 
1035     const lld::File     &_file;
1036     StringRef            _name;
1037     CanBeNull            _canBeNull;
1038   };
1039 
mappingllvm::yaml::MappingTraits1040   static void mapping(IO &io, const lld::UndefinedAtom *&atom) {
1041     YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext());
1042     MappingNormalizationHeap<NormalizedAtom, const lld::UndefinedAtom *> keys(
1043         io, atom, &info->_file->allocator());
1044 
1045     io.mapRequired("name",        keys->_name);
1046     io.mapOptional("can-be-null", keys->_canBeNull,
1047                                   lld::UndefinedAtom::canBeNullNever);
1048   }
1049 };
1050 
1051 template <> struct MappingTraits<lld::UndefinedAtom *> {
mappingllvm::yaml::MappingTraits1052   static void mapping(IO &io, lld::UndefinedAtom *&atom) {
1053     const lld::UndefinedAtom *atomPtr = atom;
1054     MappingTraits<const lld::UndefinedAtom *>::mapping(io, atomPtr);
1055     atom = const_cast<lld::UndefinedAtom *>(atomPtr);
1056   }
1057 };
1058 
1059 // YAML conversion for const lld::SharedLibraryAtom*
1060 template <> struct MappingTraits<const lld::SharedLibraryAtom *> {
1061   class NormalizedAtom : public lld::SharedLibraryAtom {
1062   public:
NormalizedAtom(IO & io)1063     NormalizedAtom(IO &io)
1064         : _file(fileFromContext(io)), _canBeNull(false),
1065           _type(Type::Unknown), _size(0) {}
1066 
NormalizedAtom(IO & io,const lld::SharedLibraryAtom * atom)1067     NormalizedAtom(IO &io, const lld::SharedLibraryAtom *atom)
1068         : _file(fileFromContext(io)), _name(atom->name()),
1069           _loadName(atom->loadName()), _canBeNull(atom->canBeNullAtRuntime()),
1070           _type(atom->type()), _size(atom->size()) {}
1071 
1072     ~NormalizedAtom() override = default;
1073 
denormalize(IO & io)1074     const lld::SharedLibraryAtom *denormalize(IO &io) {
1075       YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext());
1076       assert(info != nullptr);
1077       typedef MappingTraits<const lld::File *>::NormalizedFile NormalizedFile;
1078       NormalizedFile *f = reinterpret_cast<NormalizedFile *>(info->_file);
1079       if (!_name.empty())
1080         _name = f->copyString(_name);
1081       if (!_loadName.empty())
1082         _loadName = f->copyString(_loadName);
1083 
1084       DEBUG_WITH_TYPE("WriterYAML",
1085                       llvm::dbgs() << "created SharedLibraryAtom named: '"
1086                                    << _name << "' ("
1087                                    << (const void *)_name.data()
1088                                    << ", " << _name.size() << ")\n");
1089       return this;
1090     }
1091 
1092     // Extract current File object from YAML I/O parsing context
fileFromContext(IO & io)1093     const lld::File &fileFromContext(IO &io) {
1094       YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext());
1095       assert(info != nullptr);
1096       assert(info->_file != nullptr);
1097       return *info->_file;
1098     }
1099 
file() const1100     const lld::File &file() const override { return _file; }
name() const1101     StringRef name() const override { return _name; }
loadName() const1102     StringRef loadName() const override { return _loadName; }
canBeNullAtRuntime() const1103     bool canBeNullAtRuntime() const override { return _canBeNull; }
type() const1104     Type type() const override { return _type; }
size() const1105     uint64_t size() const override { return _size; }
1106 
1107     const lld::File &_file;
1108     StringRef        _name;
1109     StringRef        _loadName;
1110     ShlibCanBeNull   _canBeNull;
1111     Type             _type;
1112     uint64_t         _size;
1113   };
1114 
mappingllvm::yaml::MappingTraits1115   static void mapping(IO &io, const lld::SharedLibraryAtom *&atom) {
1116 
1117     YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext());
1118     MappingNormalizationHeap<NormalizedAtom, const lld::SharedLibraryAtom *>
1119     keys(io, atom, &info->_file->allocator());
1120 
1121     io.mapRequired("name",        keys->_name);
1122     io.mapOptional("load-name",   keys->_loadName);
1123     io.mapOptional("can-be-null", keys->_canBeNull, (ShlibCanBeNull) false);
1124     io.mapOptional("type",        keys->_type, SharedLibraryAtom::Type::Code);
1125     io.mapOptional("size",        keys->_size, uint64_t(0));
1126   }
1127 };
1128 
1129 template <> struct MappingTraits<lld::SharedLibraryAtom *> {
mappingllvm::yaml::MappingTraits1130   static void mapping(IO &io, lld::SharedLibraryAtom *&atom) {
1131     const lld::SharedLibraryAtom *atomPtr = atom;
1132     MappingTraits<const lld::SharedLibraryAtom *>::mapping(io, atomPtr);
1133     atom = const_cast<lld::SharedLibraryAtom *>(atomPtr);
1134   }
1135 };
1136 
1137 // YAML conversion for const lld::AbsoluteAtom*
1138 template <> struct MappingTraits<const lld::AbsoluteAtom *> {
1139   class NormalizedAtom : public lld::AbsoluteAtom {
1140   public:
NormalizedAtom(IO & io)1141     NormalizedAtom(IO &io)
1142         : _file(fileFromContext(io)), _scope(), _value(0) {}
1143 
NormalizedAtom(IO & io,const lld::AbsoluteAtom * atom)1144     NormalizedAtom(IO &io, const lld::AbsoluteAtom *atom)
1145         : _file(fileFromContext(io)), _name(atom->name()),
1146           _scope(atom->scope()), _value(atom->value()) {}
1147 
1148     ~NormalizedAtom() override = default;
1149 
denormalize(IO & io)1150     const lld::AbsoluteAtom *denormalize(IO &io) {
1151       YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext());
1152       assert(info != nullptr);
1153       typedef MappingTraits<const lld::File *>::NormalizedFile NormalizedFile;
1154       NormalizedFile *f = reinterpret_cast<NormalizedFile *>(info->_file);
1155       if (!_name.empty())
1156         _name = f->copyString(_name);
1157 
1158       DEBUG_WITH_TYPE("WriterYAML",
1159                       llvm::dbgs() << "created AbsoluteAtom named: '" << _name
1160                                    << "' (" << (const void *)_name.data()
1161                                    << ", " << _name.size() << ")\n");
1162       return this;
1163     }
1164 
1165     // Extract current File object from YAML I/O parsing context
fileFromContext(IO & io)1166     const lld::File &fileFromContext(IO &io) {
1167       YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext());
1168       assert(info != nullptr);
1169       assert(info->_file != nullptr);
1170       return *info->_file;
1171     }
1172 
file() const1173     const lld::File &file() const override { return _file; }
name() const1174     StringRef name() const override { return _name; }
value() const1175     uint64_t value() const override { return _value; }
scope() const1176     Scope scope() const override { return _scope; }
1177 
1178     const lld::File &_file;
1179     StringRef        _name;
1180     StringRef        _refName;
1181     Scope            _scope;
1182     Hex64            _value;
1183   };
1184 
mappingllvm::yaml::MappingTraits1185   static void mapping(IO &io, const lld::AbsoluteAtom *&atom) {
1186     YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext());
1187     MappingNormalizationHeap<NormalizedAtom, const lld::AbsoluteAtom *> keys(
1188         io, atom, &info->_file->allocator());
1189 
1190     if (io.outputting()) {
1191       typedef MappingTraits<const lld::File *>::NormalizedFile NormalizedFile;
1192       YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext());
1193       assert(info != nullptr);
1194       NormalizedFile *f = reinterpret_cast<NormalizedFile *>(info->_file);
1195       assert(f);
1196       assert(f->_rnb);
1197       if (f->_rnb->hasRefName(atom)) {
1198         keys->_refName = f->_rnb->refName(atom);
1199       }
1200     }
1201 
1202     io.mapRequired("name",     keys->_name);
1203     io.mapOptional("ref-name", keys->_refName, StringRef());
1204     io.mapOptional("scope",    keys->_scope);
1205     io.mapRequired("value",    keys->_value);
1206   }
1207 };
1208 
1209 template <> struct MappingTraits<lld::AbsoluteAtom *> {
mappingllvm::yaml::MappingTraits1210   static void mapping(IO &io, lld::AbsoluteAtom *&atom) {
1211     const lld::AbsoluteAtom *atomPtr = atom;
1212     MappingTraits<const lld::AbsoluteAtom *>::mapping(io, atomPtr);
1213     atom = const_cast<lld::AbsoluteAtom *>(atomPtr);
1214   }
1215 };
1216 
1217 } // end namespace llvm
1218 } // end namespace yaml
1219 
RefNameResolver(const lld::File * file,IO & io)1220 RefNameResolver::RefNameResolver(const lld::File *file, IO &io) : _io(io) {
1221   typedef MappingTraits<const lld::DefinedAtom *>::NormalizedAtom
1222   NormalizedAtom;
1223   for (const lld::DefinedAtom *a : file->defined()) {
1224     const auto *na = (const NormalizedAtom *)a;
1225     if (!na->_refName.empty())
1226       add(na->_refName, a);
1227     else if (!na->_name.empty())
1228       add(na->_name, a);
1229   }
1230 
1231   for (const lld::UndefinedAtom *a : file->undefined())
1232     add(a->name(), a);
1233 
1234   for (const lld::SharedLibraryAtom *a : file->sharedLibrary())
1235     add(a->name(), a);
1236 
1237   typedef MappingTraits<const lld::AbsoluteAtom *>::NormalizedAtom NormAbsAtom;
1238   for (const lld::AbsoluteAtom *a : file->absolute()) {
1239     const auto *na = (const NormAbsAtom *)a;
1240     if (na->_refName.empty())
1241       add(na->_name, a);
1242     else
1243       add(na->_refName, a);
1244   }
1245 }
1246 
1247 inline const lld::File *
denormalize(IO & io)1248 MappingTraits<const lld::File *>::NormalizedFile::denormalize(IO &io) {
1249   typedef MappingTraits<const lld::DefinedAtom *>::NormalizedAtom
1250   NormalizedAtom;
1251 
1252   RefNameResolver nameResolver(this, io);
1253   // Now that all atoms are parsed, references can be bound.
1254   for (const lld::DefinedAtom *a : this->defined()) {
1255     auto *normAtom = (NormalizedAtom *)const_cast<DefinedAtom *>(a);
1256     normAtom->bind(nameResolver);
1257   }
1258 
1259   return this;
1260 }
1261 
bind(const RefNameResolver & resolver)1262 inline void MappingTraits<const lld::DefinedAtom *>::NormalizedAtom::bind(
1263     const RefNameResolver &resolver) {
1264   typedef MappingTraits<const lld::Reference *>::NormalizedReference
1265   NormalizedReference;
1266   for (const lld::Reference *ref : _references) {
1267     auto *normRef = (NormalizedReference *)const_cast<Reference *>(ref);
1268     normRef->bind(resolver);
1269   }
1270 }
1271 
bind(const RefNameResolver & resolver)1272 inline void MappingTraits<const lld::Reference *>::NormalizedReference::bind(
1273     const RefNameResolver &resolver) {
1274   _target = resolver.lookup(_targetName);
1275 }
1276 
1277 inline StringRef
targetName(IO & io,const lld::Reference * ref)1278 MappingTraits<const lld::Reference *>::NormalizedReference::targetName(
1279     IO &io, const lld::Reference *ref) {
1280   if (ref->target() == nullptr)
1281     return StringRef();
1282   YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext());
1283   assert(info != nullptr);
1284   typedef MappingTraits<const lld::File *>::NormalizedFile NormalizedFile;
1285   NormalizedFile *f = reinterpret_cast<NormalizedFile *>(info->_file);
1286   RefNameBuilder &rnb = *f->_rnb;
1287   if (rnb.hasRefName(ref->target()))
1288     return rnb.refName(ref->target());
1289   return ref->target()->name();
1290 }
1291 
1292 namespace lld {
1293 namespace yaml {
1294 
1295 class Writer : public lld::Writer {
1296 public:
Writer(const LinkingContext & context)1297   Writer(const LinkingContext &context) : _ctx(context) {}
1298 
writeFile(const lld::File & file,StringRef outPath)1299   llvm::Error writeFile(const lld::File &file, StringRef outPath) override {
1300     // Create stream to path.
1301     std::error_code ec;
1302     llvm::raw_fd_ostream out(outPath, ec, llvm::sys::fs::OF_TextWithCRLF);
1303     if (ec)
1304       return llvm::errorCodeToError(ec);
1305 
1306     // Create yaml Output writer, using yaml options for context.
1307     YamlContext yamlContext;
1308     yamlContext._ctx = &_ctx;
1309     yamlContext._registry = &_ctx.registry();
1310     llvm::yaml::Output yout(out, &yamlContext);
1311 
1312     // Write yaml output.
1313     const lld::File *fileRef = &file;
1314     yout << fileRef;
1315 
1316     return llvm::Error::success();
1317   }
1318 
1319 private:
1320   const LinkingContext &_ctx;
1321 };
1322 
1323 } // end namespace yaml
1324 
1325 namespace {
1326 
1327 /// Handles !native tagged yaml documents.
1328 class NativeYamlIOTaggedDocumentHandler : public YamlIOTaggedDocumentHandler {
handledDocTag(llvm::yaml::IO & io,const lld::File * & file) const1329   bool handledDocTag(llvm::yaml::IO &io, const lld::File *&file) const override {
1330     if (io.mapTag("!native")) {
1331       MappingTraits<const lld::File *>::mappingAtoms(io, file);
1332       return true;
1333     }
1334     return false;
1335   }
1336 };
1337 
1338 /// Handles !archive tagged yaml documents.
1339 class ArchiveYamlIOTaggedDocumentHandler : public YamlIOTaggedDocumentHandler {
handledDocTag(llvm::yaml::IO & io,const lld::File * & file) const1340   bool handledDocTag(llvm::yaml::IO &io, const lld::File *&file) const override {
1341     if (io.mapTag("!archive")) {
1342       MappingTraits<const lld::File *>::mappingArchive(io, file);
1343       return true;
1344     }
1345     return false;
1346   }
1347 };
1348 
1349 class YAMLReader : public Reader {
1350 public:
YAMLReader(const Registry & registry)1351   YAMLReader(const Registry &registry) : _registry(registry) {}
1352 
canParse(file_magic magic,MemoryBufferRef mb) const1353   bool canParse(file_magic magic, MemoryBufferRef mb) const override {
1354     StringRef name = mb.getBufferIdentifier();
1355     return name.endswith(".objtxt") || name.endswith(".yaml");
1356   }
1357 
1358   ErrorOr<std::unique_ptr<File>>
loadFile(std::unique_ptr<MemoryBuffer> mb,const class Registry &) const1359   loadFile(std::unique_ptr<MemoryBuffer> mb,
1360            const class Registry &) const override {
1361     // Create YAML Input Reader.
1362     YamlContext yamlContext;
1363     yamlContext._registry = &_registry;
1364     yamlContext._path = mb->getBufferIdentifier();
1365     llvm::yaml::Input yin(mb->getBuffer(), &yamlContext);
1366 
1367     // Fill vector with File objects created by parsing yaml.
1368     std::vector<const lld::File *> createdFiles;
1369     yin >> createdFiles;
1370     assert(createdFiles.size() == 1);
1371 
1372     // Error out now if there were parsing errors.
1373     if (yin.error())
1374       return make_error_code(lld::YamlReaderError::illegal_value);
1375 
1376     std::shared_ptr<MemoryBuffer> smb(mb.release());
1377     const File *file = createdFiles[0];
1378     // Note: loadFile() should return vector of *const* File
1379     File *f = const_cast<File *>(file);
1380     f->setLastError(std::error_code());
1381     f->setSharedMemoryBuffer(smb);
1382     return std::unique_ptr<File>(f);
1383   }
1384 
1385 private:
1386   const Registry &_registry;
1387 };
1388 
1389 } // end anonymous namespace
1390 
addSupportYamlFiles()1391 void Registry::addSupportYamlFiles() {
1392   add(std::unique_ptr<Reader>(new YAMLReader(*this)));
1393   add(std::unique_ptr<YamlIOTaggedDocumentHandler>(
1394                                     new NativeYamlIOTaggedDocumentHandler()));
1395   add(std::unique_ptr<YamlIOTaggedDocumentHandler>(
1396                                     new ArchiveYamlIOTaggedDocumentHandler()));
1397 }
1398 
createWriterYAML(const LinkingContext & context)1399 std::unique_ptr<Writer> createWriterYAML(const LinkingContext &context) {
1400   return std::unique_ptr<Writer>(new lld::yaml::Writer(context));
1401 }
1402 
1403 } // end namespace lld
1404