| // Copyright 2016, VIXL authors |
| // All rights reserved. |
| // |
| // Redistribution and use in source and binary forms, with or without |
| // modification, are permitted provided that the following conditions are met: |
| // |
| // * Redistributions of source code must retain the above copyright notice, |
| // this list of conditions and the following disclaimer. |
| // * Redistributions in binary form must reproduce the above copyright notice, |
| // this list of conditions and the following disclaimer in the documentation |
| // and/or other materials provided with the distribution. |
| // * Neither the name of ARM Limited nor the names of its contributors may be |
| // used to endorse or promote products derived from this software without |
| // specific prior written permission. |
| // |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND |
| // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
| // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE |
| // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
| // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
| // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
| // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| |
| |
| // The example assumes support for ELF binaries. |
| #ifdef __linux__ |
| |
| extern "C" { |
| #include <elf.h> |
| #include <fcntl.h> |
| #include <stdint.h> |
| #include <sys/mman.h> |
| #include <sys/stat.h> |
| #include <sys/types.h> |
| #include <unistd.h> |
| } |
| |
| #include <cerrno> |
| #include <iostream> |
| #include <map> |
| #include <string> |
| |
| #include "globals-vixl.h" |
| #include "aarch32/disasm-aarch32.h" |
| #include "aarch32/instructions-aarch32.h" |
| |
| class Symbol { |
| Elf32_Addr addr_; |
| int32_t offset_; |
| uint32_t size_; |
| int section_; |
| std::string name_; |
| |
| public: |
| Symbol(const char* name, |
| Elf32_Addr addr, |
| int32_t offset, |
| uint32_t size, |
| int section) |
| : addr_(addr), |
| offset_(offset), |
| size_(size), |
| section_(section), |
| name_(name) {} |
| Symbol(const Symbol& ref) |
| : addr_(ref.addr_), |
| offset_(ref.offset_), |
| size_(ref.size_), |
| section_(ref.section_), |
| name_(ref.name_) {} |
| |
| Elf32_Addr GetAddress() const { return addr_; } |
| Elf32_Addr GetMemoryAddress() const { return (addr_ & ~1) + offset_; } |
| uint32_t GetSize() const { return size_; } |
| const std::string& GetName() const { return name_; } |
| int GetSection() const { return section_; } |
| }; |
| |
| |
| class SymbolTable : public std::map<Elf32_Addr, Symbol> { |
| public: |
| void insert(const Symbol& sym) { |
| VIXL_ASSERT(find(sym.GetAddress()) == end()); |
| std::map<Elf32_Addr, Symbol>::insert( |
| std::make_pair(sym.GetMemoryAddress(), sym)); |
| } |
| }; |
| |
| |
| class SectionLocator { |
| const Elf32_Shdr* shdr_; |
| int nsections_; |
| const char* shstrtab_; |
| |
| public: |
| explicit SectionLocator(const Elf32_Ehdr* ehdr) { |
| shdr_ = reinterpret_cast<const Elf32_Shdr*>( |
| reinterpret_cast<const char*>(ehdr) + ehdr->e_shoff); |
| // shstrtab holds the section names as an offset in the file. |
| shstrtab_ = |
| reinterpret_cast<const char*>(ehdr) + shdr_[ehdr->e_shstrndx].sh_offset; |
| nsections_ = ehdr->e_shnum; |
| } |
| |
| const Elf32_Shdr* Locate(Elf32_Word type, |
| const std::string& section_name) const { |
| for (int shnum = 1; shnum < nsections_; shnum++) { |
| if ((shdr_[shnum].sh_type == type) && |
| std::string(shstrtab_ + shdr_[shnum].sh_name) == section_name) { |
| return &shdr_[shnum]; |
| } |
| } |
| return NULL; |
| } |
| }; |
| |
| |
| template <typename VISITOR> |
| void LocateSymbols(const Elf32_Ehdr* ehdr, |
| const Elf32_Shdr* symtab, |
| const Elf32_Shdr* strtab, |
| VISITOR* visitor) { |
| if ((symtab != NULL) && (strtab != NULL)) { |
| const Elf32_Shdr* shdr = reinterpret_cast<const Elf32_Shdr*>( |
| reinterpret_cast<const char*>(ehdr) + ehdr->e_shoff); |
| |
| const char* symnames = |
| reinterpret_cast<const char*>(ehdr) + strtab->sh_offset; |
| VIXL_CHECK(symnames != NULL); |
| |
| int nsym = symtab->sh_size / symtab->sh_entsize; |
| const Elf32_Sym* sym = reinterpret_cast<const Elf32_Sym*>( |
| reinterpret_cast<const char*>(ehdr) + symtab->sh_offset); |
| for (int snum = 0; snum < nsym; snum++) { |
| if ((sym[snum].st_shndx > 0) && (sym[snum].st_shndx < ehdr->e_shnum) && |
| (sym[snum].st_value != 0) && |
| (shdr[sym[snum].st_shndx].sh_type == SHT_PROGBITS) && |
| ((ELF32_ST_BIND(sym[snum].st_info) == STB_LOCAL) || |
| (ELF32_ST_BIND(sym[snum].st_info) == STB_GLOBAL)) && |
| (ELF32_ST_TYPE(sym[snum].st_info) == STT_FUNC)) { |
| visitor->visit(symnames + sym[snum].st_name, sym[snum]); |
| } |
| } |
| } |
| } |
| |
| |
| class DynamicSymbolVisitor { |
| SymbolTable* symbols_; |
| |
| public: |
| explicit DynamicSymbolVisitor(SymbolTable* symbols) : symbols_(symbols) {} |
| void visit(const char* symname, const Elf32_Sym& sym) { |
| symbols_->insert( |
| Symbol(symname, sym.st_value, 0, sym.st_size, sym.st_shndx)); |
| } |
| }; |
| |
| |
| class StaticSymbolVisitor { |
| const Elf32_Ehdr* ehdr_; |
| const Elf32_Shdr* shdr_; |
| SymbolTable* symbols_; |
| |
| public: |
| StaticSymbolVisitor(const Elf32_Ehdr* ehdr, SymbolTable* symbols) |
| : ehdr_(ehdr), |
| shdr_(reinterpret_cast<const Elf32_Shdr*>( |
| reinterpret_cast<const char*>(ehdr) + ehdr->e_shoff)), |
| symbols_(symbols) {} |
| void visit(const char* symname, const Elf32_Sym& sym) { |
| if (ehdr_->e_type == ET_REL) { |
| symbols_->insert(Symbol(symname, |
| sym.st_value, |
| shdr_[sym.st_shndx].sh_offset, |
| sym.st_size, |
| sym.st_shndx)); |
| } else { |
| symbols_->insert( |
| Symbol(symname, |
| sym.st_value, |
| shdr_[sym.st_shndx].sh_offset - shdr_[sym.st_shndx].sh_addr, |
| sym.st_size, |
| sym.st_shndx)); |
| } |
| } |
| }; |
| |
| |
| void usage() { |
| std::cout << "usage: disasm-a32 <file>\n" |
| "where <file> is an ELF ARM binaryfile, either an executable, " |
| "a shared object, or an object file." |
| << std::endl; |
| } |
| |
| |
| int main(int argc, char** argv) { |
| const int kErrorNotARMELF32 = -1; |
| const int kErrorArguments = -2; |
| if (argc < 2) { |
| usage(); |
| return kErrorArguments; |
| } |
| |
| const char* filename = argv[1]; |
| struct stat sb; |
| |
| |
| if (lstat(filename, &sb) == -1) { |
| std::cerr << "Cannot stat this file" << filename << std::endl; |
| return errno; |
| } |
| |
| if (S_ISLNK(sb.st_mode)) { |
| static char linkname[4096]; |
| filename = realpath(argv[1], linkname); |
| if (lstat(linkname, &sb) == -1) { |
| std::cerr << "Cannot stat this file: " << linkname << std::endl; |
| return errno; |
| } |
| } |
| |
| int elf_in; |
| if ((elf_in = open(filename, O_RDONLY)) < 0) { |
| std::cerr << "Cannot open: " << argv[1]; |
| if (filename != argv[1]) std::cerr << " aka " << filename; |
| std::cerr << std::endl; |
| return errno; |
| } |
| |
| char* base_addr; |
| VIXL_CHECK((base_addr = reinterpret_cast<char*>( |
| mmap(0, sb.st_size, PROT_READ, MAP_PRIVATE, elf_in, 0))) != |
| 0); |
| |
| const Elf32_Ehdr* ehdr = reinterpret_cast<const Elf32_Ehdr*>(base_addr); |
| if ((ehdr->e_ident[0] != 0x7f) || (ehdr->e_ident[1] != 'E') || |
| (ehdr->e_ident[2] != 'L') || (ehdr->e_ident[3] != 'F') || |
| (ehdr->e_ehsize != sizeof(Elf32_Ehdr))) { |
| std::cerr << "This file is not an 32-bit ELF file." << std::endl; |
| munmap(base_addr, sb.st_size); |
| return kErrorNotARMELF32; |
| } |
| |
| if (ehdr->e_machine != EM_ARM) { |
| std::cerr << "This file is not using the ARM isa." << std::endl; |
| munmap(base_addr, sb.st_size); |
| return kErrorNotARMELF32; |
| } |
| |
| // shstrtab holds the section names as an offset in the file. |
| const Elf32_Shdr* shdr = |
| reinterpret_cast<const Elf32_Shdr*>(base_addr + ehdr->e_shoff); |
| |
| SectionLocator section_locator(ehdr); |
| |
| SymbolTable symbol_names; |
| |
| // Traverse the dynamic symbols defined in any text section |
| DynamicSymbolVisitor dynamic_visitor(&symbol_names); |
| LocateSymbols(ehdr, |
| section_locator.Locate(SHT_DYNSYM, ".dynsym"), |
| section_locator.Locate(SHT_STRTAB, ".dynstr"), |
| &dynamic_visitor); |
| |
| // Traverse the static symbols defined in the any test section |
| StaticSymbolVisitor static_visitor(ehdr, &symbol_names); |
| LocateSymbols(ehdr, |
| section_locator.Locate(SHT_SYMTAB, ".symtab"), |
| section_locator.Locate(SHT_STRTAB, ".strtab"), |
| &static_visitor); |
| |
| |
| vixl::aarch32::PrintDisassembler dis(std::cout, 0); |
| for (SymbolTable::iterator sres = symbol_names.begin(); |
| sres != symbol_names.end(); |
| sres++) { |
| const Symbol& symbol = sres->second; |
| uint32_t func_addr = symbol.GetAddress(); |
| uint32_t func_size = symbol.GetSize(); |
| if (func_size == 0) { |
| SymbolTable::iterator next_func = sres; |
| next_func++; |
| if (next_func == symbol_names.end()) { |
| const Elf32_Shdr& shndx = shdr[sres->second.GetSection()]; |
| func_size = (shndx.sh_offset + shndx.sh_size) - sres->first; |
| } else { |
| func_size = next_func->first - sres->first; |
| } |
| } |
| |
| std::cout << "--- " << symbol.GetName() << ":" << std::endl; |
| if ((func_addr & 1) == 1) { |
| func_addr &= ~1; |
| dis.SetCodeAddress(func_addr); |
| dis.DisassembleT32Buffer(reinterpret_cast<uint16_t*>( |
| base_addr + symbol.GetMemoryAddress()), |
| func_size); |
| } else { |
| dis.SetCodeAddress(func_addr); |
| dis.DisassembleA32Buffer(reinterpret_cast<uint32_t*>( |
| base_addr + symbol.GetMemoryAddress()), |
| func_size); |
| } |
| } |
| munmap(base_addr, sb.st_size); |
| return 0; |
| } |
| |
| |
| #else |
| |
| #include "globals-vixl.h" |
| |
| // TODO: Implement this example for macOS. |
| int main(void) { |
| VIXL_WARNING("This example has not been implemented for macOS."); |
| return 0; |
| } |
| |
| #endif // __linux__ |