blob: c38e735b4643724985388edec192e6c319377cce [file] [log] [blame]
Michael J. Spencer773a8fb2011-12-18 08:27:59 +00001//===- Core/YamlWriter.cpp - Writes YAML ----------------------------------===//
2//
3// The LLVM Linker
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9
Nick Kledzik7735a7d2012-01-04 23:58:17 +000010#include "YamlKeyValues.h"
11
Michael J. Spencer773a8fb2011-12-18 08:27:59 +000012#include "lld/Core/YamlWriter.h"
13#include "lld/Core/Atom.h"
14#include "lld/Core/File.h"
15#include "lld/Core/Reference.h"
16
17#include "llvm/ADT/OwningPtr.h"
Nick Kledzik49d6cc82012-02-15 00:38:09 +000018#include "llvm/ADT/DenseMap.h"
19#include "llvm/ADT/StringExtras.h"
Nick Kledzikbfedfc12012-01-09 20:18:15 +000020#include "llvm/ADT/ArrayRef.h"
21#include "llvm/ADT/StringExtras.h"
Michael J. Spencer773a8fb2011-12-18 08:27:59 +000022#include "llvm/Support/DataTypes.h"
23#include "llvm/Support/MemoryBuffer.h"
24#include "llvm/Support/system_error.h"
25
26#include <vector>
27
28namespace lld {
29namespace yaml {
30
Nick Kledzik49d6cc82012-02-15 00:38:09 +000031namespace {
32///
33/// In most cases, atoms names are unambiguous, so references can just
34/// use the atom name as the target (e.g. target: foo). But in a few
35/// cases that does not work, so ref-names are added. These are labels
36/// used only in yaml. The labels do not exist in the Atom model.
37///
38/// One need for ref-names are when atoms have no user supplied name
39/// (e.g. c-string literal). Another case is when two object files with
40/// identically named static functions are merged (ld -r) into one object file.
41/// In that case referencing the function by name is ambiguous, so a unique
42/// ref-name is added.
43///
44class RefNameBuilder : public File::AtomHandler,
45 public DefinedAtom::ReferenceHandler {
Michael J. Spencer773a8fb2011-12-18 08:27:59 +000046public:
Nick Kledzik49d6cc82012-02-15 00:38:09 +000047 RefNameBuilder() { }
48
49 virtual void doReference(const Reference& ref) {
50 // create refname for any unnamed reference target
51 if ( ref.target()->name().empty() ) {
52 char* buffer;
53 asprintf(&buffer, "L%03d", _unnamedCounter++);
54 _refNames[ref.target()] = buffer;
55 }
56 }
57
58 virtual void doFile(const File &) { }
59
60 virtual void doDefinedAtom(const DefinedAtom& atom) {
61 // Build map of atoms names to detect duplicates
62 if ( ! atom.name().empty() )
63 buildDuplicateNameMap(atom);
64
65 // Find references to unnamed atoms and create ref-names for them.
66 _unnamedCounter = 0;
67 atom.forEachReference(*this);
68 }
69
70 virtual void doUndefinedAtom(const UndefinedAtom& atom) {
71 buildDuplicateNameMap(atom);
72 }
73
74 void buildDuplicateNameMap(const Atom& atom) {
75 assert(!atom.name().empty());
76 NameToAtom::iterator pos = _nameMap.find(atom.name());
77 if ( pos != _nameMap.end() ) {
78 // Found name collision, give each a unique ref-name.
79 char* buffer;
80 asprintf(&buffer, "%s.%03d", atom.name().data(), ++_collisionCount);
81 _refNames[&atom] = buffer;
82 const Atom* prevAtom = pos->second;
83 AtomToRefName::iterator pos2 = _refNames.find(prevAtom);
84 if ( pos2 == _refNames.end() ) {
85 // only create ref-name for previous if none already created
86 asprintf(&buffer, "%s.%03d", prevAtom->name().data(), ++_collisionCount);
87 _refNames[prevAtom] = buffer;
88 }
89 }
90 else {
91 // First time we've seen this name, just add it to map.
92 _nameMap[atom.name()] = &atom;
93 }
94 }
95
96 bool hasRefName(const Atom* atom) {
97 return _refNames.count(atom);
98 }
99
100 const char* refName(const Atom* atom) {
101 return _refNames.find(atom)->second;
102 }
103
104private:
105 struct MyMappingInfo {
106 static llvm::StringRef getEmptyKey() { return llvm::StringRef(); }
107 static llvm::StringRef getTombstoneKey() { return llvm::StringRef(" ", 0); }
108 static unsigned getHashValue(llvm::StringRef const val) {
109 return llvm::HashString(val); }
110 static bool isEqual(llvm::StringRef const lhs,
111 llvm::StringRef const rhs) { return lhs.equals(rhs); }
112 };
113 typedef llvm::DenseMap<llvm::StringRef, const Atom*, MyMappingInfo> NameToAtom;
114 typedef llvm::DenseMap<const Atom*, const char*> AtomToRefName;
115
116 unsigned int _collisionCount;
117 unsigned int _unnamedCounter;
118 NameToAtom _nameMap;
119 AtomToRefName _refNames;
120};
121
122
123///
124/// Helper class for writeObjectText() to write out atoms in yaml format.
125///
126class AtomWriter : public File::AtomHandler,
127 public DefinedAtom::ReferenceHandler {
128public:
129 AtomWriter(RefNameBuilder& rnb, llvm::raw_ostream &out)
130 : _out(out), _rnb(rnb), _firstAtom(true) { }
Michael J. Spencer773a8fb2011-12-18 08:27:59 +0000131
Nick Kledzik7735a7d2012-01-04 23:58:17 +0000132 virtual void doFile(const class File &) { _firstAtom = true; }
133
Nick Kledzikf4fb2c52012-01-11 01:06:19 +0000134 virtual void doDefinedAtom(const class DefinedAtom &atom) {
Nick Kledzik49d6cc82012-02-15 00:38:09 +0000135 // add blank line between atoms for readability
136 if ( !_firstAtom )
137 _out << "\n";
138 _firstAtom = false;
139
140 bool hasDash = false;
141 if ( !atom.name().empty() ) {
Nick Kledzik7735a7d2012-01-04 23:58:17 +0000142 _out << " - "
143 << KeyValues::nameKeyword
144 << ":"
145 << spacePadding(KeyValues::nameKeyword)
146 << atom.name()
147 << "\n";
Nick Kledzik49d6cc82012-02-15 00:38:09 +0000148 hasDash = true;
149 }
150
151 if ( _rnb.hasRefName(&atom) ) {
152 _out << (hasDash ? " " : " - ")
153 << KeyValues::refNameKeyword
Nick Kledzik7735a7d2012-01-04 23:58:17 +0000154 << ":"
Nick Kledzik49d6cc82012-02-15 00:38:09 +0000155 << spacePadding(KeyValues::refNameKeyword)
156 << _rnb.refName(&atom)
Nick Kledzik7735a7d2012-01-04 23:58:17 +0000157 << "\n";
Nick Kledzik49d6cc82012-02-15 00:38:09 +0000158 hasDash = true;
Nick Kledzik7735a7d2012-01-04 23:58:17 +0000159 }
Nick Kledzik38eec3d2011-12-22 02:38:01 +0000160
Nick Kledzik7735a7d2012-01-04 23:58:17 +0000161 if ( atom.definition() != KeyValues::definitionDefault ) {
Nick Kledzik49d6cc82012-02-15 00:38:09 +0000162 _out << (hasDash ? " " : " - ")
Nick Kledzik7735a7d2012-01-04 23:58:17 +0000163 << KeyValues::definitionKeyword
164 << ":"
165 << spacePadding(KeyValues::definitionKeyword)
166 << KeyValues::definition(atom.definition())
167 << "\n";
Nick Kledzik49d6cc82012-02-15 00:38:09 +0000168 hasDash = true;
Nick Kledzik7735a7d2012-01-04 23:58:17 +0000169 }
170
171 if ( atom.scope() != KeyValues::scopeDefault ) {
Nick Kledzik49d6cc82012-02-15 00:38:09 +0000172 _out << (hasDash ? " " : " - ")
Nick Kledzik7735a7d2012-01-04 23:58:17 +0000173 << KeyValues::scopeKeyword
174 << ":"
175 << spacePadding(KeyValues::scopeKeyword)
176 << KeyValues::scope(atom.scope())
177 << "\n";
Nick Kledzik49d6cc82012-02-15 00:38:09 +0000178 hasDash = true;
Nick Kledzik7735a7d2012-01-04 23:58:17 +0000179 }
180
Nick Kledzikf4fb2c52012-01-11 01:06:19 +0000181 if ( atom.interposable() != KeyValues::interposableDefault ) {
182 _out << " "
183 << KeyValues::interposableKeyword
184 << ":"
185 << spacePadding(KeyValues::interposableKeyword)
186 << KeyValues::interposable(atom.interposable())
187 << "\n";
188 }
189
Nick Kledzik23384e82012-02-07 02:59:54 +0000190 if ( atom.merge() != KeyValues::mergeDefault ) {
Nick Kledzikf4fb2c52012-01-11 01:06:19 +0000191 _out << " "
192 << KeyValues::mergeKeyword
193 << ":"
194 << spacePadding(KeyValues::mergeKeyword)
195 << KeyValues::merge(atom.merge())
196 << "\n";
197 }
198
Nick Kledzik7735a7d2012-01-04 23:58:17 +0000199 if ( atom.contentType() != KeyValues::contentTypeDefault ) {
200 _out << " "
201 << KeyValues::contentTypeKeyword
202 << ":"
203 << spacePadding(KeyValues::contentTypeKeyword)
204 << KeyValues::contentType(atom.contentType())
205 << "\n";
206 }
207
208 if ( atom.deadStrip() != KeyValues::deadStripKindDefault ) {
209 _out << " "
210 << KeyValues::deadStripKindKeyword
211 << ":"
212 << spacePadding(KeyValues::deadStripKindKeyword)
213 << KeyValues::deadStripKind(atom.deadStrip())
214 << "\n";
215 }
216
217 if ( atom.sectionChoice() != KeyValues::sectionChoiceDefault ) {
218 _out << " "
219 << KeyValues::sectionChoiceKeyword
220 << ":"
221 << spacePadding(KeyValues::sectionChoiceKeyword)
222 << KeyValues::sectionChoice(atom.sectionChoice())
223 << "\n";
224 assert( ! atom.customSectionName().empty() );
225 _out << " "
226 << KeyValues::sectionNameKeyword
227 << ":"
228 << spacePadding(KeyValues::sectionNameKeyword)
229 << atom.customSectionName()
230 << "\n";
231 }
232
Nick Kledzik23384e82012-02-07 02:59:54 +0000233 if ( atom.isThumb() != KeyValues::isThumbDefault ) {
Nick Kledzik7735a7d2012-01-04 23:58:17 +0000234 _out << " "
235 << KeyValues::isThumbKeyword
236 << ":"
237 << spacePadding(KeyValues::isThumbKeyword)
238 << KeyValues::isThumb(atom.isThumb())
239 << "\n";
240 }
241
242 if ( atom.isAlias() != KeyValues::isAliasDefault ) {
243 _out << " "
244 << KeyValues::isAliasKeyword
245 << ":"
246 << spacePadding(KeyValues::isAliasKeyword)
247 << KeyValues::isAlias(atom.isAlias())
248 << "\n";
249 }
250
Nick Kledzik23384e82012-02-07 02:59:54 +0000251 if ( (atom.contentType() != DefinedAtom::typeZeroFill)
252 && (atom.size() != 0) ) {
Nick Kledzikbfedfc12012-01-09 20:18:15 +0000253 _out << " "
254 << KeyValues::contentKeyword
255 << ":"
256 << spacePadding(KeyValues::contentKeyword)
257 << "[ ";
258 llvm::ArrayRef<uint8_t> arr = atom.rawContent();
259 bool needComma = false;
260 for (unsigned int i=0; i < arr.size(); ++i) {
261 if ( needComma )
262 _out << ", ";
263 _out << hexdigit(arr[i] >> 4);
264 _out << hexdigit(arr[i] & 0x0F);
265 needComma = true;
266 }
267 _out << " ]\n";
268 }
269
Nick Kledzik49d6cc82012-02-15 00:38:09 +0000270 _wroteFirstFixup = false;
271 atom.forEachReference(*this);
Michael J. Spencer773a8fb2011-12-18 08:27:59 +0000272 }
Nick Kledzik49d6cc82012-02-15 00:38:09 +0000273
274 virtual void doReference(const Reference& ref) {
275 if ( !_wroteFirstFixup ) {
276 _out << " fixups:\n";
277 _wroteFirstFixup = true;
278 }
279 _out << " - "
280 << KeyValues::fixupsOffsetKeyword
281 << ":"
282 << spacePadding(KeyValues::fixupsOffsetKeyword)
283 << ref.offsetInAtom()
284 << "\n";
285 _out << " "
286 << KeyValues::fixupsKindKeyword
287 << ":"
288 << spacePadding(KeyValues::fixupsKindKeyword)
289 << ref.kind()
290 << "\n";
291 const Atom* target = ref.target();
292 if ( target != NULL ) {
293 llvm::StringRef refName = target->name();
294 if ( _rnb.hasRefName(target) )
295 refName = _rnb.refName(target);
296 assert(!refName.empty());
297 _out << " "
298 << KeyValues::fixupsTargetKeyword
299 << ":"
300 << spacePadding(KeyValues::fixupsTargetKeyword)
301 << refName
302 << "\n";
303 }
304 if ( ref.addend() != 0 ) {
305 _out << " "
306 << KeyValues::fixupsAddendKeyword
307 << ":"
308 << spacePadding(KeyValues::fixupsAddendKeyword)
309 << ref.addend()
310 << "\n";
311 }
312 }
313
Michael J. Spencer773a8fb2011-12-18 08:27:59 +0000314
Nick Kledzik23384e82012-02-07 02:59:54 +0000315 virtual void doUndefinedAtom(const class UndefinedAtom &atom) {
316 // add blank line between atoms for readability
317 if ( !_firstAtom )
318 _out << "\n";
319 _firstAtom = false;
320
321 _out << " - "
322 << KeyValues::nameKeyword
323 << ":"
324 << spacePadding(KeyValues::nameKeyword)
325 << atom.name()
326 << "\n";
Nick Kledzikf4fb2c52012-01-11 01:06:19 +0000327
Nick Kledzik23384e82012-02-07 02:59:54 +0000328 _out << " "
329 << KeyValues::definitionKeyword
330 << ":"
331 << spacePadding(KeyValues::definitionKeyword)
332 << KeyValues::definition(atom.definition())
333 << "\n";
334
335 if ( atom.weakImport() != KeyValues::weakImportDefault ) {
336 _out << " "
337 << KeyValues::weakImportKeyword
338 << ":"
339 << spacePadding(KeyValues::weakImportKeyword)
340 << KeyValues::weakImport(atom.weakImport())
341 << "\n";
342 }
343 }
Nick Kledzikf4fb2c52012-01-11 01:06:19 +0000344
345
Michael J. Spencer773a8fb2011-12-18 08:27:59 +0000346private:
Nick Kledzik7735a7d2012-01-04 23:58:17 +0000347 // return a string of the correct number of spaces to align value
348 const char* spacePadding(const char* key) {
349 const char* spaces = " ";
350 assert(strlen(spaces) > strlen(key));
351 return &spaces[strlen(key)];
Michael J. Spencer773a8fb2011-12-18 08:27:59 +0000352 }
353
Nick Kledzikbfedfc12012-01-09 20:18:15 +0000354 char hexdigit(uint8_t nibble) {
355 if ( nibble < 0x0A )
356 return '0' + nibble;
357 else
358 return 'A' + nibble - 0x0A;
359 }
Michael J. Spencer773a8fb2011-12-18 08:27:59 +0000360
Nick Kledzik7735a7d2012-01-04 23:58:17 +0000361 llvm::raw_ostream& _out;
Nick Kledzik49d6cc82012-02-15 00:38:09 +0000362 RefNameBuilder _rnb;
Nick Kledzik7735a7d2012-01-04 23:58:17 +0000363 bool _firstAtom;
Nick Kledzik49d6cc82012-02-15 00:38:09 +0000364 bool _wroteFirstFixup;
Michael J. Spencer773a8fb2011-12-18 08:27:59 +0000365};
366
Nick Kledzik49d6cc82012-02-15 00:38:09 +0000367} // anonymous namespace
368
369
370
371///
372/// writeObjectText - writes the lld::File object as in YAML
373/// format to the specified stream.
374///
Nick Kledzik55fd6be2012-01-16 22:03:44 +0000375void writeObjectText(const File &file, llvm::raw_ostream &out) {
Nick Kledzik49d6cc82012-02-15 00:38:09 +0000376 // Figure what ref-name labels are needed
377 RefNameBuilder rnb;
378 file.forEachAtom(rnb);
379
380 // Write out all atoms
381 AtomWriter h(rnb, out);
Michael J. Spencer773a8fb2011-12-18 08:27:59 +0000382 out << "---\n";
383 out << "atoms:\n";
Nick Kledzik38eec3d2011-12-22 02:38:01 +0000384 file.forEachAtom(h);
Michael J. Spencer773a8fb2011-12-18 08:27:59 +0000385 out << "...\n";
386}
387
388} // namespace yaml
389} // namespace lld