]> git.kernelconcepts.de Git - karo-tx-uboot.git/blob - tools/elftosb/common/StELFFile.cpp
Unified codebase for TX28, TX48, TX51, TX53
[karo-tx-uboot.git] / tools / elftosb / common / StELFFile.cpp
1 /*
2  * File:        StELFFile.cpp
3  *
4  * Copyright (c) Freescale Semiconductor, Inc. All rights reserved.
5  * See included license file for license details.
6  */
7
8 #include "StELFFile.h"
9 #include <ios>
10 #include <stdexcept>
11 #include <stdio.h>
12 #include "EndianUtilities.h"
13
14 //! \exception StELFFileException is thrown if there is a problem with the file format.
15 //!
16 StELFFile::StELFFile(std::istream & inStream)
17 :       m_stream(inStream)
18 {
19         readFileHeaders();
20 }
21
22 //! Disposes of the string table data.
23 StELFFile::~StELFFile()
24 {
25         SectionDataMap::iterator it = m_sectionDataCache.begin();
26         for (; it != m_sectionDataCache.end(); ++it)
27         {
28                 SectionDataInfo & info = it->second;
29                 if (info.m_data != NULL)
30                 {
31                         delete [] info.m_data;
32                 }
33         }
34 }
35
36 //! \exception StELFFileException is thrown if the file is not an ELF file.
37 //!
38 void StELFFile::readFileHeaders()
39 {
40         // move read head to beginning of stream
41         m_stream.seekg(0, std::ios_base::beg);
42         
43         // read ELF header
44         m_stream.read(reinterpret_cast<char *>(&m_header), sizeof(m_header));
45         if (m_stream.bad())
46         {
47                 throw StELFFileException("could not read file header");
48         }
49         
50         // convert endianness
51         m_header.e_type = ENDIAN_LITTLE_TO_HOST_U16(m_header.e_type);
52         m_header.e_machine = ENDIAN_LITTLE_TO_HOST_U16(m_header.e_machine);
53         m_header.e_version = ENDIAN_LITTLE_TO_HOST_U32(m_header.e_version);
54         m_header.e_entry = ENDIAN_LITTLE_TO_HOST_U32(m_header.e_entry);
55         m_header.e_phoff = ENDIAN_LITTLE_TO_HOST_U32(m_header.e_phoff);
56         m_header.e_shoff = ENDIAN_LITTLE_TO_HOST_U32(m_header.e_shoff);
57         m_header.e_flags = ENDIAN_LITTLE_TO_HOST_U32(m_header.e_flags);
58         m_header.e_ehsize = ENDIAN_LITTLE_TO_HOST_U16(m_header.e_ehsize);
59         m_header.e_phentsize = ENDIAN_LITTLE_TO_HOST_U16(m_header.e_phentsize);
60         m_header.e_phnum = ENDIAN_LITTLE_TO_HOST_U16(m_header.e_phnum);
61         m_header.e_shentsize = ENDIAN_LITTLE_TO_HOST_U16(m_header.e_shentsize);
62         m_header.e_shnum = ENDIAN_LITTLE_TO_HOST_U16(m_header.e_shnum);
63         m_header.e_shstrndx = ENDIAN_LITTLE_TO_HOST_U16(m_header.e_shstrndx);
64         
65         // check magic number
66         if (!(m_header.e_ident[EI_MAG0] == ELFMAG0 && m_header.e_ident[EI_MAG1] == ELFMAG1 && m_header.e_ident[EI_MAG2] == ELFMAG2 && m_header.e_ident[EI_MAG3] == ELFMAG3))
67         {
68                 throw StELFFileException("invalid magic number in ELF header");
69         }
70         
71         try
72         {
73                 int i;
74                 
75                 // read section headers
76                 if (m_header.e_shoff != 0 && m_header.e_shnum > 0)
77                 {
78                         Elf32_Shdr sectionHeader;
79                         for (i=0; i < m_header.e_shnum; ++i)
80                         {
81                                 m_stream.seekg(m_header.e_shoff + m_header.e_shentsize * i, std::ios::beg);
82                                 m_stream.read(reinterpret_cast<char *>(&sectionHeader), sizeof(sectionHeader));
83                                 if (m_stream.bad())
84                                 {
85                                         throw StELFFileException("could not read section header");
86                                 }
87                                 
88                                 // convert endianness
89                                 sectionHeader.sh_name = ENDIAN_LITTLE_TO_HOST_U32(sectionHeader.sh_name);
90                                 sectionHeader.sh_type = ENDIAN_LITTLE_TO_HOST_U32(sectionHeader.sh_type);
91                                 sectionHeader.sh_flags = ENDIAN_LITTLE_TO_HOST_U32(sectionHeader.sh_flags);
92                                 sectionHeader.sh_addr = ENDIAN_LITTLE_TO_HOST_U32(sectionHeader.sh_addr);
93                                 sectionHeader.sh_offset = ENDIAN_LITTLE_TO_HOST_U32(sectionHeader.sh_offset);
94                                 sectionHeader.sh_size = ENDIAN_LITTLE_TO_HOST_U32(sectionHeader.sh_size);
95                                 sectionHeader.sh_link = ENDIAN_LITTLE_TO_HOST_U32(sectionHeader.sh_link);
96                                 sectionHeader.sh_info = ENDIAN_LITTLE_TO_HOST_U32(sectionHeader.sh_info);
97                                 sectionHeader.sh_addralign = ENDIAN_LITTLE_TO_HOST_U32(sectionHeader.sh_addralign);
98                                 sectionHeader.sh_entsize = ENDIAN_LITTLE_TO_HOST_U32(sectionHeader.sh_entsize);
99                                 
100                                 m_sectionHeaders.push_back(sectionHeader);
101                         }
102                 }
103                 
104                 // read program headers
105                 if (m_header.e_phoff != 0 && m_header.e_phnum > 0)
106                 {
107                         Elf32_Phdr programHeader;
108                         for (i=0; i < m_header.e_phnum; ++i)
109                         {
110                                 m_stream.seekg(m_header.e_phoff + m_header.e_phentsize * i, std::ios::beg);
111                                 m_stream.read(reinterpret_cast<char *>(&programHeader), sizeof(programHeader));
112                                 if (m_stream.bad())
113                                 {
114                                         throw StELFFileException("could not read program header");
115                                 }
116                                 
117                                 // convert endianness
118                                 programHeader.p_type = ENDIAN_LITTLE_TO_HOST_U32(programHeader.p_type);
119                                 programHeader.p_offset = ENDIAN_LITTLE_TO_HOST_U32(programHeader.p_type);
120                                 programHeader.p_vaddr = ENDIAN_LITTLE_TO_HOST_U32(programHeader.p_vaddr);
121                                 programHeader.p_paddr = ENDIAN_LITTLE_TO_HOST_U32(programHeader.p_paddr);
122                                 programHeader.p_filesz = ENDIAN_LITTLE_TO_HOST_U32(programHeader.p_filesz);
123                                 programHeader.p_memsz = ENDIAN_LITTLE_TO_HOST_U32(programHeader.p_memsz);
124                                 programHeader.p_flags = ENDIAN_LITTLE_TO_HOST_U32(programHeader.p_flags);
125                                 programHeader.p_align = ENDIAN_LITTLE_TO_HOST_U32(programHeader.p_align);
126                                 
127                                 m_programHeaders.push_back(programHeader);
128                         }
129                 }
130                 
131                 // look up symbol table section index
132                 {
133                     std::string symtab_section_name(SYMTAB_SECTION_NAME);
134                     m_symbolTableIndex = getIndexOfSectionWithName(symtab_section_name);
135                 }
136         }
137         catch (...)
138         {
139                 throw StELFFileException("error reading file");
140         }
141 }
142
143 const Elf32_Shdr & StELFFile::getSectionAtIndex(unsigned inIndex) const
144 {
145         if (inIndex > m_sectionHeaders.size())
146                 throw std::invalid_argument("inIndex");
147         
148         return m_sectionHeaders[inIndex];
149 }
150
151 //! If there is not a matching section, then #SHN_UNDEF is returned instead.
152 //!
153 unsigned StELFFile::getIndexOfSectionWithName(const std::string & inName)
154 {
155         unsigned sectionIndex = 0;
156         const_section_iterator it = getSectionBegin();
157         for (; it != getSectionEnd(); ++it, ++sectionIndex)
158         {
159                 const Elf32_Shdr & header = *it;
160                 if (header.sh_name != 0)
161                 {
162                         std::string sectionName = getSectionNameAtIndex(header.sh_name);
163                         if (inName == sectionName)
164                                 return sectionIndex;
165                 }
166         }
167         
168         // no matching section
169         return SHN_UNDEF;
170 }
171
172 //! The pointer returned from this method must be freed with the delete array operator (i.e., delete []).
173 //! If either the section data offset (sh_offset) or the section size (sh_size) are 0, then NULL will
174 //! be returned instead.
175 //!
176 //! The data is read directly from the input stream passed into the constructor. The stream must
177 //! still be open, or an exception will be thrown.
178 //!
179 //! \exception StELFFileException is thrown if an error occurs while reading the file.
180 //! \exception std::bad_alloc is thrown if memory for the data cannot be allocated.
181 uint8_t * StELFFile::getSectionDataAtIndex(unsigned inIndex)
182 {
183         return readSectionData(m_sectionHeaders[inIndex]);
184 }
185
186 //! The pointer returned from this method must be freed with the delete array operator (i.e., delete []).
187 //! If either the section data offset (sh_offset) or the section size (sh_size) are 0, then NULL will
188 //! be returned instead.
189 //!
190 //! The data is read directly from the input stream passed into the constructor. The stream must
191 //! still be open, or an exception will be thrown.
192 //!
193 //! \exception StELFFileException is thrown if an error occurs while reading the file.
194 //! \exception std::bad_alloc is thrown if memory for the data cannot be allocated.
195 uint8_t * StELFFile::getSectionData(const_section_iterator inSection)
196 {
197         return readSectionData(*inSection);
198 }
199
200 //! \exception StELFFileException is thrown if an error occurs while reading the file.
201 //! \exception std::bad_alloc is thrown if memory for the data cannot be allocated.
202 uint8_t * StELFFile::readSectionData(const Elf32_Shdr & inHeader)
203 {
204         // check for empty data
205         if (inHeader.sh_offset == 0 || inHeader.sh_size == 0)
206                 return NULL;
207                 
208         uint8_t * sectionData = new uint8_t[inHeader.sh_size];
209         
210         try
211         {
212                 m_stream.seekg(inHeader.sh_offset, std::ios::beg);
213                 m_stream.read(reinterpret_cast<char *>(sectionData), inHeader.sh_size);
214                 if (m_stream.bad())
215                         throw StELFFileException("could not read entire section");
216         }
217         catch (StELFFileException)
218         {
219                 throw;
220         }
221         catch (...)
222         {
223                 throw StELFFileException("error reading section data");
224         }
225                 
226         return sectionData;
227 }
228
229 const Elf32_Phdr & StELFFile::getSegmentAtIndex(unsigned inIndex) const
230 {
231         if (inIndex > m_programHeaders.size())
232                 throw std::invalid_argument("inIndex");
233         
234         return m_programHeaders[inIndex];
235 }
236
237 //! The pointer returned from this method must be freed with the delete array operator (i.e., delete []).
238 //! If either the segment offset (p_offset) or the segment file size (p_filesz) are 0, then NULL will
239 //! be returned instead.
240 //!
241 //! The data is read directly from the input stream passed into the constructor. The stream must
242 //! still be open, or an exception will be thrown.
243 //!
244 //! \exception StELFFileException is thrown if an error occurs while reading the file.
245 //! \exception std::bad_alloc is thrown if memory for the data cannot be allocated.
246 uint8_t * StELFFile::getSegmentDataAtIndex(unsigned inIndex)
247 {
248         return readSegmentData(m_programHeaders[inIndex]);
249 }
250
251 //! The pointer returned from this method must be freed with the delete array operator (i.e., delete []).
252 //! If either the segment offset (p_offset) or the segment file size (p_filesz) are 0, then NULL will
253 //! be returned instead.
254 //!
255 //! The data is read directly from the input stream passed into the constructor. The stream must
256 //! still be open, or an exception will be thrown.
257 //!
258 //! \exception StELFFileException is thrown if an error occurs while reading the file.
259 //! \exception std::bad_alloc is thrown if memory for the data cannot be allocated.
260 uint8_t * StELFFile::getSegmentData(const_segment_iterator inSegment)
261 {
262         return readSegmentData(*inSegment);
263 }
264         
265 //! \exception StELFFileException is thrown if an error occurs while reading the file.
266 //! \exception std::bad_alloc is thrown if memory for the data cannot be allocated.
267 uint8_t * StELFFile::readSegmentData(const Elf32_Phdr & inHeader)
268 {
269         // check for empty data
270         if (inHeader.p_offset == 0 || inHeader.p_filesz== 0)
271                 return NULL;
272                 
273         uint8_t * segmentData = new uint8_t[inHeader.p_filesz];
274         
275         try
276         {
277                 m_stream.seekg(inHeader.p_offset, std::ios::beg);
278                 m_stream.read(reinterpret_cast<char *>(segmentData), inHeader.p_filesz);
279                 if (m_stream.bad())
280                         throw StELFFileException("could not read entire segment");
281         }
282         catch (StELFFileException)
283         {
284                 throw;
285         }
286         catch (...)
287         {
288                 throw StELFFileException("error reading segment data");
289         }
290         
291         return segmentData;
292 }
293
294 //! If the index is out of range, or if there is no string table in the file, then
295 //! an empty string will be returned instead. This will also happen when the index
296 //! is either 0 or the last byte in the table, since the table begins and ends with
297 //! zero bytes.
298 std::string StELFFile::getSectionNameAtIndex(unsigned inIndex)
299 {
300         // make sure there's a section name string table
301         if (m_header.e_shstrndx == SHN_UNDEF)
302                 return std::string("");
303         
304         return getStringAtIndex(m_header.e_shstrndx, inIndex);
305 }
306
307 //! \exception std::invalid_argument is thrown if the section identified by \a
308 //!             inStringTableSectionIndex is not actually a string table, or if \a
309 //!             inStringIndex is out of range for the string table.
310 std::string StELFFile::getStringAtIndex(unsigned inStringTableSectionIndex, unsigned inStringIndex)
311 {
312         // check section type
313         const Elf32_Shdr & header = getSectionAtIndex(inStringTableSectionIndex);
314         if (header.sh_type != SHT_STRTAB)
315                 throw std::invalid_argument("inStringTableSectionIndex");
316         
317         if (inStringIndex >= header.sh_size)
318                 throw std::invalid_argument("inStringTableSectionIndex");
319         
320         // check cache
321         SectionDataInfo & info = getCachedSectionData(inStringTableSectionIndex);
322         return std::string(&reinterpret_cast<char *>(info.m_data)[inStringIndex]);
323 }
324
325 StELFFile::SectionDataInfo & StELFFile::getCachedSectionData(unsigned inSectionIndex)
326 {
327         // check cache
328         SectionDataMap::iterator it = m_sectionDataCache.find(inSectionIndex);
329         if (it != m_sectionDataCache.end())
330                 return it->second;
331         
332         // not in cache, add it
333         const Elf32_Shdr & header = getSectionAtIndex(inSectionIndex);
334         uint8_t * data = getSectionDataAtIndex(inSectionIndex);
335         
336         SectionDataInfo info;
337         info.m_data = data;
338         info.m_size = header.sh_size;
339         
340         m_sectionDataCache[inSectionIndex] = info;
341         return m_sectionDataCache[inSectionIndex];
342 }
343
344 //! The number of entries in the symbol table is the symbol table section size
345 //! divided by the size of each symbol entry (the #Elf32_Shdr::sh_entsize field of the
346 //! symbol table section header).
347 unsigned StELFFile::getSymbolCount()
348 {
349         if (m_symbolTableIndex == SHN_UNDEF)
350                 return 0;
351         
352         const Elf32_Shdr & header = getSectionAtIndex(m_symbolTableIndex);
353         return header.sh_size / header.sh_entsize;
354 }
355
356 //! \exception std::invalid_argument is thrown if \a inIndex is out of range.]
357 //!
358 const Elf32_Sym & StELFFile::getSymbolAtIndex(unsigned inIndex)
359 {
360         // get section data
361         const Elf32_Shdr & header = getSectionAtIndex(m_symbolTableIndex);
362         SectionDataInfo & info = getCachedSectionData(m_symbolTableIndex);
363         
364         // has the symbol table been byte swapped yet?
365         if (!info.m_swapped)
366         {
367                 byteSwapSymbolTable(header, info);
368         }
369         
370         unsigned symbolOffset = header.sh_entsize * inIndex;
371         if (symbolOffset >= info.m_size)
372         {
373                 throw std::invalid_argument("inIndex");
374         }
375         
376         Elf32_Sym * symbol = reinterpret_cast<Elf32_Sym *>(&info.m_data[symbolOffset]);
377         return *symbol;
378 }
379
380 void StELFFile::byteSwapSymbolTable(const Elf32_Shdr & header, SectionDataInfo & info)
381 {
382         unsigned symbolCount = getSymbolCount();
383         unsigned i = 0;
384         unsigned symbolOffset = 0;
385         
386         for (; i < symbolCount; ++i, symbolOffset += header.sh_entsize)
387         {
388                 Elf32_Sym * symbol = reinterpret_cast<Elf32_Sym *>(&info.m_data[symbolOffset]);
389                 symbol->st_name = ENDIAN_LITTLE_TO_HOST_U32(symbol->st_name);
390                 symbol->st_value = ENDIAN_LITTLE_TO_HOST_U32(symbol->st_value);
391                 symbol->st_size = ENDIAN_LITTLE_TO_HOST_U32(symbol->st_size);
392                 symbol->st_shndx = ENDIAN_LITTLE_TO_HOST_U16(symbol->st_shndx);
393         }
394         
395         // remember that we've byte swapped the symbols
396         info.m_swapped = true;
397 }
398
399 unsigned StELFFile::getSymbolNameStringTableIndex() const
400 {
401         const Elf32_Shdr & header = getSectionAtIndex(m_symbolTableIndex);
402         return header.sh_link;
403 }
404
405 std::string StELFFile::getSymbolName(const Elf32_Sym & inSymbol)
406 {
407         unsigned symbolStringTableIndex = getSymbolNameStringTableIndex();
408         return getStringAtIndex(symbolStringTableIndex, inSymbol.st_name);
409 }
410
411 //! Returns STN_UNDEF if it cannot find a symbol at the given \a symbolAddress.
412 unsigned StELFFile::getIndexOfSymbolAtAddress(uint32_t symbolAddress, bool strict)
413 {
414         unsigned symbolCount = getSymbolCount();
415         unsigned symbolIndex = 0;
416         for (; symbolIndex < symbolCount; ++symbolIndex)
417         {
418                 const Elf32_Sym & symbol = getSymbolAtIndex(symbolIndex);
419                 
420                 // the GHS toolchain puts in STT_FUNC symbols marking the beginning and ending of each
421                 // file. if the entry point happens to be at the beginning of the file, the beginning-
422                 // of-file symbol will have the same value and type. fortunately, the size of these
423                 // symbols is 0 (or seems to be). we also ignore symbols that start with two dots just
424                 // in case.
425                 if (symbol.st_value == symbolAddress && (strict && ELF32_ST_TYPE(symbol.st_info) == STT_FUNC && symbol.st_size != 0))
426                 {
427                         std::string symbolName = getSymbolName(symbol);
428                         
429                         // ignore symbols that start with two dots
430                         if (symbolName[0] == '.' && symbolName[1] == '.')
431                                 continue;
432                         
433                         // found the symbol!
434                         return symbolIndex;
435                 }
436         }
437         
438         return STN_UNDEF;
439 }
440
441 ARMSymbolType_t StELFFile::getTypeOfSymbolAtIndex(unsigned symbolIndex)
442 {
443         ARMSymbolType_t symType = eARMSymbol;
444         const Elf32_Sym & symbol = getSymbolAtIndex(symbolIndex);
445         
446         if (m_elfVariant == eGHSVariant)
447         {
448                 if (symbol.st_other & STO_THUMB)
449                         symType = eThumbSymbol;
450         }
451         else
452         {
453                 unsigned mappingSymStart = 1;
454                 unsigned mappingSymCount = getSymbolCount() - 1;        // don't include first undefined symbol
455                 bool mapSymsFirst = (m_header.e_flags & EF_ARM_MAPSYMSFIRST) != 0;
456                 if (mapSymsFirst)
457                 {
458                         // first symbol '$m' is number of mapping syms
459                         const Elf32_Sym & mappingSymCountSym = getSymbolAtIndex(1);
460                         if (getSymbolName(mappingSymCountSym) == MAPPING_SYMBOL_COUNT_TAGSYM)
461                         {
462                                 mappingSymCount = mappingSymCountSym.st_value;
463                                 mappingSymStart = 2;
464                         }
465
466                 }
467                 
468                 uint32_t lastMappingSymAddress = 0;
469                 unsigned mappingSymIndex = mappingSymStart;
470                 for (; mappingSymIndex < mappingSymCount + mappingSymStart; ++mappingSymIndex)
471                 {
472                         const Elf32_Sym & mappingSym = getSymbolAtIndex(mappingSymIndex);
473                         std::string mappingSymName = getSymbolName(mappingSym);
474                         ARMSymbolType_t nextSymType = eUnknownSymbol;
475                         
476                         if (mappingSymName == ARM_SEQUENCE_MAPSYM)
477                                 symType = eARMSymbol;
478                         else if (mappingSymName == DATA_SEQUENCE_MAPSYM)
479                                 symType = eDataSymbol;
480                         else if (mappingSymName == THUMB_SEQUENCE_MAPSYM)
481                                 symType = eThumbSymbol;
482                         
483                         if (nextSymType != eUnknownSymbol)
484                         {
485                                 if (symbol.st_value >= lastMappingSymAddress && symbol.st_value < mappingSym.st_value)
486                                         break;
487                                 
488                                 symType = nextSymType;
489                                 lastMappingSymAddress = mappingSym.st_value;
490                         }
491                 }
492         }
493         
494         return symType;
495 }
496
497 void StELFFile::dumpSections()
498 {
499         unsigned count = getSectionCount();
500         unsigned i = 0;
501         
502         const char * sectionTypes[12] = { "NULL", "PROGBITS", "SYMTAB", "STRTAB", "RELA", "HASH", "DYNAMIC", "NOTE", "NOBITS", "REL", "SHLIB", "DYNSYM" };
503         
504         for (; i < count; ++i)
505         {
506                 const Elf32_Shdr & header = getSectionAtIndex(i);
507                 std::string name = getSectionNameAtIndex(header.sh_name);
508
509                 if (header.sh_type < sizeof(sectionTypes) / sizeof(sectionTypes[0])) {
510                         printf("%s: %s, 0x%08x, 0x%08x, 0x%08x, %d, %d, %d\n",
511                                name.c_str(), sectionTypes[header.sh_type],
512                                header.sh_addr, header.sh_offset,
513                                header.sh_size, header.sh_link,
514                                header.sh_info, header.sh_entsize);
515                 } else {
516                         printf("%s: 0x%02x, 0x%08x, 0x%08x, 0x%08x, %d, %d, %d\n",
517                                name.c_str(), header.sh_type,
518                                header.sh_addr, header.sh_offset,
519                                header.sh_size, header.sh_link,
520                                header.sh_info, header.sh_entsize);
521                 }
522         }
523 }
524
525 void StELFFile::dumpSymbolTable()
526 {
527         const char * symbolTypes[5] = { "NOTYPE", "OBJECT", "FUNC", "SECTION", "FILE" };
528         const char * symbolBinding[3] = { "LOCAL", "GLOBAL", "WEAK" };
529         
530         unsigned count = getSymbolCount();
531         unsigned i = 0;
532         
533         for (; i < count; ++i)
534         {
535                 const Elf32_Sym & symbol = getSymbolAtIndex(i);
536                 std::string name = getSymbolName(symbol);
537                 
538                 printf("'%s': %s, %s, 0x%08x, 0x%08x, %d. 0x%08x\n", name.c_str(), symbolTypes[ELF32_ST_TYPE(symbol.st_info)], symbolBinding[ELF32_ST_BIND(symbol.st_info)], symbol.st_value, symbol.st_size, symbol.st_shndx, symbol.st_other);
539         }
540 }
541
542
543