blob: 0316a2e5bb3fc7517d450bc9c1736bc8d647051c [file] [log] [blame]
Nick Kledzik55fd6be2012-01-16 22:03:44 +00001//===- Core/NativeReader.cpp - reads 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
12#include <assert.h>
13
14#include "llvm/ADT/ArrayRef.h"
15#include "llvm/ADT/OwningPtr.h"
16#include "llvm/ADT/StringRef.h"
17#include "llvm/Support/ErrorHandling.h"
18#include "llvm/Support/MemoryBuffer.h"
19#include "llvm/Support/system_error.h"
20
21#include "lld/Core/File.h"
22#include "lld/Core/Atom.h"
23
24#include "NativeFileFormat.h"
25
26namespace lld {
27
28// forward reference
29class NativeFile;
30
31
32enum native_reader_errors {
33 success = 0,
34 unknown_file_format,
35 file_too_short,
36 file_malformed,
37 unknown_chunk_type,
38 memory_error,
39};
40
41class reader_error_category : public llvm::_do_message {
42public:
43 virtual const char* name() const {
44 return "lld.native.reader";
45 }
46 virtual std::string message(int ev) const;
47};
48
49const reader_error_category reader_error_category_singleton;
50
51std::string reader_error_category::message(int ev) const {
52 switch (ev) {
53 case success:
54 return "Success";
55 case unknown_file_format:
56 return "Unknown file foramt";
57 case file_too_short:
58 return "file truncated";
59 case file_malformed:
60 return "file malformed";
61 case memory_error:
62 return "out of memory";
63 case unknown_chunk_type:
64 return "unknown chunk type";
65 default:
66 llvm_unreachable("An enumerator of native_reader_errors does not have a "
67 "message defined.");
68 }
69}
70
71inline llvm::error_code make_error_code(native_reader_errors e) {
72 return llvm::error_code(static_cast<int>(e), reader_error_category_singleton);
73}
74
75
76
77
78//
79// An object of this class is instantied for each NativeDefinedAtomIvarsV1
80// struct in the NCS_DefinedAtomsV1 chunk.
81//
82class NativeDefinedAtomV1 : public DefinedAtom {
83public:
84 NativeDefinedAtomV1(const NativeFile& f,
85 const NativeDefinedAtomIvarsV1* ivarData)
86 : _file(&f), _ivarData(ivarData) { }
87
88 virtual const class File& file() const;
89
90 virtual uint64_t ordinal() const;
91
92 virtual llvm::StringRef name() const;
93
94 virtual bool internalName() const {
95 return attributes().internalName;
96 }
97
98 virtual uint64_t size() const {
99 return _ivarData->contentSize;
100 }
101
102 virtual DefinedAtom::Scope scope() const {
103 return (DefinedAtom::Scope)(attributes().scope);
104 }
105
106 virtual DefinedAtom::Interposable interposable() const {
107 return (DefinedAtom::Interposable)(attributes().interposable);
108 }
109
110 virtual DefinedAtom::Merge merge() const {
111 return (DefinedAtom::Merge)(attributes().merge);
112 }
113
114 virtual DefinedAtom::ContentType contentType() const {
115 return (DefinedAtom::ContentType)(attributes().contentType);
116 }
117
118 virtual DefinedAtom::Alignment alignment() const {
119 return DefinedAtom::Alignment(attributes().align2, attributes().alignModulus);
120 }
121
122 virtual DefinedAtom::SectionChoice sectionChoice() const {
123 return (DefinedAtom::SectionChoice)(attributes().sectionChoice);
124 }
125
126 virtual llvm::StringRef customSectionName() const;
127
128 virtual DefinedAtom::DeadStripKind deadStrip() const {
129 return (DefinedAtom::DeadStripKind)(attributes().deadStrip);
130 }
131
132 virtual DefinedAtom::ContentPermissions permissions() const {
133 return (DefinedAtom::ContentPermissions)(attributes().permissions);
134 }
135
136 virtual bool isThumb() const {
137 return (attributes().thumb != 0);
138 }
139
140 virtual bool isAlias() const {
141 return (attributes().alias != 0);
142 }
143
144 llvm::ArrayRef<uint8_t> rawContent() const;
145
146 virtual Reference::iterator referencesBegin() const {
147 return 0;
148 }
149
150 virtual Reference::iterator referencesEnd() const {
151 return 0;
152 }
153
154private:
155 const NativeAtomAttributesV1& attributes() const;
156
157 const NativeFile* _file;
158 const NativeDefinedAtomIvarsV1* _ivarData;
159};
160
161
162
163
164//
165// lld::File object for native llvm object file
166//
167class NativeFile : public File {
168public:
169
170 /// Instantiates a File object from a native object file. Ownership
171 /// of the MemoryBuffer is transfered to the resulting File object.
172 static llvm::error_code make(llvm::MemoryBuffer* mb, llvm::StringRef path,
173 File*& result) {
174 const uint8_t* const base =
175 reinterpret_cast<const uint8_t*>(mb->getBufferStart());
176 const NativeFileHeader* const header =
177 reinterpret_cast<const NativeFileHeader*>(base);
Michael J. Spencerb2bd7332012-01-31 21:45:53 +0000178 const NativeChunk *const chunks =
179 reinterpret_cast<const NativeChunk*>(base + sizeof(NativeFileHeader));
Nick Kledzik55fd6be2012-01-16 22:03:44 +0000180 // make sure magic matches
181 if ( memcmp(header->magic, NATIVE_FILE_HEADER_MAGIC, 16) != 0 )
182 return make_error_code(unknown_file_format);
183
184 // make sure mapped file contains all needed data
185 const size_t fileSize = mb->getBufferSize();
186 if ( header->fileSize > fileSize )
187 return make_error_code(file_too_short);
188
189 // instantiate NativeFile object and add values to it as found
190 NativeFile* file = new NativeFile(mb, path);
191
192 // process each chunk
193 for(uint32_t i=0; i < header->chunkCount; ++i) {
194 llvm::error_code ec;
Michael J. Spencerb2bd7332012-01-31 21:45:53 +0000195 const NativeChunk* chunk = &chunks[i];
Nick Kledzik55fd6be2012-01-16 22:03:44 +0000196 // sanity check chunk is within file
197 if ( chunk->fileOffset > fileSize )
198 return make_error_code(file_malformed);
199 if ( (chunk->fileOffset + chunk->fileSize) > fileSize)
200 return make_error_code(file_malformed);
201 // process chunk, based on signature
202 switch ( chunk->signature ) {
203 case NCS_DefinedAtomsV1:
204 ec = file->processDefinedAtomsV1(base, chunk);
205 break;
206 case NCS_AttributesArrayV1:
207 ec = file->processAttributesV1(base, chunk);
208 break;
209 case NCS_Content:
210 ec = file->processContent(base, chunk);
211 break;
212 case NCS_Strings:
213 ec = file->processStrings(base, chunk);
214 break;
215 default:
216 return make_error_code(unknown_chunk_type);
217 }
218 if ( ec ) {
219 delete file;
220 return ec;
221 }
222
223 // TO DO: validate enough chunks were used
224
225 result = file;
226 }
227
228
229 return make_error_code(success);
230 }
231
232 virtual ~NativeFile() {
233 // The NativeFile owns the MemoryBuffer and must not delete it.
234 delete _buffer;
235 // All other ivar pointers are pointers into the MemoryBuffer, except
236 // the _definedAtoms array which was allocated to contain an array
237 // of Atom objects. The atoms have empty destructors, so it is ok
238 // to just delete the memory.
239 delete _definedAtoms.arrayStart;
240 }
241
242 // visits each atom in the file
243 virtual bool forEachAtom(AtomHandler& handler) const {
244 for(const uint8_t* p=_definedAtoms.arrayStart; p != _definedAtoms.arrayEnd;
245 p += _definedAtoms.elementSize) {
246 const DefinedAtom* atom = reinterpret_cast<const DefinedAtom*>(p);
247 handler.doDefinedAtom(*atom);
248 }
249 return (_definedAtoms.arrayStart != _definedAtoms.arrayEnd);
250 }
251
252 // not used
253 virtual bool justInTimeforEachAtom(llvm::StringRef name,
254 AtomHandler &) const {
255 return false;
256 }
257
258private:
259 friend class NativeDefinedAtomV1;
260
261 // instantiate array of DefinedAtoms from v1 ivar data in file
262 llvm::error_code processDefinedAtomsV1(const uint8_t* base,
263 const NativeChunk* chunk) {
264 const size_t atomSize = sizeof(NativeDefinedAtomV1);
265 size_t atomsArraySize = chunk->elementCount * atomSize;
266 uint8_t* atomsStart = reinterpret_cast<uint8_t*>
267 (operator new(atomsArraySize, std::nothrow));
268 if (atomsStart == NULL )
269 return make_error_code(memory_error);
270 const size_t ivarElementSize = chunk->fileSize
271 / chunk->elementCount;
272 if ( ivarElementSize != sizeof(NativeDefinedAtomIvarsV1) )
273 return make_error_code(file_malformed);
274 uint8_t* atomsEnd = atomsStart + atomsArraySize;
275 const NativeDefinedAtomIvarsV1* ivarData =
276 reinterpret_cast<const NativeDefinedAtomIvarsV1*>
277 (base + chunk->fileOffset);
278 for(uint8_t* s = atomsStart; s != atomsEnd; s += atomSize) {
279 NativeDefinedAtomV1* atomAllocSpace =
280 reinterpret_cast<NativeDefinedAtomV1*>(s);
281 new (atomAllocSpace) NativeDefinedAtomV1(*this, ivarData);
282 ++ivarData;
283 }
284 this->_definedAtoms.arrayStart = atomsStart;
285 this->_definedAtoms.arrayEnd = atomsEnd;
286 this->_definedAtoms.elementSize = atomSize;
287 return make_error_code(success);
288 }
289
290 // set up pointers to attributes array
291 llvm::error_code processAttributesV1(const uint8_t* base, const NativeChunk* chunk) {
292 this->_attributes = base + chunk->fileOffset;
293 this->_attributesMaxOffset = chunk->fileSize;
294 return make_error_code(success);
295 }
296
297 // set up pointers to string pool in file
298 llvm::error_code processStrings(const uint8_t* base,
299 const NativeChunk* chunk) {
300 this->_strings = reinterpret_cast<const char*>(base + chunk->fileOffset);
301 this->_stringsMaxOffset = chunk->fileSize;
302 return make_error_code(success);
303 }
304
305 // set up pointers to content area in file
306 llvm::error_code processContent(const uint8_t* base,
307 const NativeChunk* chunk) {
308 this->_contentStart = base + chunk->fileOffset;
309 this->_contentEnd = base + chunk->fileOffset + chunk->fileSize;
310 return make_error_code(success);
311 }
312
313 llvm::StringRef string(uint32_t offset) const {
314 assert(offset < _stringsMaxOffset);
315 return llvm::StringRef(&_strings[offset]);
316 }
317
318 const NativeAtomAttributesV1& attribute(uint32_t offset) const {
319 assert(offset < _attributesMaxOffset);
320 return *reinterpret_cast<const NativeAtomAttributesV1*>(_attributes + offset);
321 }
322
323 const uint8_t* content(uint32_t offset, uint32_t size) const {
324 const uint8_t* result = _contentStart + offset;
325 assert((result+size) <= _contentEnd);
326 return result;
327 }
328
329
330 // private constructor, only called by make()
331 NativeFile(llvm::MemoryBuffer* mb, llvm::StringRef path) :
332 lld::File(path), _buffer(mb), _header(NULL),
333 _strings(NULL), _stringsMaxOffset(0),
334 _contentStart(NULL), _contentEnd(NULL)
335 {
336 _header = reinterpret_cast<const NativeFileHeader*>(mb->getBufferStart());
337 }
338
339 struct AtomArray {
340 AtomArray() : arrayStart(NULL), arrayEnd(NULL),
341 elementSize(0) { }
342 const uint8_t* arrayStart;
343 const uint8_t* arrayEnd;
344 uint32_t elementSize;
345 };
346
347 llvm::MemoryBuffer* _buffer;
348 const NativeFileHeader* _header;
349 AtomArray _definedAtoms;
350 const uint8_t* _attributes;
351 uint32_t _attributesMaxOffset;
352 const char* _strings;
353 uint32_t _stringsMaxOffset;
354 const uint8_t* _contentStart;
355 const uint8_t* _contentEnd;
356};
357
358
359
360inline const class File& NativeDefinedAtomV1::file() const {
361 return *_file;
362}
363
364inline uint64_t NativeDefinedAtomV1:: ordinal() const {
365 const uint8_t* p = reinterpret_cast<const uint8_t*>(_ivarData);
366 return p - _file->_definedAtoms.arrayStart;
367}
368
369inline llvm::StringRef NativeDefinedAtomV1::name() const {
370 return _file->string(_ivarData->nameOffset);
371}
372
373inline const NativeAtomAttributesV1& NativeDefinedAtomV1::attributes() const {
374 return _file->attribute(_ivarData->attributesOffset);
375}
376
377inline llvm::ArrayRef<uint8_t> NativeDefinedAtomV1::rawContent() const {
378 if ( this->contentType() == DefinedAtom::typeZeroFill )
379 return llvm::ArrayRef<uint8_t>();
380 const uint8_t* p = _file->content(_ivarData->contentOffset,
381 _ivarData->contentSize);
382 return llvm::ArrayRef<uint8_t>(p, _ivarData->contentSize);
383}
384
385inline llvm::StringRef NativeDefinedAtomV1::customSectionName() const {
386 uint32_t offset = attributes().sectionNameOffset;
387 return _file->string(offset);
388}
389
390
391
392//
393// Instantiate an lld::File from the given native object file buffer
394//
395llvm::error_code parseNativeObjectFile(llvm::MemoryBuffer* mb,
396 llvm::StringRef path, File*& result) {
397 return NativeFile::make(mb, path, result);
398}
399
400
401
402//
403// Instantiate an lld::File from the given native object file path
404//
405llvm::error_code parseNativeObjectFileOrSTDIN(llvm::StringRef path,
406 File*& result) {
407 llvm::OwningPtr<llvm::MemoryBuffer> mb;
408 llvm::error_code ec = llvm::MemoryBuffer::getFileOrSTDIN(path, mb);
409 if ( ec )
410 return ec;
411
Michael J. Spencer4e45ebb2012-01-31 21:46:29 +0000412 return parseNativeObjectFile(mb.take(), path, result);
Nick Kledzik55fd6be2012-01-16 22:03:44 +0000413}
414
415
416
417} // namespace lld