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_Text);
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 ®istry) : _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