1 // Copyright 2016, VIXL authors
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are met:
6 //
7 //   * Redistributions of source code must retain the above copyright notice,
8 //     this list of conditions and the following disclaimer.
9 //   * Redistributions in binary form must reproduce the above copyright notice,
10 //     this list of conditions and the following disclaimer in the documentation
11 //     and/or other materials provided with the distribution.
12 //   * Neither the name of ARM Limited nor the names of its contributors may be
13 //     used to endorse or promote products derived from this software without
14 //     specific prior written permission.
15 //
16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND
17 // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
20 // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22 // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23 // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 
27 
28 // The example assumes support for ELF binaries.
29 #ifdef __linux__
30 
31 extern "C" {
32 #include <elf.h>
33 #include <fcntl.h>
34 #include <stdint.h>
35 #include <sys/mman.h>
36 #include <sys/stat.h>
37 #include <sys/types.h>
38 #include <unistd.h>
39 }
40 
41 #include <cerrno>
42 #include <iostream>
43 #include <map>
44 #include <string>
45 
46 #include "globals-vixl.h"
47 #include "aarch32/disasm-aarch32.h"
48 #include "aarch32/instructions-aarch32.h"
49 
50 class Symbol {
51   Elf32_Addr addr_;
52   int32_t offset_;
53   uint32_t size_;
54   int section_;
55   std::string name_;
56 
57  public:
Symbol(const char * name,Elf32_Addr addr,int32_t offset,uint32_t size,int section)58   Symbol(const char* name,
59          Elf32_Addr addr,
60          int32_t offset,
61          uint32_t size,
62          int section)
63       : addr_(addr),
64         offset_(offset),
65         size_(size),
66         section_(section),
67         name_(name) {}
Symbol(const Symbol & ref)68   Symbol(const Symbol& ref)
69       : addr_(ref.addr_),
70         offset_(ref.offset_),
71         size_(ref.size_),
72         section_(ref.section_),
73         name_(ref.name_) {}
74 
GetAddress() const75   Elf32_Addr GetAddress() const { return addr_; }
GetMemoryAddress() const76   Elf32_Addr GetMemoryAddress() const { return (addr_ & ~1) + offset_; }
GetSize() const77   uint32_t GetSize() const { return size_; }
GetName() const78   const std::string& GetName() const { return name_; }
GetSection() const79   int GetSection() const { return section_; }
80 };
81 
82 
83 class SymbolTable : public std::map<Elf32_Addr, Symbol> {
84  public:
insert(const Symbol & sym)85   void insert(const Symbol& sym) {
86     VIXL_ASSERT(find(sym.GetAddress()) == end());
87     std::map<Elf32_Addr, Symbol>::insert(
88         std::make_pair(sym.GetMemoryAddress(), sym));
89   }
90 };
91 
92 
93 class SectionLocator {
94   const Elf32_Shdr* shdr_;
95   int nsections_;
96   const char* shstrtab_;
97 
98  public:
SectionLocator(const Elf32_Ehdr * ehdr)99   explicit SectionLocator(const Elf32_Ehdr* ehdr) {
100     shdr_ = reinterpret_cast<const Elf32_Shdr*>(
101         reinterpret_cast<const char*>(ehdr) + ehdr->e_shoff);
102     // shstrtab holds the section names as an offset in the file.
103     shstrtab_ =
104         reinterpret_cast<const char*>(ehdr) + shdr_[ehdr->e_shstrndx].sh_offset;
105     nsections_ = ehdr->e_shnum;
106   }
107 
Locate(Elf32_Word type,const std::string & section_name) const108   const Elf32_Shdr* Locate(Elf32_Word type,
109                            const std::string& section_name) const {
110     for (int shnum = 1; shnum < nsections_; shnum++) {
111       if ((shdr_[shnum].sh_type == type) &&
112           std::string(shstrtab_ + shdr_[shnum].sh_name) == section_name) {
113         return &shdr_[shnum];
114       }
115     }
116     return NULL;
117   }
118 };
119 
120 
121 template <typename VISITOR>
LocateSymbols(const Elf32_Ehdr * ehdr,const Elf32_Shdr * symtab,const Elf32_Shdr * strtab,VISITOR * visitor)122 void LocateSymbols(const Elf32_Ehdr* ehdr,
123                    const Elf32_Shdr* symtab,
124                    const Elf32_Shdr* strtab,
125                    VISITOR* visitor) {
126   if ((symtab != NULL) && (strtab != NULL)) {
127     const Elf32_Shdr* shdr = reinterpret_cast<const Elf32_Shdr*>(
128         reinterpret_cast<const char*>(ehdr) + ehdr->e_shoff);
129 
130     const char* symnames =
131         reinterpret_cast<const char*>(ehdr) + strtab->sh_offset;
132     VIXL_CHECK(symnames != NULL);
133 
134     int nsym = symtab->sh_size / symtab->sh_entsize;
135     const Elf32_Sym* sym = reinterpret_cast<const Elf32_Sym*>(
136         reinterpret_cast<const char*>(ehdr) + symtab->sh_offset);
137     for (int snum = 0; snum < nsym; snum++) {
138       if ((sym[snum].st_shndx > 0) && (sym[snum].st_shndx < ehdr->e_shnum) &&
139           (sym[snum].st_value != 0) &&
140           (shdr[sym[snum].st_shndx].sh_type == SHT_PROGBITS) &&
141           ((ELF32_ST_BIND(sym[snum].st_info) == STB_LOCAL) ||
142            (ELF32_ST_BIND(sym[snum].st_info) == STB_GLOBAL)) &&
143           (ELF32_ST_TYPE(sym[snum].st_info) == STT_FUNC)) {
144         visitor->visit(symnames + sym[snum].st_name, sym[snum]);
145       }
146     }
147   }
148 }
149 
150 
151 class DynamicSymbolVisitor {
152   SymbolTable* symbols_;
153 
154  public:
DynamicSymbolVisitor(SymbolTable * symbols)155   explicit DynamicSymbolVisitor(SymbolTable* symbols) : symbols_(symbols) {}
visit(const char * symname,const Elf32_Sym & sym)156   void visit(const char* symname, const Elf32_Sym& sym) {
157     symbols_->insert(
158         Symbol(symname, sym.st_value, 0, sym.st_size, sym.st_shndx));
159   }
160 };
161 
162 
163 class StaticSymbolVisitor {
164   const Elf32_Ehdr* ehdr_;
165   const Elf32_Shdr* shdr_;
166   SymbolTable* symbols_;
167 
168  public:
StaticSymbolVisitor(const Elf32_Ehdr * ehdr,SymbolTable * symbols)169   StaticSymbolVisitor(const Elf32_Ehdr* ehdr, SymbolTable* symbols)
170       : ehdr_(ehdr),
171         shdr_(reinterpret_cast<const Elf32_Shdr*>(
172             reinterpret_cast<const char*>(ehdr) + ehdr->e_shoff)),
173         symbols_(symbols) {}
visit(const char * symname,const Elf32_Sym & sym)174   void visit(const char* symname, const Elf32_Sym& sym) {
175     if (ehdr_->e_type == ET_REL) {
176       symbols_->insert(Symbol(symname,
177                               sym.st_value,
178                               shdr_[sym.st_shndx].sh_offset,
179                               sym.st_size,
180                               sym.st_shndx));
181     } else {
182       symbols_->insert(
183           Symbol(symname,
184                  sym.st_value,
185                  shdr_[sym.st_shndx].sh_offset - shdr_[sym.st_shndx].sh_addr,
186                  sym.st_size,
187                  sym.st_shndx));
188     }
189   }
190 };
191 
192 
usage()193 void usage() {
194   std::cout << "usage: disasm-a32 <file>\n"
195                "where <file> is an ELF ARM binaryfile, either an executable, "
196                "a shared object, or an object file."
197             << std::endl;
198 }
199 
200 
main(int argc,char ** argv)201 int main(int argc, char** argv) {
202   const int kErrorNotARMELF32 = -1;
203   const int kErrorArguments = -2;
204   if (argc < 2) {
205     usage();
206     return kErrorArguments;
207   }
208 
209   const char* filename = argv[1];
210   struct stat sb;
211 
212 
213   if (lstat(filename, &sb) == -1) {
214     std::cerr << "Cannot stat this file" << filename << std::endl;
215     return errno;
216   }
217 
218   if (S_ISLNK(sb.st_mode)) {
219     static char linkname[4096];
220     filename = realpath(argv[1], linkname);
221     if (lstat(linkname, &sb) == -1) {
222       std::cerr << "Cannot stat this file: " << linkname << std::endl;
223       return errno;
224     }
225   }
226 
227   int elf_in;
228   if ((elf_in = open(filename, O_RDONLY)) < 0) {
229     std::cerr << "Cannot open: " << argv[1];
230     if (filename != argv[1]) std::cerr << " aka " << filename;
231     std::cerr << std::endl;
232     return errno;
233   }
234 
235   char* base_addr;
236   VIXL_CHECK((base_addr = reinterpret_cast<char*>(
237                   mmap(0, sb.st_size, PROT_READ, MAP_PRIVATE, elf_in, 0))) !=
238              0);
239 
240   const Elf32_Ehdr* ehdr = reinterpret_cast<const Elf32_Ehdr*>(base_addr);
241   if ((ehdr->e_ident[0] != 0x7f) || (ehdr->e_ident[1] != 'E') ||
242       (ehdr->e_ident[2] != 'L') || (ehdr->e_ident[3] != 'F') ||
243       (ehdr->e_ehsize != sizeof(Elf32_Ehdr))) {
244     std::cerr << "This file is not an 32-bit ELF file." << std::endl;
245     munmap(base_addr, sb.st_size);
246     return kErrorNotARMELF32;
247   }
248 
249   if (ehdr->e_machine != EM_ARM) {
250     std::cerr << "This file is not using the ARM isa." << std::endl;
251     munmap(base_addr, sb.st_size);
252     return kErrorNotARMELF32;
253   }
254 
255   // shstrtab holds the section names as an offset in the file.
256   const Elf32_Shdr* shdr =
257       reinterpret_cast<const Elf32_Shdr*>(base_addr + ehdr->e_shoff);
258 
259   SectionLocator section_locator(ehdr);
260 
261   SymbolTable symbol_names;
262 
263   // Traverse the dynamic symbols defined in any text section
264   DynamicSymbolVisitor dynamic_visitor(&symbol_names);
265   LocateSymbols(ehdr,
266                 section_locator.Locate(SHT_DYNSYM, ".dynsym"),
267                 section_locator.Locate(SHT_STRTAB, ".dynstr"),
268                 &dynamic_visitor);
269 
270   // Traverse the static symbols defined in the any test section
271   StaticSymbolVisitor static_visitor(ehdr, &symbol_names);
272   LocateSymbols(ehdr,
273                 section_locator.Locate(SHT_SYMTAB, ".symtab"),
274                 section_locator.Locate(SHT_STRTAB, ".strtab"),
275                 &static_visitor);
276 
277 
278   vixl::aarch32::PrintDisassembler dis(std::cout, 0);
279   for (SymbolTable::iterator sres = symbol_names.begin();
280        sres != symbol_names.end();
281        sres++) {
282     const Symbol& symbol = sres->second;
283     uint32_t func_addr = symbol.GetAddress();
284     uint32_t func_size = symbol.GetSize();
285     if (func_size == 0) {
286       SymbolTable::iterator next_func = sres;
287       next_func++;
288       if (next_func == symbol_names.end()) {
289         const Elf32_Shdr& shndx = shdr[sres->second.GetSection()];
290         func_size = (shndx.sh_offset + shndx.sh_size) - sres->first;
291       } else {
292         func_size = next_func->first - sres->first;
293       }
294     }
295 
296     std::cout << "--- " << symbol.GetName() << ":" << std::endl;
297     if ((func_addr & 1) == 1) {
298       func_addr &= ~1;
299       dis.SetCodeAddress(func_addr);
300       dis.DisassembleT32Buffer(reinterpret_cast<uint16_t*>(
301                                    base_addr + symbol.GetMemoryAddress()),
302                                func_size);
303     } else {
304       dis.SetCodeAddress(func_addr);
305       dis.DisassembleA32Buffer(reinterpret_cast<uint32_t*>(
306                                    base_addr + symbol.GetMemoryAddress()),
307                                func_size);
308     }
309   }
310   munmap(base_addr, sb.st_size);
311   return 0;
312 }
313 
314 
315 #else
316 
317 #include "globals-vixl.h"
318 
319 // TODO: Implement this example for macOS.
main(void)320 int main(void) {
321   VIXL_WARNING("This example has not been implemented for macOS.");
322   return 0;
323 }
324 
325 #endif  // __linux__
326