blob: da877fd652cf92de85463c568d81ccb8cdb4afcc [file] [log] [blame]
Nick Kledzik55fd6be2012-01-16 22:03:44 +00001//===- Core/NativeWriter.cpp - Creates a native object file ---------------===//
2//
3// The LLVM Linker
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9
10#include <vector>
11#include <map>
12
13#include "llvm/ADT/StringRef.h"
14#include "llvm/ADT/ArrayRef.h"
Nick Kledzik49d6cc82012-02-15 00:38:09 +000015#include "llvm/ADT/DenseMap.h"
Nick Kledzik55fd6be2012-01-16 22:03:44 +000016
17#include "lld/Core/File.h"
18#include "lld/Core/NativeWriter.h"
19
20#include "NativeFileFormat.h"
21
22
23namespace lld {
24
25
26///
27/// Class for writing native object files.
28///
Nick Kledzik49d6cc82012-02-15 00:38:09 +000029class NativeWriter : public File::AtomHandler,
30 public DefinedAtom::ReferenceHandler {
Nick Kledzik55fd6be2012-01-16 22:03:44 +000031public:
32 /// construct writer for an lld::File object
33 NativeWriter(const lld::File& file) : _file(file) {
Nick Kledzik49d6cc82012-02-15 00:38:09 +000034 // reserve first byte for unnamed atoms
35 _stringPool.push_back('\0');
Nick Kledzik55fd6be2012-01-16 22:03:44 +000036 // visit all atoms
37 _file.forEachAtom(*this);
38 // construct file header based on atom information accumulated
39 makeHeader();
40 }
41
42 // write the lld::File in native format to the specified stream
43 void write(llvm::raw_ostream& out) {
Nick Kledzik49d6cc82012-02-15 00:38:09 +000044 assert( out.tell() == 0 );
Nick Kledzik55fd6be2012-01-16 22:03:44 +000045 out.write((char*)_headerBuffer, _headerBufferSize);
Nick Kledzik49d6cc82012-02-15 00:38:09 +000046
47 if (!_definedAtomIvars.empty()) {
48 assert( out.tell() == findChunk(NCS_DefinedAtomsV1).fileOffset );
Michael J. Spencer8c36f452012-01-31 21:46:41 +000049 out.write((char*)&_definedAtomIvars[0],
50 _definedAtomIvars.size()*sizeof(NativeDefinedAtomIvarsV1));
Nick Kledzik49d6cc82012-02-15 00:38:09 +000051 }
52
53 if (!_attributes.empty()) {
54 assert( out.tell() == findChunk(NCS_AttributesArrayV1).fileOffset );
Michael J. Spencer8c36f452012-01-31 21:46:41 +000055 out.write((char*)&_attributes[0],
56 _attributes.size()*sizeof(NativeAtomAttributesV1));
Nick Kledzik49d6cc82012-02-15 00:38:09 +000057 }
58
59 if ( !_undefinedAtomIvars.empty() ) {
60 assert( out.tell() == findChunk(NCS_UndefinedAtomsV1).fileOffset );
Nick Kledzik23384e82012-02-07 02:59:54 +000061 out.write((char*)&_undefinedAtomIvars[0],
62 _undefinedAtomIvars.size()*sizeof(NativeUndefinedAtomIvarsV1));
Nick Kledzik49d6cc82012-02-15 00:38:09 +000063 }
64
65 if (!_stringPool.empty()) {
66 assert( out.tell() == findChunk(NCS_Strings).fileOffset );
Michael J. Spencer8c36f452012-01-31 21:46:41 +000067 out.write(&_stringPool[0], _stringPool.size());
Nick Kledzik49d6cc82012-02-15 00:38:09 +000068 }
69
70 if ( !_references.empty() ) {
71 assert( out.tell() == findChunk(NCS_ReferencesArrayV1).fileOffset );
72 out.write((char*)&_references[0],
73 _references.size()*sizeof(NativeReferenceIvarsV1));
74 }
75
76 if ( !_targetsTableIndex.empty() ) {
77 assert( out.tell() == findChunk(NCS_TargetsTable).fileOffset );
78 writeTargetTable(out);
79 }
80
81 if ( !_addendsTableIndex.empty() ) {
82 assert( out.tell() == findChunk(NCS_AddendsTable).fileOffset );
83 writeAddendTable(out);
84 }
85
86 if (!_contentPool.empty()) {
87 assert( out.tell() == findChunk(NCS_Content).fileOffset );
Nick Kledzik23384e82012-02-07 02:59:54 +000088 out.write((char*)&_contentPool[0], _contentPool.size());
Nick Kledzik49d6cc82012-02-15 00:38:09 +000089 }
Nick Kledzik55fd6be2012-01-16 22:03:44 +000090 }
91
92private:
93
94 // visitor routine called by forEachAtom()
Nick Kledzik49d6cc82012-02-15 00:38:09 +000095 virtual void doDefinedAtom(const DefinedAtom& atom) {
96 _definedAtomIndex[&atom] = _definedAtomIvars.size();
Nick Kledzik55fd6be2012-01-16 22:03:44 +000097 NativeDefinedAtomIvarsV1 ivar;
Nick Kledzik49d6cc82012-02-15 00:38:09 +000098 unsigned refsCount;
Nick Kledzik55fd6be2012-01-16 22:03:44 +000099 ivar.nameOffset = getNameOffset(atom);
100 ivar.attributesOffset = getAttributeOffset(atom);
Nick Kledzik49d6cc82012-02-15 00:38:09 +0000101 ivar.referencesStartIndex = getReferencesIndex(atom, refsCount);
102 ivar.referencesCount = refsCount;
Nick Kledzik55fd6be2012-01-16 22:03:44 +0000103 ivar.contentOffset = getContentOffset(atom);
104 ivar.contentSize = atom.size();
105 _definedAtomIvars.push_back(ivar);
106 }
107
108 // visitor routine called by forEachAtom()
Nick Kledzik49d6cc82012-02-15 00:38:09 +0000109 virtual void doUndefinedAtom(const UndefinedAtom& atom) {
110 _undefinedAtomIndex[&atom] = _undefinedAtomIvars.size();
Nick Kledzik23384e82012-02-07 02:59:54 +0000111 NativeUndefinedAtomIvarsV1 ivar;
112 ivar.nameOffset = getNameOffset(atom);
113 ivar.flags = (atom.weakImport() ? 1 : 0);
114 _undefinedAtomIvars.push_back(ivar);
Nick Kledzik55fd6be2012-01-16 22:03:44 +0000115 }
116
117 // visitor routine called by forEachAtom()
Nick Kledzik49d6cc82012-02-15 00:38:09 +0000118 virtual void doFile(const File &) {
Nick Kledzik55fd6be2012-01-16 22:03:44 +0000119 }
Nick Kledzik23384e82012-02-07 02:59:54 +0000120
Nick Kledzik55fd6be2012-01-16 22:03:44 +0000121 // fill out native file header and chunk directory
122 void makeHeader() {
Nick Kledzik23384e82012-02-07 02:59:54 +0000123 const bool hasUndefines = !_undefinedAtomIvars.empty();
Nick Kledzik49d6cc82012-02-15 00:38:09 +0000124 const bool hasTargetsTable = !_targetsTableIndex.empty();
125 const bool hasAddendTable = !_addendsTableIndex.empty();
126 int chunkCount = 5;
127 if ( hasUndefines ) ++chunkCount;
128 if ( hasTargetsTable ) ++chunkCount;
129 if ( hasAddendTable ) ++chunkCount;
Nick Kledzik23384e82012-02-07 02:59:54 +0000130 _headerBufferSize = sizeof(NativeFileHeader)
131 + chunkCount*sizeof(NativeChunk);
Nick Kledzik55fd6be2012-01-16 22:03:44 +0000132 _headerBuffer = reinterpret_cast<NativeFileHeader*>
133 (operator new(_headerBufferSize, std::nothrow));
Michael J. Spencerb2bd7332012-01-31 21:45:53 +0000134 NativeChunk *chunks =
135 reinterpret_cast<NativeChunk*>(reinterpret_cast<char*>(_headerBuffer)
136 + sizeof(NativeFileHeader));
Nick Kledzik55fd6be2012-01-16 22:03:44 +0000137 memcpy(_headerBuffer->magic, NATIVE_FILE_HEADER_MAGIC, 16);
138 _headerBuffer->endian = NFH_LittleEndian;
139 _headerBuffer->architecture = 0;
140 _headerBuffer->fileSize = 0;
Nick Kledzik23384e82012-02-07 02:59:54 +0000141 _headerBuffer->chunkCount = chunkCount;
142
Nick Kledzik55fd6be2012-01-16 22:03:44 +0000143
144 // create chunk for atom ivar array
Nick Kledzik23384e82012-02-07 02:59:54 +0000145 int nextIndex = 0;
146 NativeChunk& chd = chunks[nextIndex++];
147 chd.signature = NCS_DefinedAtomsV1;
148 chd.fileOffset = _headerBufferSize;
149 chd.fileSize = _definedAtomIvars.size()*sizeof(NativeDefinedAtomIvarsV1);
150 chd.elementCount = _definedAtomIvars.size();
151 uint32_t nextFileOffset = chd.fileOffset + chd.fileSize;
152
153 // create chunk for attributes
154 NativeChunk& cha = chunks[nextIndex++];
155 cha.signature = NCS_AttributesArrayV1;
156 cha.fileOffset = nextFileOffset;
157 cha.fileSize = _attributes.size()*sizeof(NativeAtomAttributesV1);
158 cha.elementCount = _attributes.size();
159 nextFileOffset = cha.fileOffset + cha.fileSize;
Nick Kledzik55fd6be2012-01-16 22:03:44 +0000160
Nick Kledzik23384e82012-02-07 02:59:54 +0000161 // create chunk for undefined atom array
162 if ( hasUndefines ) {
163 NativeChunk& chu = chunks[nextIndex++];
164 chu.signature = NCS_UndefinedAtomsV1;
165 chu.fileOffset = nextFileOffset;
166 chu.fileSize = _undefinedAtomIvars.size() *
167 sizeof(NativeUndefinedAtomIvarsV1);
168 chu.elementCount = _undefinedAtomIvars.size();
169 nextFileOffset = chu.fileOffset + chu.fileSize;
170 }
171
172 // create chunk for symbol strings
Nick Kledzik49d6cc82012-02-15 00:38:09 +0000173 // pad end of string pool to 4-bytes
174 while ( (_stringPool.size() % 4) != 0 )
175 _stringPool.push_back('\0');
Nick Kledzik23384e82012-02-07 02:59:54 +0000176 NativeChunk& chs = chunks[nextIndex++];
177 chs.signature = NCS_Strings;
178 chs.fileOffset = nextFileOffset;
179 chs.fileSize = _stringPool.size();
180 chs.elementCount = _stringPool.size();
181 nextFileOffset = chs.fileOffset + chs.fileSize;
182
Nick Kledzik49d6cc82012-02-15 00:38:09 +0000183 // create chunk for references
184 NativeChunk& chr = chunks[nextIndex++];
185 chr.signature = NCS_ReferencesArrayV1;
186 chr.fileOffset = nextFileOffset;
187 chr.fileSize = _references.size() * sizeof(NativeReferenceIvarsV1);
188 chr.elementCount = _references.size();
189 nextFileOffset = chr.fileOffset + chr.fileSize;
190
191 // create chunk for target table
192 if ( hasTargetsTable ) {
193 NativeChunk& cht = chunks[nextIndex++];
194 cht.signature = NCS_TargetsTable;
195 cht.fileOffset = nextFileOffset;
196 cht.fileSize = _targetsTableIndex.size() * sizeof(uint32_t);
197 cht.elementCount = _targetsTableIndex.size();
198 nextFileOffset = cht.fileOffset + cht.fileSize;
199 }
200
201 // create chunk for addend table
202 if ( hasAddendTable ) {
203 NativeChunk& chad = chunks[nextIndex++];
204 chad.signature = NCS_AddendsTable;
205 chad.fileOffset = nextFileOffset;
206 chad.fileSize = _addendsTableIndex.size() * sizeof(Reference::Addend);
207 chad.elementCount = _addendsTableIndex.size();
208 nextFileOffset = chad.fileOffset + chad.fileSize;
209 }
210
Nick Kledzik23384e82012-02-07 02:59:54 +0000211 // create chunk for content
212 NativeChunk& chc = chunks[nextIndex++];
213 chc.signature = NCS_Content;
214 chc.fileOffset = nextFileOffset;
215 chc.fileSize = _contentPool.size();
216 chc.elementCount = _contentPool.size();
217 nextFileOffset = chc.fileOffset + chc.fileSize;
218
219 _headerBuffer->fileSize = nextFileOffset;
Nick Kledzik55fd6be2012-01-16 22:03:44 +0000220 }
221
Nick Kledzik49d6cc82012-02-15 00:38:09 +0000222 // scan header to find particular chunk
223 NativeChunk& findChunk(uint32_t signature) {
224 const uint32_t chunkCount = _headerBuffer->chunkCount;
225 NativeChunk* chunks =
226 reinterpret_cast<NativeChunk*>(reinterpret_cast<char*>(_headerBuffer)
227 + sizeof(NativeFileHeader));
228 for (uint32_t i=0; i < chunkCount; ++i) {
229 if ( chunks[i].signature == signature )
230 return chunks[i];
231 }
232 assert(0 && "findChunk() signature not found");
233 }
Nick Kledzik55fd6be2012-01-16 22:03:44 +0000234
235 // append atom name to string pool and return offset
Nick Kledzik23384e82012-02-07 02:59:54 +0000236 uint32_t getNameOffset(const Atom& atom) {
Nick Kledzik55fd6be2012-01-16 22:03:44 +0000237 return this->getNameOffset(atom.name());
238 }
239
240 // append atom name to string pool and return offset
241 uint32_t getNameOffset(llvm::StringRef name) {
Nick Kledzik49d6cc82012-02-15 00:38:09 +0000242 if ( name.empty() )
243 return 0;
Nick Kledzik55fd6be2012-01-16 22:03:44 +0000244 uint32_t result = _stringPool.size();
245 _stringPool.insert(_stringPool.end(), name.size()+1, 0);
246 strcpy(&_stringPool[result], name.data());
247 return result;
248 }
249
250 // append atom cotent to content pool and return offset
251 uint32_t getContentOffset(const class DefinedAtom& atom) {
252 if ( atom.contentType() == DefinedAtom::typeZeroFill )
253 return 0;
254 uint32_t result = _contentPool.size();
255 llvm::ArrayRef<uint8_t> cont = atom.rawContent();
Michael J. Spencer846fe662012-01-31 21:46:05 +0000256 _contentPool.insert(_contentPool.end(), cont.begin(), cont.end());
Nick Kledzik55fd6be2012-01-16 22:03:44 +0000257 return result;
258 }
259
260 // reuse existing attributes entry or create a new one and return offet
261 uint32_t getAttributeOffset(const class DefinedAtom& atom) {
262 NativeAtomAttributesV1 attrs;
263 computeAttributesV1(atom, attrs);
264 for(unsigned int i=0; i < _attributes.size(); ++i) {
265 if ( !memcmp(&_attributes[i], &attrs, sizeof(NativeAtomAttributesV1)) ) {
266 // found that this set of attributes already used, so re-use
267 return i * sizeof(NativeAtomAttributesV1);
268 }
269 }
270 // append new attribute set to end
271 uint32_t result = _attributes.size() * sizeof(NativeAtomAttributesV1);
272 _attributes.push_back(attrs);
273 return result;
274 }
275
276 uint32_t sectionNameOffset(const class DefinedAtom& atom) {
277 // if section based on content, then no custom section name available
278 if ( atom.sectionChoice() == DefinedAtom::sectionBasedOnContent )
279 return 0;
280 llvm::StringRef name = atom.customSectionName();
281 assert( ! name.empty() );
282 // look to see if this section name was used by another atom
283 for(NameToOffsetVector::iterator it=_sectionNames.begin();
284 it != _sectionNames.end(); ++it) {
285 if ( name.equals(it->first) )
286 return it->second;
287 }
288 // first use of this section name
289 uint32_t result = this->getNameOffset(name);
290 _sectionNames.push_back(
291 std::make_pair<llvm::StringRef, uint32_t>(name, result));
292 return result;
293 }
294
295 void computeAttributesV1(const class DefinedAtom& atom,
296 NativeAtomAttributesV1& attrs) {
297 attrs.sectionNameOffset = sectionNameOffset(atom);
298 attrs.align2 = atom.alignment().powerOf2;
299 attrs.alignModulus = atom.alignment().modulus;
Nick Kledzik55fd6be2012-01-16 22:03:44 +0000300 attrs.scope = atom.scope();
301 attrs.interposable = atom.interposable();
302 attrs.merge = atom.merge();
303 attrs.contentType = atom.contentType();
304 attrs.sectionChoice = atom.sectionChoice();
305 attrs.deadStrip = atom.deadStrip();
306 attrs.permissions = atom.permissions();
Nick Kledzik49d6cc82012-02-15 00:38:09 +0000307 //attrs.thumb = atom.isThumb();
Nick Kledzik55fd6be2012-01-16 22:03:44 +0000308 attrs.alias = atom.isAlias();
309 }
310
Nick Kledzik49d6cc82012-02-15 00:38:09 +0000311 // add references for this atom in a contiguous block in NCS_ReferencesArrayV1
312 uint32_t getReferencesIndex(const DefinedAtom& atom, unsigned& count) {
313 count = 0;
314 size_t startRefSize = _references.size();
315 uint32_t result = startRefSize;
316 atom.forEachReference(*this);
317 count = _references.size() - startRefSize;
318 if ( count == 0 )
319 return 0;
320 else
321 return result;
322 }
323
324 void doReference(const Reference& ref) {
325 NativeReferenceIvarsV1 nref;
326 nref.offsetInAtom = ref.offsetInAtom();
327 nref.kind = ref.kind();
328 nref.targetIndex = this->getTargetIndex(ref.target());
329 nref.addendIndex = this->getAddendIndex(ref.addend());
330 _references.push_back(nref);
331 }
332
333 uint32_t getTargetIndex(const Atom* target) {
334 TargetToIndex::const_iterator pos = _targetsTableIndex.find(target);
335 if ( pos != _targetsTableIndex.end() ) {
336 return pos->second;
337 }
338 uint32_t result = _targetsTableIndex.size();
339 _targetsTableIndex[target] = result;
340 return result;
341 }
342
343 void writeTargetTable(llvm::raw_ostream& out) {
344 // Build table of target indexes
345 uint32_t maxTargetIndex = _targetsTableIndex.size();
346 uint32_t targetIndexes[maxTargetIndex];
347 for (TargetToIndex::iterator it = _targetsTableIndex.begin();
348 it != _targetsTableIndex.end(); ++it) {
349 const Atom* atom = it->first;
350 uint32_t targetIndex = it->second;
351 assert(targetIndex < maxTargetIndex);
352 uint32_t atomIndex = 0;
353 TargetToIndex::iterator pos = _definedAtomIndex.find(atom);
354 if ( pos != _definedAtomIndex.end() ) {
355 atomIndex = pos->second;
356 }
357 else {
358 pos = _undefinedAtomIndex.find(atom);
359 assert(pos != _undefinedAtomIndex.end());
360 atomIndex = pos->second + _definedAtomIvars.size();
361 }
362 targetIndexes[targetIndex] = atomIndex;
363 }
364 // write table
365 out.write((char*)&targetIndexes[0], maxTargetIndex*sizeof(uint32_t));
366 }
367
368 uint32_t getAddendIndex(Reference::Addend addend) {
369 if ( addend == 0 )
370 return 0; // addend index zero is used to mean "no addend"
371 AddendToIndex::const_iterator pos = _addendsTableIndex.find(addend);
372 if ( pos != _addendsTableIndex.end() ) {
373 return pos->second;
374 }
375 uint32_t result = _addendsTableIndex.size() + 1; // one-based index
376 _addendsTableIndex[addend] = result;
377 return result;
378 }
379
380 void writeAddendTable(llvm::raw_ostream& out) {
381 // Build table of addends
382 uint32_t maxAddendIndex = _addendsTableIndex.size();
383 Reference::Addend addends[maxAddendIndex];
384 for (AddendToIndex::iterator it = _addendsTableIndex.begin();
385 it != _addendsTableIndex.end(); ++it) {
386 Reference::Addend addend = it->first;
387 uint32_t index = it->second;
388 assert(index <= maxAddendIndex);
389 addends[index-1] = addend;
390 }
391 // write table
392 out.write((char*)&addends[0], maxAddendIndex*sizeof(Reference::Addend));
393 }
394
Nick Kledzik55fd6be2012-01-16 22:03:44 +0000395 typedef std::vector<std::pair<llvm::StringRef, uint32_t> > NameToOffsetVector;
396
Nick Kledzik49d6cc82012-02-15 00:38:09 +0000397 typedef llvm::DenseMap<const Atom*, uint32_t> TargetToIndex;
398 typedef llvm::DenseMap<Reference::Addend, uint32_t> AddendToIndex;
399
Nick Kledzik23384e82012-02-07 02:59:54 +0000400 const lld::File& _file;
401 NativeFileHeader* _headerBuffer;
402 size_t _headerBufferSize;
403 std::vector<char> _stringPool;
404 std::vector<uint8_t> _contentPool;
405 std::vector<NativeDefinedAtomIvarsV1> _definedAtomIvars;
406 std::vector<NativeAtomAttributesV1> _attributes;
407 std::vector<NativeUndefinedAtomIvarsV1> _undefinedAtomIvars;
Nick Kledzik49d6cc82012-02-15 00:38:09 +0000408 std::vector<NativeReferenceIvarsV1> _references;
409 TargetToIndex _targetsTableIndex;
410 TargetToIndex _definedAtomIndex;
411 TargetToIndex _undefinedAtomIndex;
412 AddendToIndex _addendsTableIndex;
Nick Kledzik23384e82012-02-07 02:59:54 +0000413 NameToOffsetVector _sectionNames;
Nick Kledzik55fd6be2012-01-16 22:03:44 +0000414};
415
416
417
418
419
420/// writeNativeObjectFile - writes the lld::File object in native object
421/// file format to the specified stream.
422int writeNativeObjectFile(const lld::File &file, llvm::raw_ostream &out) {
423 NativeWriter writer(file);
424 writer.write(out);
425 return 0;
426}
427
428/// writeNativeObjectFile - writes the lld::File object in native object
429/// file format to the specified file path.
430int writeNativeObjectFile(const lld::File& file, llvm::StringRef path) {
431 std::string errorInfo;
432 llvm::raw_fd_ostream out(path.data(), errorInfo, llvm::raw_fd_ostream::F_Binary);
433 if ( !errorInfo.empty() )
434 return -1;
435 return writeNativeObjectFile(file, out);
436}
437
438} // namespace lld