├── elf ├── .gitignore ├── to_hex.hh ├── mmap_loader.cc ├── Makefile ├── common.hh ├── enum-print.py ├── elf.cc ├── elf++.hh └── data.hh ├── test ├── golden-gcc-4.9.2 │ ├── example │ ├── NOTES │ ├── lines │ ├── segments │ ├── tree │ ├── syms │ └── sections ├── golden-gcc-6.2.1-s390x │ ├── example │ ├── NOTES │ ├── lines │ ├── segments │ ├── tree │ ├── sections │ └── syms ├── example.c └── test.sh ├── dwarf ├── .gitignore ├── elf.cc ├── Makefile ├── rangelist.cc ├── die_str_map.cc ├── small_vector.hh ├── cursor.cc ├── die.cc ├── abbrev.cc ├── internal.hh ├── attrs.cc ├── value.cc ├── dwarf.cc ├── line.cc └── expr.cc ├── examples ├── .gitignore ├── dump-lines.cc ├── Makefile ├── dump-segments.cc ├── dump-tree.cc ├── dump-sections.cc ├── dump-syms.cc └── find-pc.cc ├── .dir-locals.el ├── .travis.yml ├── Makefile ├── LICENSE └── README.md /elf/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | to_string.cc 3 | libelf++.a 4 | libelf++.so 5 | libelf++.so.* 6 | libelf++.pc 7 | -------------------------------------------------------------------------------- /test/golden-gcc-4.9.2/example: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TartanLlama/libelfin/HEAD/test/golden-gcc-4.9.2/example -------------------------------------------------------------------------------- /dwarf/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | to_string.cc 3 | libdwarf++.a 4 | libdwarf++.so 5 | libdwarf++.so.* 6 | libdwarf++.pc 7 | /doc/ 8 | -------------------------------------------------------------------------------- /examples/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | .*.d 3 | dump-sections 4 | dump-segments 5 | dump-syms 6 | dump-lines 7 | dump-tree 8 | find-pc 9 | -------------------------------------------------------------------------------- /test/golden-gcc-6.2.1-s390x/example: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TartanLlama/libelfin/HEAD/test/golden-gcc-6.2.1-s390x/example -------------------------------------------------------------------------------- /.dir-locals.el: -------------------------------------------------------------------------------- 1 | ((c-mode 2 | (indent-tabs-mode . nil) 3 | (c-file-style . "bsd")) 4 | (c++-mode 5 | (indent-tabs-mode . nil) 6 | (c-file-style . "bsd")) 7 | ) 8 | -------------------------------------------------------------------------------- /test/golden-gcc-4.9.2/NOTES: -------------------------------------------------------------------------------- 1 | Built with 2 | 3 | $ gcc -o golden-gcc-4.9.2/example -g -fdebug-prefix-map=$PWD=x example.c 4 | 5 | where gcc is version 4.9.2 from Debian. 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: required 2 | # The default environment is Ubuntu 12.04, which only has GCC 4.6. 3 | dist: trusty 4 | language: cpp 5 | script: 6 | - make 7 | - make check 8 | compiler: 9 | - gcc 10 | - clang 11 | -------------------------------------------------------------------------------- /test/golden-gcc-6.2.1-s390x/NOTES: -------------------------------------------------------------------------------- 1 | Built with 2 | 3 | $ gcc -o golden-gcc-6.2.1-s390x/example -g -fdebug-prefix-map=$PWD=x example.c 4 | 5 | using gcc 6.2.1 from Debian on s390x. This binary uses big-endian ELF 6 | and DWARF. 7 | -------------------------------------------------------------------------------- /test/example.c: -------------------------------------------------------------------------------- 1 | int fib(int x) 2 | { 3 | if (x <= 1) 4 | return x; 5 | return fib(x - 1) + fib(x - 2); 6 | } 7 | 8 | int main(int argc, char **argv) 9 | { 10 | return fib(10); 11 | } 12 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | $(MAKE) -C elf 3 | $(MAKE) -C dwarf 4 | 5 | install: 6 | $(MAKE) -C elf install 7 | $(MAKE) -C dwarf install 8 | 9 | clean: 10 | $(MAKE) -C elf clean 11 | $(MAKE) -C dwarf clean 12 | 13 | check: 14 | cd test && ./test.sh 15 | -------------------------------------------------------------------------------- /test/golden-gcc-4.9.2/lines: -------------------------------------------------------------------------------- 1 | --- <0> 2 | x/example.c 2 0x4004b6 3 | x/example.c 3 0x4004c2 4 | x/example.c 4 0x4004c8 5 | x/example.c 5 0x4004cd 6 | x/example.c 6 0x4004eb 7 | x/example.c 9 0x4004f2 8 | x/example.c 10 0x400501 9 | x/example.c 11 0x40050b 10 | 11 | -------------------------------------------------------------------------------- /test/golden-gcc-6.2.1-s390x/lines: -------------------------------------------------------------------------------- 1 | --- <0> 2 | x/example.c 2 0x768 3 | x/example.c 3 0x77e 4 | x/example.c 4 0x78a 5 | x/example.c 5 0x792 6 | x/example.c 6 0x7ce 7 | x/example.c 9 0x7e0 8 | x/example.c 10 0x7fc 9 | x/example.c 11 0x80e 10 | 11 | -------------------------------------------------------------------------------- /elf/to_hex.hh: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013 Austin T. Clements. All rights reserved. 2 | // Use of this source code is governed by an MIT license 3 | // that can be found in the LICENSE file. 4 | 5 | #ifndef _ELFPP_TO_HEX_HH_ 6 | #define _ELFPP_TO_HEX_HH_ 7 | 8 | #include 9 | #include 10 | 11 | template 12 | std::string 13 | to_hex(T v) 14 | { 15 | static_assert(std::is_integral::value, 16 | "to_hex applied to non-integral type"); 17 | if (v == 0) 18 | return std::string("0"); 19 | char buf[sizeof(T)*2 + 1]; 20 | char *pos = &buf[sizeof(buf)-1]; 21 | *pos-- = '\0'; 22 | while (v && pos >= buf) { 23 | int digit = v & 0xf; 24 | if (digit < 10) 25 | *pos = '0' + digit; 26 | else 27 | *pos = 'a' + (digit - 10); 28 | pos--; 29 | v >>= 4; 30 | } 31 | return std::string(pos + 1); 32 | } 33 | 34 | #endif // _ELFPP_TO_HEX_HH_ 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 Austin T. Clements 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /examples/dump-lines.cc: -------------------------------------------------------------------------------- 1 | #include "elf++.hh" 2 | #include "dwarf++.hh" 3 | 4 | #include 5 | #include 6 | 7 | using namespace std; 8 | 9 | void 10 | dump_line_table(const dwarf::line_table <) 11 | { 12 | for (auto &line : lt) { 13 | if (line.end_sequence) 14 | printf("\n"); 15 | else 16 | printf("%-40s%8d%#20" PRIx64 "\n", line.file->path.c_str(), 17 | line.line, line.address); 18 | } 19 | } 20 | 21 | int 22 | main(int argc, char **argv) 23 | { 24 | if (argc != 2) { 25 | fprintf(stderr, "usage: %s elf-file\n", argv[0]); 26 | return 2; 27 | } 28 | 29 | int fd = open(argv[1], O_RDONLY); 30 | if (fd < 0) { 31 | fprintf(stderr, "%s: %s\n", argv[1], strerror(errno)); 32 | return 1; 33 | } 34 | 35 | elf::elf ef(elf::create_mmap_loader(fd)); 36 | dwarf::dwarf dw(dwarf::elf::create_loader(ef)); 37 | 38 | for (auto cu : dw.compilation_units()) { 39 | printf("--- <%x>\n", (unsigned int)cu.get_section_offset()); 40 | dump_line_table(cu.get_line_table()); 41 | printf("\n"); 42 | } 43 | 44 | return 0; 45 | } 46 | -------------------------------------------------------------------------------- /examples/Makefile: -------------------------------------------------------------------------------- 1 | CXXFLAGS+=-g -O2 -Werror 2 | override CXXFLAGS+=-std=c++0x -Wall 3 | 4 | CLEAN := 5 | 6 | all: dump-sections dump-segments dump-syms dump-tree dump-lines find-pc 7 | 8 | # Find libs 9 | export PKG_CONFIG_PATH=../elf:../dwarf 10 | CPPFLAGS+=$$(pkg-config --cflags libelf++ libdwarf++) 11 | # Statically link against our libs to keep the example binaries simple 12 | # and dependencies correct. 13 | LIBS=../dwarf/libdwarf++.a ../elf/libelf++.a 14 | 15 | # Dependencies 16 | CPPFLAGS+=-MD -MP -MF .$@.d 17 | -include .*.d 18 | 19 | dump-sections: dump-sections.o $(LIBS) 20 | $(LINK.cc) $^ $(LOADLIBES) $(LDLIBS) -o $@ 21 | CLEAN += dump-sections dump-sections.o 22 | 23 | dump-segments: dump-segments.o $(LIBS) 24 | $(LINK.cc) $^ $(LOADLIBES) $(LDLIBS) -o $@ 25 | CLEAN += dump-segments dump-segments.o 26 | 27 | dump-syms: dump-syms.o $(LIBS) 28 | $(LINK.cc) $^ $(LOADLIBES) $(LDLIBS) -o $@ 29 | CLEAN += dump-syms dump-syms.o 30 | 31 | dump-tree: dump-tree.o $(LIBS) 32 | $(LINK.cc) $^ $(LOADLIBES) $(LDLIBS) -o $@ 33 | CLEAN += dump-tree dump-tree.o 34 | 35 | dump-lines: dump-lines.o $(LIBS) 36 | $(LINK.cc) $^ $(LOADLIBES) $(LDLIBS) -o $@ 37 | CLEAN += dump-lines dump-lines.o 38 | 39 | find-pc: find-pc.o $(LIBS) 40 | $(LINK.cc) $^ $(LOADLIBES) $(LDLIBS) -o $@ 41 | CLEAN += find-pc find-pc.o 42 | 43 | clean: 44 | rm -f $(CLEAN) .*.d 45 | -------------------------------------------------------------------------------- /test/golden-gcc-4.9.2/segments: -------------------------------------------------------------------------------- 1 | Type Offset VirtAddr PhysAddr 2 | FileSiz MemSiz Flags Align 3 | phdr 0x0000000000000040 0x0000000000400040 0x0000000000400040 4 | 0x00000000000001c0 0x00000000000001c0 x|r 8 5 | interp 0x0000000000000200 0x0000000000400200 0x0000000000400200 6 | 0x000000000000001c 0x000000000000001c r 1 7 | load 0x0000000000000000 0x0000000000400000 0x0000000000400000 8 | 0x00000000000006e4 0x00000000000006e4 x|r 200000 9 | load 0x00000000000006e8 0x00000000006006e8 0x00000000006006e8 10 | 0x0000000000000228 0x0000000000000230 w|r 200000 11 | dynamic 0x0000000000000700 0x0000000000600700 0x0000000000600700 12 | 0x00000000000001d0 0x00000000000001d0 w|r 8 13 | note 0x000000000000021c 0x000000000040021c 0x000000000040021c 14 | 0x0000000000000044 0x0000000000000044 r 4 15 | (pt)0x6474e550 0x0000000000000594 0x0000000000400594 0x0000000000400594 16 | 0x000000000000003c 0x000000000000003c r 4 17 | (pt)0x6474e551 0x0000000000000000 0x0000000000000000 0x0000000000000000 18 | 0x0000000000000000 0x0000000000000000 w|r 10 19 | -------------------------------------------------------------------------------- /examples/dump-segments.cc: -------------------------------------------------------------------------------- 1 | #include "elf++.hh" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | int main(int argc, char **argv) 9 | { 10 | if (argc != 2) { 11 | fprintf(stderr, "usage: %s elf-file\n", argv[0]); 12 | return 2; 13 | } 14 | 15 | int fd = open(argv[1], O_RDONLY); 16 | if (fd < 0) { 17 | fprintf(stderr, "%s: %s\n", argv[1], strerror(errno)); 18 | return 1; 19 | } 20 | 21 | elf::elf f(elf::create_mmap_loader(fd)); 22 | printf(" %-16s %-16s %-16s %s\n", 23 | "Type", "Offset", "VirtAddr", "PhysAddr"); 24 | printf(" %-16s %-16s %-16s %6s %5s\n", 25 | " ", "FileSiz", "MemSiz", "Flags", "Align"); 26 | for (auto &seg : f.segments()) { 27 | auto &hdr = seg.get_hdr(); 28 | printf(" %-16s 0x%016" PRIx64 " 0x%016" PRIx64 " 0x%016" PRIx64 "\n", 29 | to_string(hdr.type).c_str(), hdr.offset, 30 | hdr.vaddr, hdr.paddr); 31 | printf(" %-16s 0x%016" PRIx64 " 0x%016" PRIx64 " %-5s %-5" PRIx64 "\n", "", 32 | hdr.filesz, hdr.memsz, 33 | to_string(hdr.flags).c_str(), hdr.align); 34 | } 35 | 36 | return 0; 37 | } 38 | 39 | -------------------------------------------------------------------------------- /examples/dump-tree.cc: -------------------------------------------------------------------------------- 1 | #include "elf++.hh" 2 | #include "dwarf++.hh" 3 | 4 | #include 5 | #include 6 | 7 | using namespace std; 8 | 9 | void 10 | dump_tree(const dwarf::die &node, int depth = 0) 11 | { 12 | printf("%*.s<%" PRIx64 "> %s\n", depth, "", 13 | node.get_section_offset(), 14 | to_string(node.tag).c_str()); 15 | for (auto &attr : node.attributes()) 16 | printf("%*.s %s %s\n", depth, "", 17 | to_string(attr.first).c_str(), 18 | to_string(attr.second).c_str()); 19 | for (auto &child : node) 20 | dump_tree(child, depth + 1); 21 | } 22 | 23 | int 24 | main(int argc, char **argv) 25 | { 26 | if (argc != 2) { 27 | fprintf(stderr, "usage: %s elf-file\n", argv[0]); 28 | return 2; 29 | } 30 | 31 | int fd = open(argv[1], O_RDONLY); 32 | if (fd < 0) { 33 | fprintf(stderr, "%s: %s\n", argv[1], strerror(errno)); 34 | return 1; 35 | } 36 | 37 | elf::elf ef(elf::create_mmap_loader(fd)); 38 | dwarf::dwarf dw(dwarf::elf::create_loader(ef)); 39 | 40 | for (auto cu : dw.compilation_units()) { 41 | printf("--- <%" PRIx64 ">\n", cu.get_section_offset()); 42 | dump_tree(cu.root()); 43 | } 44 | 45 | return 0; 46 | } 47 | -------------------------------------------------------------------------------- /examples/dump-sections.cc: -------------------------------------------------------------------------------- 1 | #include "elf++.hh" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | int main(int argc, char **argv) 9 | { 10 | if (argc != 2) { 11 | fprintf(stderr, "usage: %s elf-file\n", argv[0]); 12 | return 2; 13 | } 14 | 15 | int fd = open(argv[1], O_RDONLY); 16 | if (fd < 0) { 17 | fprintf(stderr, "%s: %s\n", argv[1], strerror(errno)); 18 | return 1; 19 | } 20 | 21 | elf::elf f(elf::create_mmap_loader(fd)); 22 | int i = 0; 23 | printf(" [Nr] %-16s %-16s %-16s %s\n", 24 | "Name", "Type", "Address", "Offset"); 25 | printf(" %-16s %-16s %-15s %5s %4s %5s\n", 26 | "Size", "EntSize", "Flags", "Link", "Info", "Align"); 27 | for (auto &sec : f.sections()) { 28 | auto &hdr = sec.get_hdr(); 29 | printf(" [%2d] %-16s %-16s %016" PRIx64 " %08" PRIx64 "\n", i++, 30 | sec.get_name().c_str(), 31 | to_string(hdr.type).c_str(), 32 | hdr.addr, hdr.offset); 33 | printf(" %016zx %016" PRIx64 " %-15s %5s %4d %5" PRIu64 "\n", 34 | sec.size(), hdr.entsize, 35 | to_string(hdr.flags).c_str(), to_string(hdr.link).c_str(), 36 | (int)hdr.info, hdr.addralign); 37 | } 38 | 39 | return 0; 40 | } 41 | -------------------------------------------------------------------------------- /test/golden-gcc-6.2.1-s390x/segments: -------------------------------------------------------------------------------- 1 | Type Offset VirtAddr PhysAddr 2 | FileSiz MemSiz Flags Align 3 | phdr 0x0000000000000040 0x0000000000000040 0x0000000000000040 4 | 0x00000000000001f8 0x00000000000001f8 x|r 8 5 | interp 0x0000000000000238 0x0000000000000238 0x0000000000000238 6 | 0x000000000000000f 0x000000000000000f r 1 7 | load 0x0000000000000000 0x0000000000000000 0x0000000000000000 8 | 0x00000000000009e4 0x00000000000009e4 x|r 1000 9 | load 0x0000000000000e08 0x0000000000001e08 0x0000000000001e08 10 | 0x0000000000000260 0x0000000000000268 w|r 1000 11 | dynamic 0x0000000000000e20 0x0000000000001e20 0x0000000000001e20 12 | 0x00000000000001e0 0x00000000000001e0 w|r 8 13 | note 0x0000000000000248 0x0000000000000248 0x0000000000000248 14 | 0x0000000000000044 0x0000000000000044 r 4 15 | (pt)0x6474e550 0x00000000000008e0 0x00000000000008e0 0x00000000000008e0 16 | 0x000000000000002c 0x000000000000002c r 4 17 | (pt)0x6474e551 0x0000000000000000 0x0000000000000000 0x0000000000000000 18 | 0x0000000000000000 0x0000000000000000 w|r 10 19 | (pt)0x6474e552 0x0000000000000e08 0x0000000000001e08 0x0000000000001e08 20 | 0x00000000000001f8 0x00000000000001f8 r 1 21 | -------------------------------------------------------------------------------- /examples/dump-syms.cc: -------------------------------------------------------------------------------- 1 | #include "elf++.hh" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | int main(int argc, char **argv) 9 | { 10 | if (argc != 2) { 11 | fprintf(stderr, "usage: %s elf-file\n", argv[0]); 12 | return 2; 13 | } 14 | 15 | int fd = open(argv[1], O_RDONLY); 16 | if (fd < 0) { 17 | fprintf(stderr, "%s: %s\n", argv[1], strerror(errno)); 18 | return 1; 19 | } 20 | 21 | elf::elf f(elf::create_mmap_loader(fd)); 22 | for (auto &sec : f.sections()) { 23 | if (sec.get_hdr().type != elf::sht::symtab && sec.get_hdr().type != elf::sht::dynsym) 24 | continue; 25 | 26 | printf("Symbol table '%s':\n", sec.get_name().c_str()); 27 | printf("%6s: %-16s %-5s %-7s %-7s %-5s %s\n", 28 | "Num", "Value", "Size", "Type", "Binding", "Index", 29 | "Name"); 30 | int i = 0; 31 | for (auto sym : sec.as_symtab()) { 32 | auto &d = sym.get_data(); 33 | printf("%6d: %016" PRIx64 " %5" PRId64 " %-7s %-7s %5s %s\n", 34 | i++, d.value, d.size, 35 | to_string(d.type()).c_str(), 36 | to_string(d.binding()).c_str(), 37 | to_string(d.shnxd).c_str(), 38 | sym.get_name().c_str()); 39 | } 40 | } 41 | 42 | return 0; 43 | } 44 | -------------------------------------------------------------------------------- /dwarf/elf.cc: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013 Austin T. Clements. All rights reserved. 2 | // Use of this source code is governed by an MIT license 3 | // that can be found in the LICENSE file. 4 | 5 | #include "dwarf++.hh" 6 | 7 | #include 8 | 9 | using namespace std; 10 | 11 | DWARFPP_BEGIN_NAMESPACE 12 | 13 | static const struct 14 | { 15 | const char *name; 16 | section_type type; 17 | } sections[] = { 18 | {".debug_abbrev", section_type::abbrev}, 19 | {".debug_aranges", section_type::aranges}, 20 | {".debug_frame", section_type::frame}, 21 | {".debug_info", section_type::info}, 22 | {".debug_line", section_type::line}, 23 | {".debug_loc", section_type::loc}, 24 | {".debug_macinfo", section_type::macinfo}, 25 | {".debug_pubnames", section_type::pubnames}, 26 | {".debug_pubtypes", section_type::pubtypes}, 27 | {".debug_ranges", section_type::ranges}, 28 | {".debug_str", section_type::str}, 29 | {".debug_types", section_type::types}, 30 | }; 31 | 32 | bool 33 | elf::section_name_to_type(const char *name, section_type *out) 34 | { 35 | for (auto &sec : sections) { 36 | if (strcmp(sec.name, name) == 0) { 37 | *out = sec.type; 38 | return true; 39 | } 40 | } 41 | return false; 42 | } 43 | 44 | const char * 45 | elf::section_type_to_name(section_type type) 46 | { 47 | for (auto &sec : sections) { 48 | if (sec.type == type) 49 | return sec.name; 50 | } 51 | return nullptr; 52 | } 53 | 54 | DWARFPP_END_NAMESPACE 55 | -------------------------------------------------------------------------------- /elf/mmap_loader.cc: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013 Austin T. Clements. All rights reserved. 2 | // Use of this source code is governed by an MIT license 3 | // that can be found in the LICENSE file. 4 | 5 | #include "elf++.hh" 6 | 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | using namespace std; 16 | 17 | ELFPP_BEGIN_NAMESPACE 18 | 19 | class mmap_loader : public loader 20 | { 21 | void *base; 22 | size_t lim; 23 | 24 | public: 25 | mmap_loader(int fd) 26 | { 27 | off_t end = lseek(fd, 0, SEEK_END); 28 | if (end == (off_t)-1) 29 | throw system_error(errno, system_category(), 30 | "finding file length"); 31 | lim = end; 32 | 33 | base = mmap(nullptr, lim, PROT_READ, MAP_SHARED, fd, 0); 34 | if (base == MAP_FAILED) 35 | throw system_error(errno, system_category(), 36 | "mmap'ing file"); 37 | close(fd); 38 | } 39 | 40 | ~mmap_loader() 41 | { 42 | munmap(base, lim); 43 | } 44 | 45 | const void *load(off_t offset, size_t size) 46 | { 47 | if (offset + size > lim) 48 | throw range_error("offset exceeds file size"); 49 | return (const char*)base + offset; 50 | } 51 | }; 52 | 53 | std::shared_ptr 54 | create_mmap_loader(int fd) 55 | { 56 | return make_shared(fd); 57 | } 58 | 59 | ELFPP_END_NAMESPACE 60 | -------------------------------------------------------------------------------- /test/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | die() { 4 | echo "$@" 5 | exit 1 6 | } 7 | 8 | (cd ../examples && make --quiet) || die "failed to build examples" 9 | 10 | dumps="sections segments lines syms tree" 11 | binaries=example 12 | compilers="gcc-4.9.2 gcc-6.2.1-s390x" 13 | 14 | if [[ $1 == --make-golden ]]; then 15 | MODE=make-golden 16 | fi 17 | 18 | output=$(mktemp --tmpdir libelfin.XXXXXXXXXX) 19 | trap "rm -f $output $output.out" EXIT 20 | 21 | FAILED=0 22 | for dump in $dumps; do 23 | for binary in $binaries; do 24 | for compiler in $compilers; do 25 | if [[ $MODE == make-golden ]]; then 26 | ../examples/dump-$dump golden-$compiler/$binary > golden-$compiler/$dump || \ 27 | die "failed to create golden output" 28 | continue 29 | fi 30 | 31 | PASS=1 32 | 33 | # Save stdout and stderr and redirect output to temporary file. 34 | exec 3>&1 4>&2 1>$output 2>&1 35 | 36 | # Run the test. 37 | ../examples/dump-$dump golden-$compiler/$binary >& $output.out 38 | STATUS=$? 39 | if [[ $STATUS != 0 ]]; then 40 | PASS=0 41 | echo "failed: exit status $STATUS" 42 | fi 43 | if ! diff -u golden-$compiler/$dump $output.out; then 44 | PASS=0 45 | fi 46 | 47 | # Restore FDs. 48 | exec 1>&3 2>&4 3>&- 4>&- 49 | 50 | # Report results. 51 | if [[ $PASS == 0 ]]; then 52 | FAILED=$((FAILED + 1)) 53 | echo -n "FAIL " 54 | else 55 | echo -n "PASS " 56 | fi 57 | echo dump-$dump golden-$compiler/$binary 58 | 59 | if [[ $PASS == 0 ]]; then 60 | sed 's/^/\t/' $output 61 | fi 62 | done 63 | done 64 | done 65 | 66 | if [[ $FAILED != 0 ]]; then 67 | echo "$FAILED test(s) failed" 68 | exit 1 69 | fi 70 | -------------------------------------------------------------------------------- /test/golden-gcc-6.2.1-s390x/tree: -------------------------------------------------------------------------------- 1 | --- <0> 2 | DW_TAG_compile_unit 3 | DW_AT_producer GNU C11 6.2.1 20161124 -m64 -mzarch -march=z900 -g 4 | DW_AT_language 0xc 5 | DW_AT_name example.c 6 | DW_AT_comp_dir x 7 | DW_AT_low_pc 0x768 8 | DW_AT_high_pc 0xb8 9 | DW_AT_stmt_list 10 | <2b> DW_TAG_subprogram 11 | DW_AT_external true 12 | DW_AT_name main 13 | DW_AT_decl_file 0x1 14 | DW_AT_decl_line 0x8 15 | DW_AT_prototyped true 16 | DW_AT_type <0x6b> 17 | DW_AT_low_pc 0x7e0 18 | DW_AT_high_pc 0x40 19 | DW_AT_frame_base 20 | (DW_AT)0x2116 true 21 | DW_AT_sibling <0x6b> 22 | <4c> DW_TAG_formal_parameter 23 | DW_AT_name argc 24 | DW_AT_decl_file 0x1 25 | DW_AT_decl_line 0x8 26 | DW_AT_type <0x6b> 27 | DW_AT_location 28 | <5b> DW_TAG_formal_parameter 29 | DW_AT_name argv 30 | DW_AT_decl_file 0x1 31 | DW_AT_decl_line 0x8 32 | DW_AT_type <0x72> 33 | DW_AT_location 34 | <6b> DW_TAG_base_type 35 | DW_AT_byte_size 0x4 36 | DW_AT_encoding 0x5 37 | DW_AT_name int 38 | <72> DW_TAG_pointer_type 39 | DW_AT_byte_size 0x8 40 | DW_AT_type <0x78> 41 | <78> DW_TAG_pointer_type 42 | DW_AT_byte_size 0x8 43 | DW_AT_type <0x7e> 44 | <7e> DW_TAG_base_type 45 | DW_AT_byte_size 0x1 46 | DW_AT_encoding 0x8 47 | DW_AT_name char 48 | <85> DW_TAG_subprogram 49 | DW_AT_external true 50 | DW_AT_name fib 51 | DW_AT_decl_file 0x1 52 | DW_AT_decl_line 0x1 53 | DW_AT_prototyped true 54 | DW_AT_type <0x6b> 55 | DW_AT_low_pc 0x768 56 | DW_AT_high_pc 0x78 57 | DW_AT_frame_base 58 | (DW_AT)0x2116 true 59 | DW_TAG_formal_parameter 60 | DW_AT_name x 61 | DW_AT_decl_file 0x1 62 | DW_AT_decl_line 0x1 63 | DW_AT_type <0x6b> 64 | DW_AT_location 65 | -------------------------------------------------------------------------------- /test/golden-gcc-4.9.2/tree: -------------------------------------------------------------------------------- 1 | --- <0> 2 | DW_TAG_compile_unit 3 | DW_AT_producer GNU C 4.9.2 -mtune=generic -march=x86-64 -g -fdebug-prefix-map=/home/amthrax/r/libelfin/test=x 4 | DW_AT_language 0x1 5 | DW_AT_name example.c 6 | DW_AT_comp_dir x 7 | DW_AT_low_pc 0x4004b6 8 | DW_AT_high_pc 0x57 9 | DW_AT_stmt_list 10 | <2b> DW_TAG_subprogram 11 | DW_AT_external true 12 | DW_AT_name fib 13 | DW_AT_decl_file 0x1 14 | DW_AT_decl_line 0x1 15 | DW_AT_prototyped true 16 | DW_AT_type <0x59> 17 | DW_AT_low_pc 0x4004b6 18 | DW_AT_high_pc 0x3c 19 | DW_AT_frame_base 20 | (DW_AT)0x2116 true 21 | DW_AT_sibling <0x59> 22 | <4c> DW_TAG_formal_parameter 23 | DW_AT_name x 24 | DW_AT_decl_file 0x1 25 | DW_AT_decl_line 0x1 26 | DW_AT_type <0x59> 27 | DW_AT_location 28 | <59> DW_TAG_base_type 29 | DW_AT_byte_size 0x4 30 | DW_AT_encoding 0x5 31 | DW_AT_name int 32 | <60> DW_TAG_subprogram 33 | DW_AT_external true 34 | DW_AT_name main 35 | DW_AT_decl_file 0x1 36 | DW_AT_decl_line 0x8 37 | DW_AT_prototyped true 38 | DW_AT_type <0x59> 39 | DW_AT_low_pc 0x4004f2 40 | DW_AT_high_pc 0x1b 41 | DW_AT_frame_base 42 | (DW_AT)0x2116 true 43 | DW_AT_sibling <0x9e> 44 | <81> DW_TAG_formal_parameter 45 | DW_AT_name argc 46 | DW_AT_decl_file 0x1 47 | DW_AT_decl_line 0x8 48 | DW_AT_type <0x59> 49 | DW_AT_location 50 | <8f> DW_TAG_formal_parameter 51 | DW_AT_name argv 52 | DW_AT_decl_file 0x1 53 | DW_AT_decl_line 0x8 54 | DW_AT_type <0x9e> 55 | DW_AT_location 56 | <9e> DW_TAG_pointer_type 57 | DW_AT_byte_size 0x8 58 | DW_AT_type <0xa4> 59 | DW_TAG_pointer_type 60 | DW_AT_byte_size 0x8 61 | DW_AT_type <0xaa> 62 | DW_TAG_base_type 63 | DW_AT_byte_size 0x1 64 | DW_AT_encoding 0x6 65 | DW_AT_name char 66 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [Libelfin](https://github.com/aclements/libelfin/) is a from-scratch 2 | C++11 library for reading ELF binaries and DWARFv4 debug information. 3 | 4 | Quick start 5 | ----------- 6 | 7 | `make`, and optionally `make install`. You'll need GCC 4.7 or later. 8 | 9 | Features 10 | -------- 11 | 12 | * Native C++11 code and interface, designed from scratch to interact 13 | well with C++11 features, from range-based for loops to move 14 | semantics to enum classes. 15 | 16 | * Libelfin fully implements parsing for Debugging Information Entries 17 | (DIEs), the core data structure used by the DWARF format, as well as 18 | most DWARFv4 tables. 19 | 20 | * Supports all DWARFv4 DIE value types except location lists and 21 | macros. 22 | 23 | * Nearly complete evaluator for DWARFv4 expressions and location 24 | descriptions. 25 | 26 | * Complete interpreter for DWARFv4 line tables. 27 | 28 | * Iterators for easily and naturally traversing compilation units, 29 | type units, DIE trees, and DIE attribute lists. 30 | 31 | * Every enum value can be pretty-printed. 32 | 33 | * Large collection of type-safe DIE attribute fetchers. 34 | 35 | Non-features 36 | ------------ 37 | 38 | Libelfin implements a *syntactic* layer for DWARF and ELF, but not a 39 | *semantic* layer. Interpreting the information stored in DWARF DIE 40 | trees still requires a great deal of understanding of DWARF, but 41 | libelfin will make sense of the bytes for you. 42 | 43 | Using libelfin 44 | -------------- 45 | 46 | To build against `libdwarf++`, use, for example 47 | 48 | g++ -std=c++11 a.cc $(pkg-config --cflags --libs libdwarf++) 49 | 50 | To use a local build of libelfin, set `PKG_CONFIG_PATH`. For example, 51 | 52 | export PKG_CONFIG_PATH=$PWD/elf:$PWD/dwarf 53 | 54 | There are various example programs in `examples/`. 55 | 56 | Status 57 | ------ 58 | 59 | Libelfin is a good start. It's not production-ready and there are 60 | many parts of the DWARF specification it does not yet implement, but 61 | it's complete enough to be useful for many things and is a good deal 62 | more pleasant to use than every other debug info library I've tried. 63 | -------------------------------------------------------------------------------- /elf/Makefile: -------------------------------------------------------------------------------- 1 | # Changed when ABI backwards compatibility is broken. 2 | # Typically uses the major version. 3 | SONAME = 0 4 | 5 | CXXFLAGS+=-g -O2 -Werror 6 | override CXXFLAGS+=-std=c++0x -Wall -fPIC 7 | 8 | all: libelf++.a libelf++.so libelf++.so.$(SONAME) libelf++.pc 9 | 10 | SRCS := elf.cc mmap_loader.cc to_string.cc 11 | HDRS := elf++.hh data.hh common.hh to_hex.hh 12 | CLEAN := 13 | 14 | libelf++.a: $(SRCS:.cc=.o) 15 | ar rcs $@ $^ 16 | CLEAN += libelf++.a $(SRCS:.cc=.o) 17 | 18 | $(SRCS:.cc=.o): $(HDRS) 19 | 20 | to_string.cc: enum-print.py data.hh Makefile 21 | @echo "// Automatically generated by make at $$(date)" > to_string.cc 22 | @echo "// DO NOT EDIT" >> to_string.cc 23 | @echo >> to_string.cc 24 | @echo '#include "data.hh"' >> to_string.cc 25 | @echo '#include "to_hex.hh"' >> to_string.cc 26 | @echo >> to_string.cc 27 | @echo 'ELFPP_BEGIN_NAMESPACE' >> to_string.cc 28 | @echo >> to_string.cc 29 | python3 enum-print.py -u --hex --no-type --mask shf --mask pf \ 30 | -x loos -x hios -x loproc -x hiproc < data.hh >> to_string.cc 31 | @echo 'ELFPP_END_NAMESPACE' >> to_string.cc 32 | CLEAN += to_string.cc 33 | 34 | libelf++.so.$(SONAME): $(SRCS:.cc=.o) 35 | $(CXX) $(CXXFLAGS) $(LDFLAGS) -shared -Wl,-soname,$@ -o $@ $^ 36 | CLEAN += libelf++.so.* 37 | 38 | libelf++.so: 39 | ln -s $@.$(SONAME) $@ 40 | CLEAN += libelf++.so 41 | 42 | # Create pkg-config for local library and headers. This will be 43 | # transformed in to the correct global pkg-config by install. 44 | libelf++.pc: always 45 | @(VER=$$(git describe --match 'v*' | sed -e s/^v//); \ 46 | echo "libdir=$$PWD"; \ 47 | echo "includedir=$$PWD"; \ 48 | echo ""; \ 49 | echo "Name: libelf++"; \ 50 | echo "Description: C++11 ELF library"; \ 51 | echo "Version: $$VER"; \ 52 | echo "Libs: -L\$${libdir} -lelf++"; \ 53 | echo "Cflags: -I\$${includedir}") > $@ 54 | CLEAN += libelf++.pc 55 | 56 | .PHONY: always 57 | 58 | PREFIX?=/usr/local 59 | 60 | install: libelf++.a libelf++.so libelf++.so.$(SONAME) libelf++.pc 61 | install -d $(PREFIX)/lib/pkgconfig 62 | install -t $(PREFIX)/lib libelf++.a 63 | install -t $(PREFIX)/lib libelf++.so.$(SONAME) 64 | install -t $(PREFIX)/lib libelf++.so 65 | install -d $(PREFIX)/include/libelfin/elf 66 | install -t $(PREFIX)/include/libelfin/elf common.hh data.hh elf++.hh 67 | sed 's,^libdir=.*,libdir=$(PREFIX)/lib,;s,^includedir=.*,includedir=$(PREFIX)/include,' libelf++.pc \ 68 | > $(PREFIX)/lib/pkgconfig/libelf++.pc 69 | 70 | clean: 71 | rm -f $(CLEAN) 72 | 73 | .DELETE_ON_ERROR: 74 | -------------------------------------------------------------------------------- /dwarf/Makefile: -------------------------------------------------------------------------------- 1 | # Changed when ABI backwards compatibility is broken. 2 | # Typically uses the major version. 3 | SONAME = 0 4 | 5 | CXXFLAGS+=-g -O2 -Werror 6 | override CXXFLAGS+=-std=c++0x -Wall -fPIC 7 | 8 | all: libdwarf++.a libdwarf++.so.$(SONAME) libdwarf++.so libdwarf++.pc 9 | 10 | SRCS := dwarf.cc cursor.cc die.cc value.cc abbrev.cc \ 11 | expr.cc rangelist.cc line.cc attrs.cc \ 12 | die_str_map.cc elf.cc to_string.cc 13 | HDRS := dwarf++.hh data.hh internal.hh small_vector.hh ../elf/to_hex.hh 14 | CLEAN := 15 | 16 | libdwarf++.a: $(SRCS:.cc=.o) 17 | ar rcs $@ $^ 18 | CLEAN += libdwarf++.a $(SRCS:.cc=.o) 19 | 20 | $(SRCS:.cc=.o): $(HDRS) 21 | 22 | to_string.cc: ../elf/enum-print.py dwarf++.hh data.hh Makefile 23 | @echo "// Automatically generated by make at $$(date)" > to_string.cc 24 | @echo "// DO NOT EDIT" >> to_string.cc 25 | @echo >> to_string.cc 26 | @echo '#include "internal.hh"' >> to_string.cc 27 | @echo >> to_string.cc 28 | @echo 'DWARFPP_BEGIN_NAMESPACE' >> to_string.cc 29 | @echo >> to_string.cc 30 | python3 ../elf/enum-print.py < dwarf++.hh >> to_string.cc 31 | python3 ../elf/enum-print.py -s _ -u --hex -x hi_user -x lo_user < data.hh >> to_string.cc 32 | @echo 'DWARFPP_END_NAMESPACE' >> to_string.cc 33 | CLEAN += to_string.cc 34 | 35 | libdwarf++.so.$(SONAME): $(SRCS:.cc=.o) 36 | $(CXX) $(CXXFLAGS) $(LDFLAGS) -shared -Wl,-soname,$@ -o $@ $^ 37 | CLEAN += libdwarf++.so.* 38 | 39 | libdwarf++.so: 40 | ln -s $@.$(SONAME) $@ 41 | CLEAN += libdwarf++.so 42 | 43 | # Create pkg-config for local library and headers. This will be 44 | # transformed in to the correct global pkg-config by install. 45 | libdwarf++.pc: always 46 | @(VER=$$(git describe --match 'v*' | sed -e s/^v//); \ 47 | echo "libdir=$$PWD"; \ 48 | echo "includedir=$$PWD"; \ 49 | echo ""; \ 50 | echo "Name: libdwarf++"; \ 51 | echo "Description: C++11 DWARF library"; \ 52 | echo "Version: $$VER"; \ 53 | echo "Requires: libelf++ = $$VER"; \ 54 | echo "Libs: -L\$${libdir} -ldwarf++"; \ 55 | echo "Cflags: -I\$${includedir}") > $@ 56 | CLEAN += libdwarf++.pc 57 | 58 | .PHONY: always 59 | 60 | PREFIX?=/usr/local 61 | 62 | install: libdwarf++.a libdwarf++.so.$(SONAME) libdwarf++.so libdwarf++.pc 63 | install -d $(PREFIX)/lib/pkgconfig 64 | install -t $(PREFIX)/lib libdwarf++.a 65 | install -t $(PREFIX)/lib libdwarf++.so.$(SONAME) 66 | install -t $(PREFIX)/lib libdwarf++.so 67 | install -d $(PREFIX)/include/libelfin/dwarf 68 | install -t $(PREFIX)/include/libelfin/dwarf data.hh dwarf++.hh small_vector.hh 69 | sed 's,^libdir=.*,libdir=$(PREFIX)/lib,;s,^includedir=.*,includedir=$(PREFIX)/include,' libdwarf++.pc \ 70 | > $(PREFIX)/lib/pkgconfig/libdwarf++.pc 71 | 72 | clean: 73 | rm -f $(CLEAN) 74 | 75 | .DELETE_ON_ERROR: 76 | -------------------------------------------------------------------------------- /elf/common.hh: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013 Austin T. Clements. All rights reserved. 2 | // Use of this source code is governed by an MIT license 3 | // that can be found in the LICENSE file. 4 | 5 | #ifndef _ELFPP_COMMON_HH_ 6 | #define _ELFPP_COMMON_HH_ 7 | 8 | #define ELFPP_BEGIN_NAMESPACE namespace elf { 9 | #define ELFPP_END_NAMESPACE } 10 | #define ELFPP_BEGIN_INTERNAL namespace internal { 11 | #define ELFPP_END_INTERNAL } 12 | 13 | #include 14 | 15 | ELFPP_BEGIN_NAMESPACE 16 | 17 | /** 18 | * A byte ordering. 19 | */ 20 | enum class byte_order 21 | { 22 | native, 23 | lsb, 24 | msb 25 | }; 26 | 27 | /** 28 | * Return either byte_order::lsb or byte_order::msb. If the argument 29 | * is byte_order::native, it will be resolved to whatever the native 30 | * byte order is. 31 | */ 32 | static inline byte_order 33 | resolve_order(byte_order o) 34 | { 35 | static const union 36 | { 37 | int i; 38 | char c[sizeof(int)]; 39 | } test = {1}; 40 | 41 | if (o == byte_order::native) 42 | return test.c[0] == 1 ? byte_order::lsb : byte_order::msb; 43 | return o; 44 | } 45 | 46 | /** 47 | * Return v converted from one byte order to another. 48 | */ 49 | template 50 | T 51 | swizzle(T v, byte_order from, byte_order to) 52 | { 53 | static_assert(sizeof(T) == 1 || 54 | sizeof(T) == 2 || 55 | sizeof(T) == 4 || 56 | sizeof(T) == 8, 57 | "cannot swizzle type"); 58 | 59 | from = resolve_order(from); 60 | to = resolve_order(to); 61 | 62 | if (from == to) 63 | return v; 64 | 65 | switch (sizeof(T)) { 66 | case 1: 67 | return v; 68 | case 2: { 69 | std::uint16_t x = (std::uint16_t)v; 70 | return (T)(((x&0xFF) << 8) | (x >> 8)); 71 | } 72 | case 4: 73 | return (T)__builtin_bswap32((std::uint32_t)v); 74 | case 8: 75 | return (T)__builtin_bswap64((std::uint64_t)v); 76 | } 77 | } 78 | 79 | ELFPP_BEGIN_INTERNAL 80 | 81 | /** 82 | * OrderPick selects between Native, LSB, and MSB based on ord. 83 | */ 84 | template 85 | struct OrderPick; 86 | 87 | template 88 | struct OrderPick 89 | { 90 | typedef Native T; 91 | }; 92 | 93 | template 94 | struct OrderPick 95 | { 96 | typedef LSB T; 97 | }; 98 | 99 | template 100 | struct OrderPick 101 | { 102 | typedef MSB T; 103 | }; 104 | 105 | ELFPP_END_INTERNAL 106 | 107 | ELFPP_END_NAMESPACE 108 | 109 | #endif 110 | -------------------------------------------------------------------------------- /dwarf/rangelist.cc: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013 Austin T. Clements. All rights reserved. 2 | // Use of this source code is governed by an MIT license 3 | // that can be found in the LICENSE file. 4 | 5 | #include "internal.hh" 6 | 7 | using namespace std; 8 | 9 | DWARFPP_BEGIN_NAMESPACE 10 | 11 | rangelist::rangelist(const std::shared_ptr
&sec, section_offset off, 12 | unsigned cu_addr_size, taddr cu_low_pc) 13 | : sec(sec->slice(off, ~0, format::unknown, cu_addr_size)), 14 | base_addr(cu_low_pc) 15 | { 16 | } 17 | 18 | rangelist::rangelist(const initializer_list > &ranges) 19 | { 20 | synthetic.reserve(ranges.size() * 2 + 2); 21 | for (auto &range : ranges) { 22 | synthetic.push_back(range.first); 23 | synthetic.push_back(range.second); 24 | } 25 | synthetic.push_back(0); 26 | synthetic.push_back(0); 27 | 28 | sec = make_shared
( 29 | section_type::ranges, (const char*)synthetic.data(), 30 | synthetic.size() * sizeof(taddr), 31 | native_order(), format::unknown, sizeof(taddr)); 32 | 33 | base_addr = 0; 34 | } 35 | 36 | rangelist::iterator 37 | rangelist::begin() const 38 | { 39 | if (sec) 40 | return iterator(sec, base_addr); 41 | return end(); 42 | } 43 | 44 | rangelist::iterator 45 | rangelist::end() const 46 | { 47 | return iterator(); 48 | } 49 | 50 | bool 51 | rangelist::contains(taddr addr) const 52 | { 53 | for (auto ent : *this) 54 | if (ent.contains(addr)) 55 | return true; 56 | return false; 57 | } 58 | 59 | rangelist::iterator::iterator(const std::shared_ptr
&sec, taddr base_addr) 60 | : sec(sec), base_addr(base_addr), pos(0) 61 | { 62 | // Read in the first entry 63 | ++(*this); 64 | } 65 | 66 | rangelist::iterator & 67 | rangelist::iterator::operator++() 68 | { 69 | // DWARF4 section 2.17.3 70 | taddr largest_offset = ~(taddr)0; 71 | if (sec->addr_size < sizeof(taddr)) 72 | largest_offset += 1 << (8 * sec->addr_size); 73 | 74 | // Read in entries until we reach a regular entry of an 75 | // end-of-list. Note that pos points to the beginning of the 76 | // entry *following* the current entry, so that's where we 77 | // start. 78 | cursor cur(sec, pos); 79 | while (true) { 80 | entry.low = cur.address(); 81 | entry.high = cur.address(); 82 | 83 | if (entry.low == 0 && entry.high == 0) { 84 | // End of list 85 | sec.reset(); 86 | pos = 0; 87 | break; 88 | } else if (entry.low == largest_offset) { 89 | // Base address change 90 | base_addr = entry.high; 91 | } else { 92 | // Regular entry. Adjust by base address. 93 | entry.low += base_addr; 94 | entry.high += base_addr; 95 | pos = cur.get_section_offset(); 96 | break; 97 | } 98 | } 99 | 100 | return *this; 101 | } 102 | 103 | DWARFPP_END_NAMESPACE 104 | -------------------------------------------------------------------------------- /examples/find-pc.cc: -------------------------------------------------------------------------------- 1 | #include "elf++.hh" 2 | #include "dwarf++.hh" 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | using namespace std; 9 | 10 | void 11 | usage(const char *cmd) 12 | { 13 | fprintf(stderr, "usage: %s elf-file pc\n", cmd); 14 | exit(2); 15 | } 16 | 17 | bool 18 | find_pc(const dwarf::die &d, dwarf::taddr pc, vector *stack) 19 | { 20 | using namespace dwarf; 21 | 22 | // Scan children first to find most specific DIE 23 | bool found = false; 24 | for (auto &child : d) { 25 | if ((found = find_pc(child, pc, stack))) 26 | break; 27 | } 28 | switch (d.tag) { 29 | case DW_TAG::subprogram: 30 | case DW_TAG::inlined_subroutine: 31 | try { 32 | if (found || die_pc_range(d).contains(pc)) { 33 | found = true; 34 | stack->push_back(d); 35 | } 36 | } catch (out_of_range &e) { 37 | } catch (value_type_mismatch &e) { 38 | } 39 | break; 40 | default: 41 | break; 42 | } 43 | return found; 44 | } 45 | 46 | void 47 | dump_die(const dwarf::die &node) 48 | { 49 | printf("<%" PRIx64 "> %s\n", 50 | node.get_section_offset(), 51 | to_string(node.tag).c_str()); 52 | for (auto &attr : node.attributes()) 53 | printf(" %s %s\n", 54 | to_string(attr.first).c_str(), 55 | to_string(attr.second).c_str()); 56 | } 57 | 58 | int 59 | main(int argc, char **argv) 60 | { 61 | if (argc != 3) 62 | usage(argv[0]); 63 | 64 | dwarf::taddr pc; 65 | try { 66 | pc = stoll(argv[2], nullptr, 0); 67 | } catch (invalid_argument &e) { 68 | usage(argv[0]); 69 | } catch (out_of_range &e) { 70 | usage(argv[0]); 71 | } 72 | 73 | int fd = open(argv[1], O_RDONLY); 74 | if (fd < 0) { 75 | fprintf(stderr, "%s: %s\n", argv[1], strerror(errno)); 76 | return 1; 77 | } 78 | 79 | elf::elf ef(elf::create_mmap_loader(fd)); 80 | dwarf::dwarf dw(dwarf::elf::create_loader(ef)); 81 | 82 | // Find the CU containing pc 83 | // XXX Use .debug_aranges 84 | for (auto &cu : dw.compilation_units()) { 85 | if (die_pc_range(cu.root()).contains(pc)) { 86 | // Map PC to a line 87 | auto < = cu.get_line_table(); 88 | auto it = lt.find_address(pc); 89 | if (it == lt.end()) 90 | printf("UNKNOWN\n"); 91 | else 92 | printf("%s\n", 93 | it->get_description().c_str()); 94 | 95 | // Map PC to an object 96 | // XXX Index/helper/something for looking up PCs 97 | // XXX DW_AT_specification and DW_AT_abstract_origin 98 | vector stack; 99 | if (find_pc(cu.root(), pc, &stack)) { 100 | bool first = true; 101 | for (auto &d : stack) { 102 | if (!first) 103 | printf("\nInlined in:\n"); 104 | first = false; 105 | dump_die(d); 106 | } 107 | } 108 | break; 109 | } 110 | } 111 | 112 | return 0; 113 | } 114 | -------------------------------------------------------------------------------- /dwarf/die_str_map.cc: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013 Austin T. Clements. All rights reserved. 2 | // Use of this source code is governed by an MIT license 3 | // that can be found in the LICENSE file. 4 | 5 | #include "internal.hh" 6 | 7 | #include 8 | #include 9 | 10 | using namespace std; 11 | 12 | // XXX Make this more readily available? 13 | namespace std { 14 | template<> 15 | struct hash 16 | { 17 | typedef size_t result_type; 18 | typedef dwarf::DW_TAG argument_type; 19 | result_type operator()(argument_type a) const 20 | { 21 | return (result_type)a; 22 | } 23 | }; 24 | } 25 | 26 | DWARFPP_BEGIN_NAMESPACE 27 | 28 | struct string_hash 29 | { 30 | typedef size_t result_type; 31 | typedef const char *argument_type; 32 | result_type operator()(const char *s) const 33 | { 34 | result_type h = 0; 35 | for (; *s; ++s) 36 | h += 33 * h + *s; 37 | return h; 38 | } 39 | }; 40 | 41 | struct string_eq 42 | { 43 | typedef bool result_type; 44 | typedef const char *first_argument_type; 45 | typedef const char *second_argument_type; 46 | bool operator()(const char *x, const char *y) const 47 | { 48 | return strcmp(x, y) == 0; 49 | } 50 | }; 51 | 52 | struct die_str_map::impl 53 | { 54 | impl(const die &parent, DW_AT attr, 55 | const initializer_list &accept) 56 | : attr(attr), accept(accept.begin(), accept.end()), 57 | pos(parent.begin()), end(parent.end()) { } 58 | 59 | unordered_map str_map; 60 | DW_AT attr; 61 | unordered_set accept; 62 | die::iterator pos, end; 63 | die invalid; 64 | }; 65 | 66 | die_str_map::die_str_map(const die &parent, DW_AT attr, 67 | const initializer_list &accept) 68 | : m(make_shared(parent, attr, accept)) 69 | { 70 | } 71 | 72 | die_str_map 73 | die_str_map::from_type_names(const die &parent) 74 | { 75 | return die_str_map 76 | (parent, DW_AT::name, 77 | // All DWARF type tags (this is everything that ends 78 | // with _type except thrown_type). 79 | {DW_TAG::array_type, DW_TAG::class_type, 80 | DW_TAG::enumeration_type, DW_TAG::pointer_type, 81 | DW_TAG::reference_type, DW_TAG::string_type, 82 | DW_TAG::structure_type, DW_TAG::subroutine_type, 83 | DW_TAG::union_type, DW_TAG::ptr_to_member_type, 84 | DW_TAG::set_type, DW_TAG::subrange_type, 85 | DW_TAG::base_type, DW_TAG::const_type, 86 | DW_TAG::file_type, DW_TAG::packed_type, 87 | DW_TAG::volatile_type, DW_TAG::restrict_type, 88 | DW_TAG::interface_type, DW_TAG::unspecified_type, 89 | DW_TAG::shared_type, DW_TAG::rvalue_reference_type}); 90 | } 91 | 92 | const die & 93 | die_str_map::operator[](const char *val) const 94 | { 95 | // Do we have this value? 96 | auto it = m->str_map.find(val); 97 | if (it != m->str_map.end()) 98 | return it->second; 99 | // Read more until we find the value or the end 100 | while (m->pos != m->end) { 101 | const die &d = *m->pos; 102 | ++m->pos; 103 | 104 | if (!m->accept.count(d.tag) || !d.has(m->attr)) 105 | continue; 106 | value dval(d[m->attr]); 107 | if (dval.get_type() != value::type::string) 108 | continue; 109 | const char *dstr = dval.as_cstr(); 110 | m->str_map[dstr] = d; 111 | if (strcmp(val, dstr) == 0) 112 | return m->str_map[dstr]; 113 | } 114 | // Not found 115 | return m->invalid; 116 | } 117 | 118 | DWARFPP_END_NAMESPACE 119 | -------------------------------------------------------------------------------- /test/golden-gcc-4.9.2/syms: -------------------------------------------------------------------------------- 1 | Symbol table '.dynsym': 2 | Num: Value Size Type Binding Index Name 3 | 0: 0000000000000000 0 notype local undef 4 | 1: 0000000000000000 0 func global undef __libc_start_main 5 | 2: 0000000000000000 0 notype weak undef __gmon_start__ 6 | Symbol table '.symtab': 7 | Num: Value Size Type Binding Index Name 8 | 0: 0000000000000000 0 notype local undef 9 | 1: 0000000000400200 0 section local 1 10 | 2: 000000000040021c 0 section local 2 11 | 3: 000000000040023c 0 section local 3 12 | 4: 0000000000400260 0 section local 4 13 | 5: 0000000000400280 0 section local 5 14 | 6: 00000000004002c8 0 section local 6 15 | 7: 0000000000400300 0 section local 7 16 | 8: 0000000000400308 0 section local 8 17 | 9: 0000000000400328 0 section local 9 18 | 10: 0000000000400340 0 section local 10 19 | 11: 0000000000400370 0 section local 11 20 | 12: 0000000000400390 0 section local 12 21 | 13: 00000000004003c0 0 section local 13 22 | 14: 0000000000400584 0 section local 14 23 | 15: 0000000000400590 0 section local 15 24 | 16: 0000000000400594 0 section local 16 25 | 17: 00000000004005d0 0 section local 17 26 | 18: 00000000006006e8 0 section local 18 27 | 19: 00000000006006f0 0 section local 19 28 | 20: 00000000006006f8 0 section local 20 29 | 21: 0000000000600700 0 section local 21 30 | 22: 00000000006008d0 0 section local 22 31 | 23: 00000000006008d8 0 section local 23 32 | 24: 0000000000600900 0 section local 24 33 | 25: 0000000000600910 0 section local 25 34 | 26: 0000000000000000 0 section local 26 35 | 27: 0000000000000000 0 section local 27 36 | 28: 0000000000000000 0 section local 28 37 | 29: 0000000000000000 0 section local 29 38 | 30: 0000000000000000 0 section local 30 39 | 31: 0000000000000000 0 section local 31 40 | 32: 0000000000000000 0 file local abs crtstuff.c 41 | 33: 00000000006006f8 0 object local 20 __JCR_LIST__ 42 | 34: 00000000004003f0 0 func local 13 deregister_tm_clones 43 | 35: 0000000000400430 0 func local 13 register_tm_clones 44 | 36: 0000000000400470 0 func local 13 __do_global_dtors_aux 45 | 37: 0000000000600910 1 object local 25 completed.6661 46 | 38: 00000000006006f0 0 object local 19 __do_global_dtors_aux_fini_array_entry 47 | 39: 0000000000400490 0 func local 13 frame_dummy 48 | 40: 00000000006006e8 0 object local 18 __frame_dummy_init_array_entry 49 | 41: 0000000000000000 0 file local abs example.c 50 | 42: 0000000000000000 0 file local abs crtstuff.c 51 | 43: 00000000004006e0 0 object local 17 __FRAME_END__ 52 | 44: 00000000006006f8 0 object local 20 __JCR_END__ 53 | 45: 0000000000000000 0 file local abs 54 | 46: 00000000006006f0 0 notype local 18 __init_array_end 55 | 47: 0000000000600700 0 object local 21 _DYNAMIC 56 | 48: 00000000006006e8 0 notype local 18 __init_array_start 57 | 49: 00000000006008d8 0 object local 23 _GLOBAL_OFFSET_TABLE_ 58 | 50: 0000000000400580 2 func global 13 __libc_csu_fini 59 | 51: 0000000000000000 0 notype weak undef _ITM_deregisterTMCloneTable 60 | 52: 0000000000600900 0 notype weak 24 data_start 61 | 53: 0000000000600910 0 notype global 24 _edata 62 | 54: 0000000000400584 0 func global 14 _fini 63 | 55: 0000000000000000 0 func global undef __libc_start_main@@GLIBC_2.2.5 64 | 56: 0000000000600900 0 notype global 24 __data_start 65 | 57: 0000000000000000 0 notype weak undef __gmon_start__ 66 | 58: 0000000000600908 0 object global 24 __dso_handle 67 | 59: 0000000000400590 4 object global 15 _IO_stdin_used 68 | 60: 0000000000400510 101 func global 13 __libc_csu_init 69 | 61: 0000000000600918 0 notype global 25 _end 70 | 62: 00000000004003c0 0 func global 13 _start 71 | 63: 0000000000600910 0 notype global 25 __bss_start 72 | 64: 00000000004004f2 27 func global 13 main 73 | 65: 00000000004004b6 60 func global 13 fib 74 | 66: 0000000000000000 0 notype weak undef _Jv_RegisterClasses 75 | 67: 0000000000600910 0 object global 24 __TMC_END__ 76 | 68: 0000000000000000 0 notype weak undef _ITM_registerTMCloneTable 77 | 69: 0000000000400370 0 func global 11 _init 78 | -------------------------------------------------------------------------------- /test/golden-gcc-6.2.1-s390x/sections: -------------------------------------------------------------------------------- 1 | [Nr] Name Type Address Offset 2 | Size EntSize Flags Link Info Align 3 | [ 0] null 0000000000000000 00000000 4 | 0000000000000000 0000000000000000 (shf)0x0 undef 0 0 5 | [ 1] .interp progbits 0000000000000238 00000238 6 | 000000000000000f 0000000000000000 alloc undef 0 1 7 | [ 2] .note.ABI-tag note 0000000000000248 00000248 8 | 0000000000000020 0000000000000000 alloc undef 0 4 9 | [ 3] .note.gnu.build-id note 0000000000000268 00000268 10 | 0000000000000024 0000000000000000 alloc undef 0 4 11 | [ 4] .gnu.hash (sht)0x6ffffff6 0000000000000290 00000290 12 | 0000000000000024 0000000000000000 alloc 5 0 8 13 | [ 5] .dynsym dynsym 00000000000002b8 000002b8 14 | 00000000000000d8 0000000000000018 alloc 6 2 8 15 | [ 6] .dynstr strtab 0000000000000390 00000390 16 | 000000000000008f 0000000000000000 alloc undef 0 1 17 | [ 7] .gnu.version (sht)0x6fffffff 0000000000000420 00000420 18 | 0000000000000012 0000000000000002 alloc 5 0 2 19 | [ 8] .gnu.version_r (sht)0x6ffffffe 0000000000000438 00000438 20 | 0000000000000020 0000000000000000 alloc 6 1 8 21 | [ 9] .rela.dyn rela 0000000000000458 00000458 22 | 00000000000000d8 0000000000000018 alloc 5 0 8 23 | [10] .rela.plt rela 0000000000000530 00000530 24 | 0000000000000030 0000000000000018 alloc|(shf)0x40 5 22 8 25 | [11] .init progbits 0000000000000560 00000560 26 | 0000000000000040 0000000000000000 alloc|execinstr undef 0 4 27 | [12] .plt progbits 00000000000005a0 000005a0 28 | 0000000000000060 0000000000000020 alloc|execinstr undef 0 4 29 | [13] .text progbits 0000000000000600 00000600 30 | 0000000000000290 0000000000000000 alloc|execinstr undef 0 8 31 | [14] .fini progbits 0000000000000890 00000890 32 | 000000000000002c 0000000000000000 alloc|execinstr undef 0 4 33 | [15] .rodata progbits 00000000000008c0 000008c0 34 | 0000000000000020 0000000000000000 alloc undef 0 8 35 | [16] .eh_frame_hdr progbits 00000000000008e0 000008e0 36 | 000000000000002c 0000000000000000 alloc undef 0 4 37 | [17] .eh_frame progbits 0000000000000910 00000910 38 | 00000000000000d4 0000000000000000 alloc undef 0 8 39 | [18] .init_array (sht)0xe 0000000000001e08 00000e08 40 | 0000000000000008 0000000000000008 write|alloc undef 0 8 41 | [19] .fini_array (sht)0xf 0000000000001e10 00000e10 42 | 0000000000000008 0000000000000008 write|alloc undef 0 8 43 | [20] .jcr progbits 0000000000001e18 00000e18 44 | 0000000000000008 0000000000000000 write|alloc undef 0 8 45 | [21] .dynamic dynamic 0000000000001e20 00000e20 46 | 00000000000001e0 0000000000000010 write|alloc 6 0 8 47 | [22] .got progbits 0000000000002000 00001000 48 | 0000000000000058 0000000000000008 write|alloc undef 0 8 49 | [23] .data progbits 0000000000002058 00001058 50 | 0000000000000010 0000000000000000 write|alloc undef 0 8 51 | [24] .bss nobits 0000000000002068 00001068 52 | 0000000000000008 0000000000000000 write|alloc undef 0 4 53 | [25] .comment progbits 0000000000000000 00001068 54 | 0000000000000025 0000000000000001 (shf)0x30 undef 0 1 55 | [26] .debug_aranges progbits 0000000000000000 0000108d 56 | 0000000000000030 0000000000000000 (shf)0x0 undef 0 1 57 | [27] .debug_info progbits 0000000000000000 000010bd 58 | 00000000000000b1 0000000000000000 (shf)0x0 undef 0 1 59 | [28] .debug_abbrev progbits 0000000000000000 0000116e 60 | 0000000000000087 0000000000000000 (shf)0x0 undef 0 1 61 | [29] .debug_line progbits 0000000000000000 000011f5 62 | 0000000000000048 0000000000000000 (shf)0x0 undef 0 1 63 | [30] .debug_str progbits 0000000000000000 0000123d 64 | 0000000000000051 0000000000000001 (shf)0x30 undef 0 1 65 | [31] .symtab symtab 0000000000000000 00001290 66 | 00000000000006a8 0000000000000018 (shf)0x0 32 50 8 67 | [32] .strtab strtab 0000000000000000 00001938 68 | 0000000000000220 0000000000000000 (shf)0x0 undef 0 1 69 | [33] .shstrtab strtab 0000000000000000 00001b58 70 | 000000000000013f 0000000000000000 (shf)0x0 undef 0 1 71 | -------------------------------------------------------------------------------- /test/golden-gcc-4.9.2/sections: -------------------------------------------------------------------------------- 1 | [Nr] Name Type Address Offset 2 | Size EntSize Flags Link Info Align 3 | [ 0] null 0000000000000000 00000000 4 | 0000000000000000 0000000000000000 (shf)0x0 undef 0 0 5 | [ 1] .interp progbits 0000000000400200 00000200 6 | 000000000000001c 0000000000000000 alloc undef 0 1 7 | [ 2] .note.ABI-tag note 000000000040021c 0000021c 8 | 0000000000000020 0000000000000000 alloc undef 0 4 9 | [ 3] .note.gnu.build-id note 000000000040023c 0000023c 10 | 0000000000000024 0000000000000000 alloc undef 0 4 11 | [ 4] .gnu.hash (sht)0x6ffffff6 0000000000400260 00000260 12 | 000000000000001c 0000000000000000 alloc 5 0 8 13 | [ 5] .dynsym dynsym 0000000000400280 00000280 14 | 0000000000000048 0000000000000018 alloc 6 1 8 15 | [ 6] .dynstr strtab 00000000004002c8 000002c8 16 | 0000000000000038 0000000000000000 alloc undef 0 1 17 | [ 7] .gnu.version (sht)0x6fffffff 0000000000400300 00000300 18 | 0000000000000006 0000000000000002 alloc 5 0 2 19 | [ 8] .gnu.version_r (sht)0x6ffffffe 0000000000400308 00000308 20 | 0000000000000020 0000000000000000 alloc 6 1 8 21 | [ 9] .rela.dyn rela 0000000000400328 00000328 22 | 0000000000000018 0000000000000018 alloc 5 0 8 23 | [10] .rela.plt rela 0000000000400340 00000340 24 | 0000000000000030 0000000000000018 alloc|(shf)0x40 5 12 8 25 | [11] .init progbits 0000000000400370 00000370 26 | 000000000000001a 0000000000000000 alloc|execinstr undef 0 4 27 | [12] .plt progbits 0000000000400390 00000390 28 | 0000000000000030 0000000000000010 alloc|execinstr undef 0 16 29 | [13] .text progbits 00000000004003c0 000003c0 30 | 00000000000001c2 0000000000000000 alloc|execinstr undef 0 16 31 | [14] .fini progbits 0000000000400584 00000584 32 | 0000000000000009 0000000000000000 alloc|execinstr undef 0 4 33 | [15] .rodata progbits 0000000000400590 00000590 34 | 0000000000000004 0000000000000004 alloc|(shf)0x10 undef 0 4 35 | [16] .eh_frame_hdr progbits 0000000000400594 00000594 36 | 000000000000003c 0000000000000000 alloc undef 0 4 37 | [17] .eh_frame progbits 00000000004005d0 000005d0 38 | 0000000000000114 0000000000000000 alloc undef 0 8 39 | [18] .init_array (sht)0xe 00000000006006e8 000006e8 40 | 0000000000000008 0000000000000000 write|alloc undef 0 8 41 | [19] .fini_array (sht)0xf 00000000006006f0 000006f0 42 | 0000000000000008 0000000000000000 write|alloc undef 0 8 43 | [20] .jcr progbits 00000000006006f8 000006f8 44 | 0000000000000008 0000000000000000 write|alloc undef 0 8 45 | [21] .dynamic dynamic 0000000000600700 00000700 46 | 00000000000001d0 0000000000000010 write|alloc 6 0 8 47 | [22] .got progbits 00000000006008d0 000008d0 48 | 0000000000000008 0000000000000008 write|alloc undef 0 8 49 | [23] .got.plt progbits 00000000006008d8 000008d8 50 | 0000000000000028 0000000000000008 write|alloc undef 0 8 51 | [24] .data progbits 0000000000600900 00000900 52 | 0000000000000010 0000000000000000 write|alloc undef 0 8 53 | [25] .bss nobits 0000000000600910 00000910 54 | 0000000000000008 0000000000000000 write|alloc undef 0 1 55 | [26] .comment progbits 0000000000000000 00000910 56 | 0000000000000039 0000000000000001 (shf)0x30 undef 0 1 57 | [27] .debug_aranges progbits 0000000000000000 00000949 58 | 0000000000000030 0000000000000000 (shf)0x0 undef 0 1 59 | [28] .debug_info progbits 0000000000000000 00000979 60 | 00000000000000b2 0000000000000000 (shf)0x0 undef 0 1 61 | [29] .debug_abbrev progbits 0000000000000000 00000a2b 62 | 0000000000000089 0000000000000000 (shf)0x0 undef 0 1 63 | [30] .debug_line progbits 0000000000000000 00000ab4 64 | 0000000000000043 0000000000000000 (shf)0x0 undef 0 1 65 | [31] .debug_str progbits 0000000000000000 00000af7 66 | 000000000000007d 0000000000000001 (shf)0x30 undef 0 1 67 | [32] .shstrtab strtab 0000000000000000 00000b74 68 | 0000000000000148 0000000000000000 (shf)0x0 undef 0 1 69 | [33] .symtab symtab 0000000000000000 00000cc0 70 | 0000000000000690 0000000000000018 (shf)0x0 34 50 8 71 | [34] .strtab strtab 0000000000000000 00001350 72 | 000000000000022b 0000000000000000 (shf)0x0 undef 0 1 73 | -------------------------------------------------------------------------------- /test/golden-gcc-6.2.1-s390x/syms: -------------------------------------------------------------------------------- 1 | Symbol table '.dynsym': 2 | Num: Value Size Type Binding Index Name 3 | 0: 0000000000000000 0 notype local undef 4 | 1: 0000000000000560 0 section local 11 5 | 2: 0000000000000000 0 func weak undef __cxa_finalize 6 | 3: 0000000000000000 0 notype weak undef _ITM_deregisterTMCloneTable 7 | 4: 0000000000000000 0 notype weak undef __gmon_start__ 8 | 5: 0000000000000000 0 func global undef __libc_start_main 9 | 6: 0000000000000000 0 notype weak undef _Jv_RegisterClasses 10 | 7: 0000000000000000 0 notype weak undef _ITM_registerTMCloneTable 11 | 8: 00000000000007e0 64 func global 13 main 12 | Symbol table '.symtab': 13 | Num: Value Size Type Binding Index Name 14 | 0: 0000000000000000 0 notype local undef 15 | 1: 0000000000000238 0 section local 1 16 | 2: 0000000000000248 0 section local 2 17 | 3: 0000000000000268 0 section local 3 18 | 4: 0000000000000290 0 section local 4 19 | 5: 00000000000002b8 0 section local 5 20 | 6: 0000000000000390 0 section local 6 21 | 7: 0000000000000420 0 section local 7 22 | 8: 0000000000000438 0 section local 8 23 | 9: 0000000000000458 0 section local 9 24 | 10: 0000000000000530 0 section local 10 25 | 11: 0000000000000560 0 section local 11 26 | 12: 00000000000005a0 0 section local 12 27 | 13: 0000000000000600 0 section local 13 28 | 14: 0000000000000890 0 section local 14 29 | 15: 00000000000008c0 0 section local 15 30 | 16: 00000000000008e0 0 section local 16 31 | 17: 0000000000000910 0 section local 17 32 | 18: 0000000000001e08 0 section local 18 33 | 19: 0000000000001e10 0 section local 19 34 | 20: 0000000000001e18 0 section local 20 35 | 21: 0000000000001e20 0 section local 21 36 | 22: 0000000000002000 0 section local 22 37 | 23: 0000000000002058 0 section local 23 38 | 24: 0000000000002068 0 section local 24 39 | 25: 0000000000000000 0 section local 25 40 | 26: 0000000000000000 0 section local 26 41 | 27: 0000000000000000 0 section local 27 42 | 28: 0000000000000000 0 section local 28 43 | 29: 0000000000000000 0 section local 29 44 | 30: 0000000000000000 0 section local 30 45 | 31: 0000000000000000 0 file local abs crtstuff.c 46 | 32: 0000000000001e18 0 object local 20 __JCR_LIST__ 47 | 33: 0000000000000648 0 func local 13 deregister_tm_clones 48 | 34: 0000000000000680 0 func local 13 register_tm_clones 49 | 35: 00000000000006c8 0 func local 13 __do_global_dtors_aux 50 | 36: 0000000000002068 1 object local 24 completed.6615 51 | 37: 0000000000001e10 0 object local 19 __do_global_dtors_aux_fini_array_entry 52 | 38: 0000000000000720 0 func local 13 frame_dummy 53 | 39: 0000000000001e08 0 object local 18 __frame_dummy_init_array_entry 54 | 40: 0000000000000000 0 file local abs example.c 55 | 41: 0000000000000000 0 file local abs crtstuff.c 56 | 42: 00000000000009e0 0 object local 17 __FRAME_END__ 57 | 43: 0000000000001e18 0 object local 20 __JCR_END__ 58 | 44: 0000000000000000 0 file local abs 59 | 45: 0000000000001e10 0 notype local 18 __init_array_end 60 | 46: 0000000000001e20 0 object local abs _DYNAMIC 61 | 47: 0000000000001e08 0 notype local 18 __init_array_start 62 | 48: 00000000000008e0 0 notype local 16 __GNU_EH_FRAME_HDR 63 | 49: 0000000000002000 0 object local abs _GLOBAL_OFFSET_TABLE_ 64 | 50: 0000000000000888 2 func global 13 __libc_csu_fini 65 | 51: 0000000000000000 0 func weak undef __cxa_finalize@@GLIBC_2.2 66 | 52: 0000000000000000 0 notype weak undef _ITM_deregisterTMCloneTable 67 | 53: 0000000000002058 0 notype weak 23 data_start 68 | 54: 0000000000002068 0 notype global 23 _edata 69 | 55: 0000000000000890 0 func global 14 _fini 70 | 56: 0000000000002058 0 notype global 23 __data_start 71 | 57: 0000000000000000 0 notype weak undef __gmon_start__ 72 | 58: 0000000000002060 0 object global 23 __dso_handle 73 | 59: 00000000000008c0 4 object global 15 _IO_stdin_used 74 | 60: 0000000000000000 0 func global undef __libc_start_main@@GLIBC_2.2 75 | 61: 0000000000000820 100 func global 13 __libc_csu_init 76 | 62: 0000000000002070 0 notype global 24 _end 77 | 63: 0000000000000600 0 func global 13 _start 78 | 64: 0000000000002068 0 notype global 24 __bss_start 79 | 65: 00000000000007e0 64 func global 13 main 80 | 66: 0000000000000768 120 func global 13 fib 81 | 67: 0000000000000000 0 notype weak undef _Jv_RegisterClasses 82 | 68: 0000000000002068 0 object global 23 __TMC_END__ 83 | 69: 0000000000000000 0 notype weak undef _ITM_registerTMCloneTable 84 | 70: 0000000000000560 0 func global 11 _init 85 | -------------------------------------------------------------------------------- /dwarf/small_vector.hh: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013 Austin T. Clements. All rights reserved. 2 | // Use of this source code is governed by an MIT license 3 | // that can be found in the LICENSE file. 4 | 5 | #ifndef _DWARFPP_SMALL_VECTOR_HH_ 6 | #define _DWARFPP_SMALL_VECTOR_HH_ 7 | 8 | DWARFPP_BEGIN_NAMESPACE 9 | 10 | /** 11 | * A vector-like class that only heap allocates above a specified 12 | * size. 13 | */ 14 | template 15 | class small_vector 16 | { 17 | public: 18 | typedef T value_type; 19 | typedef value_type& reference; 20 | typedef const value_type& const_reference; 21 | typedef size_t size_type; 22 | 23 | small_vector() 24 | : base((T*)buf), end(base), cap((T*)&buf[sizeof(T[Min])]) 25 | { 26 | } 27 | 28 | small_vector(const small_vector &o) 29 | : base((T*)buf), end(base), cap((T*)&buf[sizeof(T[Min])]) 30 | { 31 | *this = o; 32 | } 33 | 34 | small_vector(small_vector &&o) 35 | : base((T*)buf), end(base), cap((T*)&buf[sizeof(T[Min])]) 36 | { 37 | if ((char*)o.base == o.buf) { 38 | // Elements are inline; have to copy them 39 | base = (T*)buf; 40 | end = base; 41 | cap = (T*)&buf[sizeof(T[Min])]; 42 | 43 | *this = o; 44 | o.clear(); 45 | } else { 46 | // Elements are external; swap pointers 47 | base = o.base; 48 | end = o.end; 49 | cap = o.cap; 50 | 51 | o.base = (T*)o.buf; 52 | o.end = o.base; 53 | o.cap = (T*)&o.buf[sizeof(T[Min])]; 54 | } 55 | } 56 | 57 | ~small_vector() 58 | { 59 | clear(); 60 | if ((char*)base != buf) 61 | delete[] (char*)base; 62 | } 63 | 64 | small_vector &operator=(const small_vector &o) 65 | { 66 | size_type osize = o.size(); 67 | clear(); 68 | reserve(osize); 69 | for (size_type i = 0; i < osize; i++) 70 | new (&base[i]) T(o[i]); 71 | end = base + osize; 72 | return *this; 73 | } 74 | 75 | size_type size() const 76 | { 77 | return end - base; 78 | } 79 | 80 | bool empty() const 81 | { 82 | return base == end; 83 | } 84 | 85 | void reserve(size_type n) 86 | { 87 | if (n <= (size_type)(cap - base)) 88 | return; 89 | 90 | size_type target = cap - base; 91 | if (target == 0) 92 | target = 1; 93 | while (target < n) 94 | target <<= 1; 95 | 96 | char *newbuf = new char[sizeof(T[target])]; 97 | T *src = base, *dest = (T*)newbuf; 98 | for (; src < end; src++, dest++) { 99 | new(dest) T(*src); 100 | dest->~T(); 101 | } 102 | if ((char*)base != buf) 103 | delete[] (char*)base; 104 | base = (T*)newbuf; 105 | end = dest; 106 | cap = base + target; 107 | } 108 | 109 | reference operator[](size_type n) 110 | { 111 | return base[n]; 112 | } 113 | 114 | const_reference operator[](size_type n) const 115 | { 116 | return base[n]; 117 | } 118 | 119 | reference at(size_type n) 120 | { 121 | return base[n]; 122 | } 123 | 124 | const_reference at(size_type n) const 125 | { 126 | return base[n]; 127 | } 128 | 129 | /** 130 | * "Reverse at". revat(0) is equivalent to back(). revat(1) 131 | * is the element before back. Etc. 132 | */ 133 | reference revat(size_type n) 134 | { 135 | return *(end - 1 - n); 136 | } 137 | 138 | const_reference revat(size_type n) const 139 | { 140 | return *(end - 1 - n); 141 | } 142 | 143 | reference front() 144 | { 145 | return base[0]; 146 | } 147 | 148 | const_reference front() const 149 | { 150 | return base[0]; 151 | } 152 | 153 | reference back() 154 | { 155 | return *(end-1); 156 | } 157 | 158 | const_reference back() const 159 | { 160 | return *(end-1); 161 | } 162 | 163 | void push_back(const T& x) 164 | { 165 | reserve(size() + 1); 166 | new (end) T(x); 167 | end++; 168 | } 169 | 170 | void push_back(T&& x) 171 | { 172 | reserve(size() + 1); 173 | new (end) T(std::move(x)); 174 | end++; 175 | } 176 | 177 | void pop_back() 178 | { 179 | end--; 180 | end->~T(); 181 | } 182 | 183 | void clear() 184 | { 185 | for (T* p = base; p < end; ++p) 186 | p->~T(); 187 | end = base; 188 | } 189 | 190 | private: 191 | char buf[sizeof(T[Min])]; 192 | T *base, *end, *cap; 193 | }; 194 | 195 | DWARFPP_END_NAMESPACE 196 | 197 | #endif 198 | -------------------------------------------------------------------------------- /elf/enum-print.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2013 Austin T. Clements. All rights reserved. 2 | # Use of this source code is governed by an MIT license 3 | # that can be found in the LICENSE file. 4 | 5 | import sys, re 6 | from optparse import OptionParser 7 | 8 | def read_toks(): 9 | data = sys.stdin.read() 10 | while data: 11 | data = data.lstrip() 12 | if data.startswith("//") or data.startswith("#"): 13 | data = data.split("\n",1)[1] 14 | elif data.startswith("/*"): 15 | data = data.split("*/",1)[1] 16 | elif data.startswith("\"") or data.startswith("'"): 17 | c = data[0] 18 | m = re.match(r'%s([^\\%s]|\\.)*%s' % (c,c,c), data) 19 | yield m.group(0) 20 | data = data[m.end():] 21 | else: 22 | m = re.match(r"[_a-zA-Z0-9]+|[{}();]|[^_a-zA-Z0-9 \n\t\f]+", data) 23 | yield m.group(0) 24 | data = data[m.end():] 25 | 26 | enums = {} 27 | 28 | def do_top_level(toks, ns=[]): 29 | while toks: 30 | tok = toks.pop(0) 31 | if tok == "enum" and toks[0] == "class": 32 | toks.pop(0) 33 | name = toks.pop(0) 34 | # Get to the first token in the body 35 | while toks.pop(0) != "{": 36 | pass 37 | # Consume body and close brace 38 | do_enum_body("::".join(ns + [name]), toks) 39 | elif tok == "class": 40 | name = do_qname(toks) 41 | # Find the class body, if there is one 42 | while toks[0] != "{" and toks[0] != ";": 43 | toks.pop(0) 44 | # Enter the class's namespace 45 | if toks[0] == "{": 46 | toks.pop(0) 47 | do_top_level(toks, ns + [name]) 48 | elif tok == "{": 49 | # Enter an unknown namespace 50 | do_top_level(toks, ns + [None]) 51 | elif tok == "}": 52 | # Exit the namespace 53 | assert len(ns) 54 | return 55 | elif not ns and tok == "string" and toks[:2] == ["to_string", "("]: 56 | # Get the argument type and name 57 | toks.pop(0) 58 | toks.pop(0) 59 | typ = do_qname(toks) 60 | if typ not in enums: 61 | continue 62 | arg = toks.pop(0) 63 | assert toks[0] == ")" 64 | 65 | if typ in options.mask: 66 | make_to_string_mask(typ, arg) 67 | else: 68 | make_to_string(typ, arg) 69 | 70 | def fmt_value(typ, key): 71 | if options.no_type: 72 | val = key 73 | else: 74 | val = "%s%s%s" % (typ, options.separator, key) 75 | if options.strip_underscore: 76 | val = val.strip("_") 77 | return val 78 | 79 | def expr_remainder(typ, arg): 80 | if options.hex: 81 | return "\"(%s)0x\" + to_hex((int)%s)" % (typ, arg) 82 | else: 83 | return "\"(%s)\" + std::to_string((int)%s)" % (typ, arg) 84 | 85 | def make_to_string(typ, arg): 86 | print("std::string") 87 | print("to_string(%s %s)" % (typ, arg)) 88 | print("{") 89 | print(" switch (%s) {" % arg) 90 | for key in enums[typ]: 91 | if key in options.exclude: 92 | print(" case %s::%s: break;" % (typ, key)) 93 | continue 94 | print(" case %s::%s: return \"%s\";" % \ 95 | (typ, key, fmt_value(typ, key))) 96 | print(" }") 97 | print(" return %s;" % expr_remainder(typ, arg)) 98 | print("}") 99 | print() 100 | 101 | def make_to_string_mask(typ, arg): 102 | print("std::string") 103 | print("to_string(%s %s)" % (typ, arg)) 104 | print("{") 105 | print(" std::string res;") 106 | for key in enums[typ]: 107 | if key in options.exclude: 108 | continue 109 | print(" if ((%s & %s::%s) == %s::%s) { res += \"%s|\"; %s &= ~%s::%s; }" % \ 110 | (arg, typ, key, typ, key, fmt_value(typ, key), arg, typ, key)) 111 | print(" if (res.empty() || %s != (%s)0) res += %s;" % \ 112 | (arg, typ, expr_remainder(typ, arg))) 113 | print(" else res.pop_back();") 114 | print(" return res;") 115 | print("}") 116 | print() 117 | 118 | def do_enum_body(name, toks): 119 | keys = [] 120 | while True: 121 | key = toks.pop(0) 122 | if key == "}": 123 | assert toks.pop(0) == ";" 124 | enums[name] = keys 125 | return 126 | keys.append(key) 127 | if toks[0] == "=": 128 | toks.pop(0) 129 | toks.pop(0) 130 | if toks[0] == ",": 131 | toks.pop(0) 132 | else: 133 | assert toks[0] == "}" 134 | 135 | def do_qname(toks): 136 | # Get a nested-name-specifier followed by an identifier 137 | res = [] 138 | while True: 139 | res.append(toks.pop(0)) 140 | if toks[0] != "::": 141 | return "::".join(res) 142 | toks.pop(0) 143 | 144 | parser = OptionParser() 145 | parser.add_option("-x", "--exclude", dest="exclude", action="append", 146 | help="exclude FIELD", metavar="FIELD", default=[]) 147 | parser.add_option("-u", "--strip-underscore", dest="strip_underscore", 148 | action="store_true", 149 | help="strip leading and trailing underscores") 150 | parser.add_option("-s", "--separator", dest="separator", 151 | help="use SEP between type and field", metavar="SEP", 152 | default="::") 153 | parser.add_option("--hex", dest="hex", action="store_true", 154 | help="return unknown values in hex", default=False) 155 | parser.add_option("--no-type", dest="no_type", action="store_true", 156 | help="omit type") 157 | parser.add_option("--mask", dest="mask", action="append", 158 | help="treat TYPE as a bit-mask", metavar="TYPE", default=[]) 159 | (options, args) = parser.parse_args() 160 | if args: 161 | parser.error("expected 0 arguments") 162 | 163 | do_top_level(list(read_toks())) 164 | -------------------------------------------------------------------------------- /dwarf/cursor.cc: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013 Austin T. Clements. All rights reserved. 2 | // Use of this source code is governed by an MIT license 3 | // that can be found in the LICENSE file. 4 | 5 | #include "internal.hh" 6 | 7 | #include 8 | #include 9 | 10 | using namespace std; 11 | 12 | DWARFPP_BEGIN_NAMESPACE 13 | 14 | int64_t 15 | cursor::sleb128() 16 | { 17 | // Appendix C 18 | uint64_t result = 0; 19 | unsigned shift = 0; 20 | while (pos < sec->end) { 21 | uint8_t byte = *(uint8_t*)(pos++); 22 | result |= (uint64_t)(byte & 0x7f) << shift; 23 | shift += 7; 24 | if ((byte & 0x80) == 0) { 25 | if (shift < sizeof(result)*8 && (byte & 0x40)) 26 | result |= -((uint64_t)1 << shift); 27 | return result; 28 | } 29 | } 30 | underflow(); 31 | return 0; 32 | } 33 | 34 | shared_ptr
35 | cursor::subsection() 36 | { 37 | // Section 7.4 38 | const char *begin = pos; 39 | section_length length = fixed(); 40 | format fmt; 41 | if (length < 0xfffffff0) { 42 | fmt = format::dwarf32; 43 | length += sizeof(uword); 44 | } else if (length == 0xffffffff) { 45 | length = fixed(); 46 | fmt = format::dwarf64; 47 | length += sizeof(uword) + sizeof(uint64_t); 48 | } else { 49 | throw format_error("initial length has reserved value"); 50 | } 51 | pos = begin + length; 52 | return make_shared
(sec->type, begin, length, sec->ord, fmt); 53 | } 54 | 55 | void 56 | cursor::skip_initial_length() 57 | { 58 | switch (sec->fmt) { 59 | case format::dwarf32: 60 | pos += sizeof(uword); 61 | break; 62 | case format::dwarf64: 63 | pos += sizeof(uword) + sizeof(uint64_t); 64 | break; 65 | default: 66 | throw logic_error("cannot skip initial length with unknown format"); 67 | } 68 | } 69 | 70 | section_offset 71 | cursor::offset() 72 | { 73 | switch (sec->fmt) { 74 | case format::dwarf32: 75 | return fixed(); 76 | case format::dwarf64: 77 | return fixed(); 78 | default: 79 | throw logic_error("cannot read offset with unknown format"); 80 | } 81 | } 82 | 83 | void 84 | cursor::string(std::string &out) 85 | { 86 | size_t size; 87 | const char *p = this->cstr(&size); 88 | out.resize(size); 89 | memmove(&out.front(), p, size); 90 | } 91 | 92 | const char * 93 | cursor::cstr(size_t *size_out) 94 | { 95 | // Scan string size 96 | const char *p = pos; 97 | while (pos < sec->end && *pos) 98 | pos++; 99 | if (pos == sec->end) 100 | throw format_error("unterminated string"); 101 | if (size_out) 102 | *size_out = pos - p; 103 | pos++; 104 | return p; 105 | } 106 | 107 | void 108 | cursor::skip_form(DW_FORM form) 109 | { 110 | section_offset tmp; 111 | 112 | // Section 7.5.4 113 | switch (form) { 114 | case DW_FORM::addr: 115 | pos += sec->addr_size; 116 | break; 117 | case DW_FORM::sec_offset: 118 | case DW_FORM::ref_addr: 119 | case DW_FORM::strp: 120 | switch (sec->fmt) { 121 | case format::dwarf32: 122 | pos += 4; 123 | break; 124 | case format::dwarf64: 125 | pos += 8; 126 | break; 127 | case format::unknown: 128 | throw logic_error("cannot read form with unknown format"); 129 | } 130 | break; 131 | 132 | // size+data forms 133 | case DW_FORM::block1: 134 | tmp = fixed(); 135 | pos += tmp; 136 | break; 137 | case DW_FORM::block2: 138 | tmp = fixed(); 139 | pos += tmp; 140 | break; 141 | case DW_FORM::block4: 142 | tmp = fixed(); 143 | pos += tmp; 144 | break; 145 | case DW_FORM::block: 146 | case DW_FORM::exprloc: 147 | tmp = uleb128(); 148 | pos += tmp; 149 | break; 150 | 151 | // fixed-length forms 152 | case DW_FORM::flag_present: 153 | break; 154 | case DW_FORM::flag: 155 | case DW_FORM::data1: 156 | case DW_FORM::ref1: 157 | pos += 1; 158 | break; 159 | case DW_FORM::data2: 160 | case DW_FORM::ref2: 161 | pos += 2; 162 | break; 163 | case DW_FORM::data4: 164 | case DW_FORM::ref4: 165 | pos += 4; 166 | break; 167 | case DW_FORM::data8: 168 | case DW_FORM::ref_sig8: 169 | pos += 8; 170 | break; 171 | 172 | // variable-length forms 173 | case DW_FORM::sdata: 174 | case DW_FORM::udata: 175 | case DW_FORM::ref_udata: 176 | while (pos < sec->end && (*(uint8_t*)pos & 0x80)) 177 | pos++; 178 | pos++; 179 | break; 180 | case DW_FORM::string: 181 | while (pos < sec->end && *pos) 182 | pos++; 183 | pos++; 184 | break; 185 | 186 | case DW_FORM::indirect: 187 | skip_form((DW_FORM)uleb128()); 188 | break; 189 | 190 | default: 191 | throw format_error("unknown form " + to_string(form)); 192 | } 193 | } 194 | 195 | void 196 | cursor::underflow() 197 | { 198 | throw underflow_error("cannot read past end of DWARF section"); 199 | } 200 | 201 | DWARFPP_END_NAMESPACE 202 | -------------------------------------------------------------------------------- /dwarf/die.cc: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013 Austin T. Clements. All rights reserved. 2 | // Use of this source code is governed by an MIT license 3 | // that can be found in the LICENSE file. 4 | 5 | #include "internal.hh" 6 | 7 | using namespace std; 8 | 9 | DWARFPP_BEGIN_NAMESPACE 10 | 11 | die::die(const unit *cu) 12 | : cu(cu), abbrev(nullptr) 13 | { 14 | } 15 | 16 | const unit & 17 | die::get_unit() const 18 | { 19 | return *cu; 20 | } 21 | 22 | section_offset 23 | die::get_section_offset() const 24 | { 25 | return cu->get_section_offset() + offset; 26 | } 27 | 28 | void 29 | die::read(section_offset off) 30 | { 31 | cursor cur(cu->data(), off); 32 | 33 | offset = off; 34 | 35 | abbrev_code acode = cur.uleb128(); 36 | if (acode == 0) { 37 | abbrev = nullptr; 38 | next = cur.get_section_offset(); 39 | return; 40 | } 41 | abbrev = &cu->get_abbrev(acode); 42 | 43 | tag = abbrev->tag; 44 | 45 | // XXX We can pre-compute almost all of this work in the 46 | // abbrev_entry. 47 | attrs.clear(); 48 | attrs.reserve(abbrev->attributes.size()); 49 | for (auto &attr : abbrev->attributes) { 50 | attrs.push_back(cur.get_section_offset()); 51 | cur.skip_form(attr.form); 52 | } 53 | next = cur.get_section_offset(); 54 | } 55 | 56 | bool 57 | die::has(DW_AT attr) const 58 | { 59 | if (!abbrev) 60 | return false; 61 | // XXX Totally lame 62 | for (auto &a : abbrev->attributes) 63 | if (a.name == attr) 64 | return true; 65 | return false; 66 | } 67 | 68 | value 69 | die::operator[](DW_AT attr) const 70 | { 71 | // XXX We can pre-compute almost all of this work in the 72 | // abbrev_entry. 73 | if (abbrev) { 74 | int i = 0; 75 | for (auto &a : abbrev->attributes) { 76 | if (a.name == attr) 77 | return value(cu, a.name, a.form, a.type, attrs[i]); 78 | i++; 79 | } 80 | } 81 | throw out_of_range("DIE does not have attribute " + to_string(attr)); 82 | } 83 | 84 | value 85 | die::resolve(DW_AT attr) const 86 | { 87 | // DWARF4 section 2.13, DWARF4 section 3.3.8 88 | 89 | // DWARF4 is unclear about what to do when there's both a 90 | // DW_AT::specification and a DW_AT::abstract_origin. 91 | // Conceptually, though, a concrete inlined instance cannot 92 | // itself complete an external function that wasn't first 93 | // completed by its abstract instance, so we first try to 94 | // resolve abstract_origin, then we resolve specification. 95 | 96 | // XXX This traverses the abbrevs at least twice and 97 | // potentially several more times 98 | 99 | if (has(attr)) 100 | return (*this)[attr]; 101 | 102 | if (has(DW_AT::abstract_origin)) { 103 | die ao = (*this)[DW_AT::abstract_origin].as_reference(); 104 | if (ao.has(attr)) 105 | return ao[attr]; 106 | if (ao.has(DW_AT::specification)) { 107 | die s = ao[DW_AT::specification].as_reference(); 108 | if (s.has(attr)) 109 | return s[attr]; 110 | } 111 | } else if (has(DW_AT::specification)) { 112 | die s = (*this)[DW_AT::specification].as_reference(); 113 | if (s.has(attr)) 114 | return s[attr]; 115 | } 116 | 117 | return value(); 118 | } 119 | 120 | die::iterator 121 | die::begin() const 122 | { 123 | if (!abbrev || !abbrev->children) 124 | return end(); 125 | return iterator(cu, next); 126 | } 127 | 128 | die::iterator::iterator(const unit *cu, section_offset off) 129 | : d(cu) 130 | { 131 | d.read(off); 132 | } 133 | 134 | die::iterator & 135 | die::iterator::operator++() 136 | { 137 | if (!d.abbrev) 138 | return *this; 139 | 140 | if (!d.abbrev->children) { 141 | // The DIE has no children, so its successor follows 142 | // immediately 143 | d.read(d.next); 144 | } else if (d.has(DW_AT::sibling)) { 145 | // They made it easy on us. Follow the sibling 146 | // pointer. XXX Probably worth optimizing 147 | d = d[DW_AT::sibling].as_reference(); 148 | } else { 149 | // It's a hard-knock life. We have to iterate through 150 | // the children to find the next DIE. 151 | // XXX Particularly unfortunate if the user is doing a 152 | // DFS, since this will result in N^2 behavior. Maybe 153 | // a small cache of terminator locations in the CU? 154 | iterator sub(d.cu, d.next); 155 | while (sub->abbrev) 156 | ++sub; 157 | d.read(sub->next); 158 | } 159 | 160 | return *this; 161 | } 162 | 163 | const vector > 164 | die::attributes() const 165 | { 166 | vector > res; 167 | 168 | if (!abbrev) 169 | return res; 170 | 171 | // XXX Quite slow, especially when using this to traverse an 172 | // entire DIE tree since each DIE will produce a new vector 173 | // (whereas other vectors get reused). Might be worth a 174 | // custom iterator. 175 | int i = 0; 176 | for (auto &a : abbrev->attributes) { 177 | res.push_back(make_pair(a.name, value(cu, a.name, a.form, a.type, attrs[i]))); 178 | i++; 179 | } 180 | return res; 181 | } 182 | 183 | bool 184 | die::operator==(const die &o) const 185 | { 186 | return cu == o.cu && offset == o.offset; 187 | } 188 | 189 | bool 190 | die::operator!=(const die &o) const 191 | { 192 | return !(*this == o); 193 | } 194 | 195 | DWARFPP_END_NAMESPACE 196 | 197 | size_t 198 | std::hash::operator()(const dwarf::die &a) const 199 | { 200 | return hash()(a.cu) ^ 201 | hash()(a.get_unit_offset()); 202 | } 203 | -------------------------------------------------------------------------------- /dwarf/abbrev.cc: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013 Austin T. Clements. All rights reserved. 2 | // Use of this source code is governed by an MIT license 3 | // that can be found in the LICENSE file. 4 | 5 | #include "internal.hh" 6 | 7 | using namespace std; 8 | 9 | DWARFPP_BEGIN_NAMESPACE 10 | 11 | static value::type 12 | resolve_type(DW_AT name, DW_FORM form) 13 | { 14 | switch (form) { 15 | case DW_FORM::addr: 16 | return value::type::address; 17 | 18 | case DW_FORM::block: 19 | case DW_FORM::block1: 20 | case DW_FORM::block2: 21 | case DW_FORM::block4: 22 | // Prior to DWARF 4, exprlocs didn't have their own 23 | // form and were represented as blocks. 24 | // XXX Should this be predicated on version? 25 | switch (name) { 26 | case DW_AT::location: 27 | case DW_AT::byte_size: 28 | case DW_AT::bit_offset: 29 | case DW_AT::bit_size: 30 | case DW_AT::string_length: 31 | case DW_AT::lower_bound: 32 | case DW_AT::return_addr: 33 | case DW_AT::bit_stride: 34 | case DW_AT::upper_bound: 35 | case DW_AT::count: 36 | case DW_AT::data_member_location: 37 | case DW_AT::frame_base: 38 | case DW_AT::segment: 39 | case DW_AT::static_link: 40 | case DW_AT::use_location: 41 | case DW_AT::vtable_elem_location: 42 | case DW_AT::allocated: 43 | case DW_AT::associated: 44 | case DW_AT::data_location: 45 | case DW_AT::byte_stride: 46 | return value::type::exprloc; 47 | default: 48 | return value::type::block; 49 | } 50 | 51 | case DW_FORM::data4: 52 | case DW_FORM::data8: 53 | // Prior to DWARF 4, section offsets didn't have their 54 | // own form and were represented as data4 or data8. 55 | // DWARF 3 clarified that types that accepted both 56 | // constants and section offsets were to treat data4 57 | // and data8 as section offsets and other constant 58 | // forms as constants. 59 | // XXX Should this be predicated on version? 60 | switch (name) { 61 | case DW_AT::location: 62 | case DW_AT::stmt_list: 63 | case DW_AT::string_length: 64 | case DW_AT::return_addr: 65 | case DW_AT::start_scope: 66 | case DW_AT::data_member_location: 67 | case DW_AT::frame_base: 68 | case DW_AT::macro_info: 69 | case DW_AT::segment: 70 | case DW_AT::static_link: 71 | case DW_AT::use_location: 72 | case DW_AT::vtable_elem_location: 73 | case DW_AT::ranges: 74 | goto sec_offset; 75 | default: 76 | // Fall through 77 | break; 78 | } 79 | case DW_FORM::data1: 80 | case DW_FORM::data2: 81 | return value::type::constant; 82 | case DW_FORM::udata: 83 | return value::type::uconstant; 84 | case DW_FORM::sdata: 85 | return value::type::sconstant; 86 | 87 | case DW_FORM::exprloc: 88 | return value::type::exprloc; 89 | 90 | case DW_FORM::flag: 91 | case DW_FORM::flag_present: 92 | return value::type::flag; 93 | 94 | case DW_FORM::ref1: 95 | case DW_FORM::ref2: 96 | case DW_FORM::ref4: 97 | case DW_FORM::ref8: 98 | case DW_FORM::ref_addr: 99 | case DW_FORM::ref_sig8: 100 | case DW_FORM::ref_udata: 101 | return value::type::reference; 102 | 103 | case DW_FORM::string: 104 | case DW_FORM::strp: 105 | return value::type::string; 106 | 107 | case DW_FORM::indirect: 108 | // There's nothing meaningful we can do 109 | return value::type::invalid; 110 | 111 | case DW_FORM::sec_offset: 112 | sec_offset: 113 | // The type of this form depends on the attribute 114 | switch (name) { 115 | case DW_AT::stmt_list: 116 | return value::type::line; 117 | 118 | case DW_AT::location: 119 | case DW_AT::string_length: 120 | case DW_AT::return_addr: 121 | case DW_AT::data_member_location: 122 | case DW_AT::frame_base: 123 | case DW_AT::segment: 124 | case DW_AT::static_link: 125 | case DW_AT::use_location: 126 | case DW_AT::vtable_elem_location: 127 | return value::type::loclist; 128 | 129 | case DW_AT::macro_info: 130 | return value::type::mac; 131 | 132 | case DW_AT::start_scope: 133 | case DW_AT::ranges: 134 | return value::type::rangelist; 135 | 136 | default: 137 | throw format_error("DW_FORM_sec_offset not expected for attribute " + 138 | to_string(name)); 139 | } 140 | } 141 | throw format_error("unknown attribute form " + to_string(form)); 142 | } 143 | 144 | attribute_spec::attribute_spec(DW_AT name, DW_FORM form) 145 | : name(name), form(form), type(resolve_type(name, form)) 146 | { 147 | } 148 | 149 | bool 150 | abbrev_entry::read(cursor *cur) 151 | { 152 | attributes.clear(); 153 | 154 | // Section 7.5.3 155 | code = cur->uleb128(); 156 | if (!code) 157 | return false; 158 | 159 | tag = (DW_TAG)cur->uleb128(); 160 | children = cur->fixed() == DW_CHILDREN::yes; 161 | while (1) { 162 | DW_AT name = (DW_AT)cur->uleb128(); 163 | DW_FORM form = (DW_FORM)cur->uleb128(); 164 | if (name == (DW_AT)0 && form == (DW_FORM)0) 165 | break; 166 | attributes.push_back(attribute_spec(name, form)); 167 | } 168 | attributes.shrink_to_fit(); 169 | return true; 170 | } 171 | 172 | DWARFPP_END_NAMESPACE 173 | -------------------------------------------------------------------------------- /dwarf/internal.hh: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013 Austin T. Clements. All rights reserved. 2 | // Use of this source code is governed by an MIT license 3 | // that can be found in the LICENSE file. 4 | 5 | #ifndef _DWARFPP_INTERNAL_HH_ 6 | #define _DWARFPP_INTERNAL_HH_ 7 | 8 | #include "dwarf++.hh" 9 | #include "../elf/to_hex.hh" 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | DWARFPP_BEGIN_NAMESPACE 17 | 18 | enum class format 19 | { 20 | unknown, 21 | dwarf32, 22 | dwarf64 23 | }; 24 | 25 | enum class byte_order 26 | { 27 | lsb, 28 | msb 29 | }; 30 | 31 | /** 32 | * Return this system's native byte order. 33 | */ 34 | static inline byte_order 35 | native_order() 36 | { 37 | static const union 38 | { 39 | int i; 40 | char c[sizeof(int)]; 41 | } test = {1}; 42 | 43 | return test.c[0] == 1 ? byte_order::lsb : byte_order::msb; 44 | } 45 | 46 | /** 47 | * A single DWARF section or a slice of a section. This also tracks 48 | * dynamic information necessary to decode values in this section. 49 | */ 50 | struct section 51 | { 52 | section_type type; 53 | const char *begin, *end; 54 | const format fmt; 55 | const byte_order ord; 56 | unsigned addr_size; 57 | 58 | section(section_type type, const void *begin, 59 | section_length length, 60 | byte_order ord, format fmt = format::unknown, 61 | unsigned addr_size = 0) 62 | : type(type), begin((char*)begin), end((char*)begin + length), 63 | fmt(fmt), ord(ord), addr_size(addr_size) { } 64 | 65 | section(const section &o) = default; 66 | 67 | std::shared_ptr
slice(section_offset start, section_length len, 68 | format fmt = format::unknown, 69 | unsigned addr_size = 0) 70 | { 71 | if (fmt == format::unknown) 72 | fmt = this->fmt; 73 | if (addr_size == 0) 74 | addr_size = this->addr_size; 75 | 76 | return std::make_shared
( 77 | type, begin+start, 78 | std::min(len, (section_length)(end-begin)), 79 | ord, fmt, addr_size); 80 | } 81 | 82 | size_t size() const 83 | { 84 | return end - begin; 85 | } 86 | }; 87 | 88 | /** 89 | * A cursor pointing into a DWARF section. Provides deserialization 90 | * operations and bounds checking. 91 | */ 92 | struct cursor 93 | { 94 | // XXX There's probably a lot of overhead to maintaining the 95 | // shared pointer to the section from this. Perhaps the rule 96 | // should be that all objects keep the dwarf::impl alive 97 | // (directly or indirectly) and that keeps the loader alive, 98 | // so a cursor just needs a regular section*. 99 | 100 | std::shared_ptr
sec; 101 | const char *pos; 102 | 103 | cursor() 104 | : pos(nullptr) { } 105 | cursor(const std::shared_ptr
sec, section_offset offset = 0) 106 | : sec(sec), pos(sec->begin + offset) { } 107 | 108 | /** 109 | * Read a subsection. The cursor must be at an initial 110 | * length. After, the cursor will point just past the end of 111 | * the subsection. The returned section has the appropriate 112 | * DWARF format and begins at the current location of the 113 | * cursor (so this is usually followed by a 114 | * skip_initial_length). 115 | */ 116 | std::shared_ptr
subsection(); 117 | std::int64_t sleb128(); 118 | section_offset offset(); 119 | void string(std::string &out); 120 | const char *cstr(size_t *size_out = nullptr); 121 | 122 | void 123 | ensure(section_offset bytes) 124 | { 125 | if ((section_offset)(sec->end - pos) < bytes || pos >= sec->end) 126 | underflow(); 127 | } 128 | 129 | template 130 | T fixed() 131 | { 132 | ensure(sizeof(T)); 133 | static_assert(sizeof(T) <= 8, "T too big"); 134 | uint64_t val = 0; 135 | const unsigned char *p = (const unsigned char*)pos; 136 | if (sec->ord == byte_order::lsb) { 137 | for (unsigned i = 0; i < sizeof(T); i++) 138 | val |= ((uint64_t)p[i]) << (i * 8); 139 | } else { 140 | for (unsigned i = 0; i < sizeof(T); i++) 141 | val = (val << 8) | (uint64_t)p[i]; 142 | } 143 | pos += sizeof(T); 144 | return (T)val; 145 | } 146 | 147 | std::uint64_t uleb128() 148 | { 149 | // Appendix C 150 | // XXX Pre-compute all two byte ULEB's 151 | std::uint64_t result = 0; 152 | int shift = 0; 153 | while (pos < sec->end) { 154 | uint8_t byte = *(uint8_t*)(pos++); 155 | result |= (uint64_t)(byte & 0x7f) << shift; 156 | if ((byte & 0x80) == 0) 157 | return result; 158 | shift += 7; 159 | } 160 | underflow(); 161 | return 0; 162 | } 163 | 164 | taddr address() 165 | { 166 | switch (sec->addr_size) { 167 | case 1: 168 | return fixed(); 169 | case 2: 170 | return fixed(); 171 | case 4: 172 | return fixed(); 173 | case 8: 174 | return fixed(); 175 | default: 176 | throw std::runtime_error("address size " + std::to_string(sec->addr_size) + " not supported"); 177 | } 178 | } 179 | 180 | void skip_initial_length(); 181 | void skip_form(DW_FORM form); 182 | 183 | cursor &operator+=(section_offset offset) 184 | { 185 | pos += offset; 186 | return *this; 187 | } 188 | 189 | cursor operator+(section_offset offset) const 190 | { 191 | return cursor(sec, pos + offset); 192 | } 193 | 194 | bool operator<(const cursor &o) const 195 | { 196 | return pos < o.pos; 197 | } 198 | 199 | bool end() const 200 | { 201 | return pos >= sec->end; 202 | } 203 | 204 | bool valid() const 205 | { 206 | return !!pos; 207 | } 208 | 209 | section_offset get_section_offset() const 210 | { 211 | return pos - sec->begin; 212 | } 213 | 214 | private: 215 | cursor(const std::shared_ptr
sec, const char *pos) 216 | : sec(sec), pos(pos) { } 217 | 218 | void underflow(); 219 | }; 220 | 221 | /** 222 | * An attribute specification in an abbrev. 223 | */ 224 | struct attribute_spec 225 | { 226 | DW_AT name; 227 | DW_FORM form; 228 | 229 | // Computed information 230 | value::type type; 231 | 232 | attribute_spec(DW_AT name, DW_FORM form); 233 | }; 234 | 235 | typedef std::uint64_t abbrev_code; 236 | 237 | /** 238 | * An entry in .debug_abbrev. 239 | */ 240 | struct abbrev_entry 241 | { 242 | abbrev_code code; 243 | DW_TAG tag; 244 | bool children; 245 | std::vector attributes; 246 | 247 | abbrev_entry() : code(0) { } 248 | 249 | bool read(cursor *cur); 250 | }; 251 | 252 | /** 253 | * A section header in .debug_pubnames or .debug_pubtypes. 254 | */ 255 | struct name_unit 256 | { 257 | uhalf version; 258 | section_offset debug_info_offset; 259 | section_length debug_info_length; 260 | // Cursor to the first name_entry in this unit. This cursor's 261 | // section is limited to this unit. 262 | cursor entries; 263 | 264 | void read(cursor *cur) 265 | { 266 | // Section 7.19 267 | std::shared_ptr
subsec = cur->subsection(); 268 | cursor sub(subsec); 269 | sub.skip_initial_length(); 270 | version = sub.fixed(); 271 | if (version != 2) 272 | throw format_error("unknown name unit version " + std::to_string(version)); 273 | debug_info_offset = sub.offset(); 274 | debug_info_length = sub.offset(); 275 | entries = sub; 276 | } 277 | }; 278 | 279 | /** 280 | * An entry in a .debug_pubnames or .debug_pubtypes unit. 281 | */ 282 | struct name_entry 283 | { 284 | section_offset offset; 285 | std::string name; 286 | 287 | void read(cursor *cur) 288 | { 289 | offset = cur->offset(); 290 | cur->string(name); 291 | } 292 | }; 293 | 294 | DWARFPP_END_NAMESPACE 295 | 296 | #endif 297 | -------------------------------------------------------------------------------- /dwarf/attrs.cc: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013 Austin T. Clements. All rights reserved. 2 | // Use of this source code is governed by an MIT license 3 | // that can be found in the LICENSE file. 4 | 5 | #include "dwarf++.hh" 6 | 7 | using namespace std; 8 | 9 | DWARFPP_BEGIN_NAMESPACE 10 | 11 | #define AT_ANY(name) \ 12 | value at_##name(const die &d) \ 13 | { \ 14 | return d[DW_AT::name]; \ 15 | } \ 16 | static_assert(true, "") 17 | 18 | #define AT_ADDRESS(name) \ 19 | taddr at_##name(const die &d) \ 20 | { \ 21 | return d[DW_AT::name].as_address(); \ 22 | } \ 23 | static_assert(true, "") 24 | 25 | #define AT_ENUM(name, type) \ 26 | type at_##name(const die &d) \ 27 | { \ 28 | return (type)d[DW_AT::name].as_uconstant(); \ 29 | } \ 30 | static_assert(true, "") 31 | 32 | #define AT_FLAG(name) \ 33 | bool at_##name(const die &d) \ 34 | { \ 35 | return d[DW_AT::name].as_flag(); \ 36 | } \ 37 | static_assert(true, "") 38 | 39 | #define AT_FLAG_(name) \ 40 | bool at_##name(const die &d) \ 41 | { \ 42 | return d[DW_AT::name##_].as_flag(); \ 43 | } \ 44 | static_assert(true, "") 45 | 46 | #define AT_REFERENCE(name) \ 47 | die at_##name(const die &d) \ 48 | { \ 49 | return d[DW_AT::name].as_reference(); \ 50 | } \ 51 | static_assert(true, "") 52 | 53 | #define AT_STRING(name) \ 54 | string at_##name(const die &d) \ 55 | { \ 56 | return d[DW_AT::name].as_string(); \ 57 | } \ 58 | static_assert(true, "") 59 | 60 | #define AT_UDYNAMIC(name) \ 61 | uint64_t at_##name(const die &d, expr_context *ctx) \ 62 | { \ 63 | return _at_udynamic(DW_AT::name, d, ctx); \ 64 | } \ 65 | static_assert(true, "") 66 | 67 | static uint64_t _at_udynamic(DW_AT attr, const die &d, expr_context *ctx, int depth = 0) 68 | { 69 | // DWARF4 section 2.19 70 | if (depth > 16) 71 | throw format_error("reference depth exceeded for " + to_string(attr)); 72 | 73 | value v(d[attr]); 74 | switch (v.get_type()) { 75 | case value::type::constant: 76 | case value::type::uconstant: 77 | return v.as_uconstant(); 78 | case value::type::reference: 79 | return _at_udynamic(attr, v.as_reference(), ctx, depth + 1); 80 | case value::type::exprloc: 81 | return v.as_exprloc().evaluate(ctx).value; 82 | default: 83 | throw format_error(to_string(attr) + " has unexpected type " + 84 | to_string(v.get_type())); 85 | } 86 | } 87 | 88 | ////////////////////////////////////////////////////////////////// 89 | // 0x0X 90 | // 91 | 92 | AT_REFERENCE(sibling); 93 | // XXX location 94 | AT_STRING(name); 95 | AT_ENUM(ordering, DW_ORD); 96 | AT_UDYNAMIC(byte_size); 97 | AT_UDYNAMIC(bit_offset); 98 | AT_UDYNAMIC(bit_size); 99 | 100 | ////////////////////////////////////////////////////////////////// 101 | // 0x1X 102 | // 103 | 104 | // XXX stmt_list 105 | AT_ADDRESS(low_pc); 106 | taddr 107 | at_high_pc(const die &d) 108 | { 109 | value v(d[DW_AT::high_pc]); 110 | switch (v.get_type()) { 111 | case value::type::address: 112 | return v.as_address(); 113 | case value::type::constant: 114 | case value::type::uconstant: 115 | return at_low_pc(d) + v.as_uconstant(); 116 | default: 117 | throw format_error(to_string(DW_AT::high_pc) + " has unexpected type " + 118 | to_string(v.get_type())); 119 | } 120 | } 121 | AT_ENUM(language, DW_LANG); 122 | AT_REFERENCE(discr); 123 | AT_ANY(discr_value); // XXX Signed or unsigned 124 | AT_ENUM(visibility, DW_VIS); 125 | AT_REFERENCE(import); 126 | // XXX string_length 127 | AT_REFERENCE(common_reference); 128 | AT_STRING(comp_dir); 129 | AT_ANY(const_value); 130 | AT_REFERENCE(containing_type); 131 | // XXX default_value 132 | 133 | ////////////////////////////////////////////////////////////////// 134 | // 0x2X 135 | // 136 | 137 | DW_INL at_inline(const die &d) 138 | { 139 | // XXX Missing attribute is equivalent to DW_INL_not_inlined 140 | // (DWARF4 section 3.3.8) 141 | return (DW_INL)d[DW_AT::inline_].as_uconstant(); 142 | } 143 | AT_FLAG(is_optional); 144 | AT_UDYNAMIC(lower_bound); // XXX Language-based default? 145 | AT_STRING(producer); 146 | AT_FLAG(prototyped); 147 | // XXX return_addr 148 | // XXX start_scope 149 | AT_UDYNAMIC(bit_stride); 150 | AT_UDYNAMIC(upper_bound); 151 | 152 | ////////////////////////////////////////////////////////////////// 153 | // 0x3X 154 | // 155 | 156 | AT_REFERENCE(abstract_origin); 157 | AT_ENUM(accessibility, DW_ACCESS); 158 | // XXX const address_class 159 | AT_FLAG(artificial); 160 | // XXX base_types 161 | AT_ENUM(calling_convention, DW_CC); 162 | AT_UDYNAMIC(count); 163 | expr_result 164 | at_data_member_location(const die &d, expr_context *ctx, taddr base, taddr pc) 165 | { 166 | value v(d[DW_AT::data_member_location]); 167 | switch (v.get_type()) { 168 | case value::type::constant: 169 | case value::type::uconstant: 170 | return {expr_result::type::address, base + v.as_uconstant()}; 171 | case value::type::exprloc: 172 | return v.as_exprloc().evaluate(ctx, base); 173 | case value::type::loclist: 174 | // XXX 175 | throw std::runtime_error("not implemented"); 176 | default: 177 | throw format_error("DW_AT_data_member_location has unexpected type " + 178 | to_string(v.get_type())); 179 | } 180 | } 181 | // XXX decl_column decl_file decl_line 182 | AT_FLAG(declaration); 183 | // XXX discr_list 184 | AT_ENUM(encoding, DW_ATE); 185 | AT_FLAG(external); 186 | 187 | ////////////////////////////////////////////////////////////////// 188 | // 0x4X 189 | // 190 | 191 | // XXX frame_base 192 | die at_friend(const die &d) 193 | { 194 | return d[DW_AT::friend_].as_reference(); 195 | } 196 | AT_ENUM(identifier_case, DW_ID); 197 | // XXX macro_info 198 | AT_REFERENCE(namelist_item); 199 | AT_REFERENCE(priority); // XXX Computed might be useful 200 | // XXX segment 201 | AT_REFERENCE(specification); 202 | // XXX static_link 203 | AT_REFERENCE(type); 204 | // XXX use_location 205 | AT_FLAG(variable_parameter); 206 | // XXX 7.11 The value DW_VIRTUALITY_none is equivalent to the absence 207 | // of the DW_AT_virtuality attribute. 208 | AT_ENUM(virtuality, DW_VIRTUALITY); 209 | // XXX vtable_elem_location 210 | AT_UDYNAMIC(allocated); 211 | AT_UDYNAMIC(associated); 212 | 213 | ////////////////////////////////////////////////////////////////// 214 | // 0x5X 215 | // 216 | 217 | // XXX data_location 218 | AT_UDYNAMIC(byte_stride); 219 | AT_ADDRESS(entry_pc); 220 | AT_FLAG(use_UTF8); 221 | AT_REFERENCE(extension); 222 | rangelist 223 | at_ranges(const die &d) 224 | { 225 | return d[DW_AT::ranges].as_rangelist(); 226 | } 227 | // XXX trampoline 228 | // XXX const call_column, call_file, call_line 229 | AT_STRING(description); 230 | // XXX const binary_scale 231 | // XXX const decimal_scale 232 | AT_REFERENCE(small); 233 | // XXX const decimal_sign 234 | // XXX const digit_count 235 | 236 | ////////////////////////////////////////////////////////////////// 237 | // 0x6X 238 | // 239 | 240 | AT_STRING(picture_string); 241 | AT_FLAG_(mutable); 242 | AT_FLAG(threads_scaled); 243 | AT_FLAG_(explicit); 244 | AT_REFERENCE(object_pointer); 245 | AT_ENUM(endianity, DW_END); 246 | AT_FLAG(elemental); 247 | AT_FLAG(pure); 248 | AT_FLAG(recursive); 249 | AT_REFERENCE(signature); // XXX Computed might be useful 250 | AT_FLAG(main_subprogram); 251 | // XXX const data_bit_offset 252 | AT_FLAG(const_expr); 253 | AT_FLAG(enum_class); 254 | AT_STRING(linkage_name); 255 | 256 | rangelist 257 | die_pc_range(const die &d) 258 | { 259 | // DWARF4 section 2.17 260 | if (d.has(DW_AT::ranges)) 261 | return at_ranges(d); 262 | taddr low = at_low_pc(d); 263 | taddr high = d.has(DW_AT::high_pc) ? at_high_pc(d) : (low + 1); 264 | return rangelist({{low, high}}); 265 | } 266 | 267 | DWARFPP_END_NAMESPACE 268 | -------------------------------------------------------------------------------- /dwarf/value.cc: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013 Austin T. Clements. All rights reserved. 2 | // Use of this source code is governed by an MIT license 3 | // that can be found in the LICENSE file. 4 | 5 | #include "internal.hh" 6 | 7 | #include 8 | 9 | using namespace std; 10 | 11 | DWARFPP_BEGIN_NAMESPACE 12 | 13 | value::value(const unit *cu, 14 | DW_AT name, DW_FORM form, type typ, section_offset offset) 15 | : cu(cu), form(form), typ(typ), offset(offset) { 16 | if (form == DW_FORM::indirect) 17 | resolve_indirect(name); 18 | } 19 | 20 | section_offset 21 | value::get_section_offset() const 22 | { 23 | return cu->get_section_offset() + offset; 24 | } 25 | 26 | taddr 27 | value::as_address() const 28 | { 29 | if (form != DW_FORM::addr) 30 | throw value_type_mismatch("cannot read " + to_string(typ) + " as address"); 31 | 32 | cursor cur(cu->data(), offset); 33 | return cur.address(); 34 | } 35 | 36 | const void * 37 | value::as_block(size_t *size_out) const 38 | { 39 | // XXX Blocks can contain all sorts of things, including 40 | // references, which couldn't be resolved by callers in the 41 | // current minimal API. 42 | cursor cur(cu->data(), offset); 43 | switch (form) { 44 | case DW_FORM::block1: 45 | *size_out = cur.fixed(); 46 | break; 47 | case DW_FORM::block2: 48 | *size_out = cur.fixed(); 49 | break; 50 | case DW_FORM::block4: 51 | *size_out = cur.fixed(); 52 | break; 53 | case DW_FORM::block: 54 | case DW_FORM::exprloc: 55 | *size_out = cur.uleb128(); 56 | break; 57 | default: 58 | throw value_type_mismatch("cannot read " + to_string(typ) + " as block"); 59 | } 60 | cur.ensure(*size_out); 61 | return cur.pos; 62 | } 63 | 64 | uint64_t 65 | value::as_uconstant() const 66 | { 67 | cursor cur(cu->data(), offset); 68 | switch (form) { 69 | case DW_FORM::data1: 70 | return cur.fixed(); 71 | case DW_FORM::data2: 72 | return cur.fixed(); 73 | case DW_FORM::data4: 74 | return cur.fixed(); 75 | case DW_FORM::data8: 76 | return cur.fixed(); 77 | case DW_FORM::udata: 78 | return cur.uleb128(); 79 | default: 80 | throw value_type_mismatch("cannot read " + to_string(typ) + " as uconstant"); 81 | } 82 | } 83 | 84 | int64_t 85 | value::as_sconstant() const 86 | { 87 | cursor cur(cu->data(), offset); 88 | switch (form) { 89 | case DW_FORM::data1: 90 | return cur.fixed(); 91 | case DW_FORM::data2: 92 | return cur.fixed(); 93 | case DW_FORM::data4: 94 | return cur.fixed(); 95 | case DW_FORM::data8: 96 | return cur.fixed(); 97 | case DW_FORM::sdata: 98 | return cur.sleb128(); 99 | default: 100 | throw value_type_mismatch("cannot read " + to_string(typ) + " as sconstant"); 101 | } 102 | } 103 | 104 | expr 105 | value::as_exprloc() const 106 | { 107 | cursor cur(cu->data(), offset); 108 | size_t size; 109 | // Prior to DWARF 4, exprlocs were encoded as blocks. 110 | switch (form) { 111 | case DW_FORM::exprloc: 112 | case DW_FORM::block: 113 | size = cur.uleb128(); 114 | break; 115 | case DW_FORM::block1: 116 | size = cur.fixed(); 117 | break; 118 | case DW_FORM::block2: 119 | size = cur.fixed(); 120 | break; 121 | case DW_FORM::block4: 122 | size = cur.fixed(); 123 | break; 124 | default: 125 | throw value_type_mismatch("cannot read " + to_string(typ) + " as exprloc"); 126 | } 127 | return expr(cu, cur.get_section_offset(), size); 128 | } 129 | 130 | bool 131 | value::as_flag() const 132 | { 133 | switch (form) { 134 | case DW_FORM::flag: { 135 | cursor cur(cu->data(), offset); 136 | return cur.fixed() != 0; 137 | } 138 | case DW_FORM::flag_present: 139 | return true; 140 | default: 141 | throw value_type_mismatch("cannot read " + to_string(typ) + " as flag"); 142 | } 143 | } 144 | 145 | rangelist 146 | value::as_rangelist() const 147 | { 148 | section_offset off = as_sec_offset(); 149 | 150 | // The compilation unit may not have a base address. In this 151 | // case, the first entry in the range list must be a base 152 | // address entry, but we'll just assume 0 for the initial base 153 | // address. 154 | die cudie = cu->root(); 155 | taddr cu_low_pc = cudie.has(DW_AT::low_pc) ? at_low_pc(cudie) : 0; 156 | auto sec = cu->get_dwarf().get_section(section_type::ranges); 157 | auto cusec = cu->data(); 158 | return rangelist(sec, off, cusec->addr_size, cu_low_pc); 159 | } 160 | 161 | die 162 | value::as_reference() const 163 | { 164 | section_offset off; 165 | // XXX Would be nice if we could avoid this. The cursor is 166 | // all overhead here. 167 | cursor cur(cu->data(), offset); 168 | switch (form) { 169 | case DW_FORM::ref1: 170 | off = cur.fixed(); 171 | break; 172 | case DW_FORM::ref2: 173 | off = cur.fixed(); 174 | break; 175 | case DW_FORM::ref4: 176 | off = cur.fixed(); 177 | break; 178 | case DW_FORM::ref8: 179 | off = cur.fixed(); 180 | break; 181 | case DW_FORM::ref_udata: 182 | off = cur.uleb128(); 183 | break; 184 | 185 | case DW_FORM::ref_addr: { 186 | off = cur.offset(); 187 | // These seem to be extremely rare in practice (I 188 | // haven't been able to get gcc to produce a 189 | // ref_addr), so it's not worth caching this lookup. 190 | const compilation_unit *base_cu = nullptr; 191 | for (auto &file_cu : cu->get_dwarf().compilation_units()) { 192 | if (file_cu.get_section_offset() > off) 193 | break; 194 | base_cu = &file_cu; 195 | } 196 | die d(base_cu); 197 | d.read(off - base_cu->get_section_offset()); 198 | return d; 199 | } 200 | 201 | case DW_FORM::ref_sig8: { 202 | uint64_t sig = cur.fixed(); 203 | try { 204 | return cu->get_dwarf().get_type_unit(sig).type(); 205 | } catch (std::out_of_range &e) { 206 | throw format_error("unknown type signature 0x" + to_hex(sig)); 207 | } 208 | } 209 | 210 | default: 211 | throw value_type_mismatch("cannot read " + to_string(typ) + " as reference"); 212 | } 213 | 214 | die d(cu); 215 | d.read(off); 216 | return d; 217 | } 218 | 219 | void 220 | value::as_string(string &buf) const 221 | { 222 | size_t size; 223 | const char *p = as_cstr(&size); 224 | buf.resize(size); 225 | memmove(&buf.front(), p, size); 226 | } 227 | 228 | string 229 | value::as_string() const 230 | { 231 | size_t size; 232 | const char *s = as_cstr(&size); 233 | return string(s, size); 234 | } 235 | 236 | const char * 237 | value::as_cstr(size_t *size_out) const 238 | { 239 | cursor cur(cu->data(), offset); 240 | switch (form) { 241 | case DW_FORM::string: 242 | return cur.cstr(size_out); 243 | case DW_FORM::strp: { 244 | section_offset off = cur.offset(); 245 | cursor scur(cu->get_dwarf().get_section(section_type::str), off); 246 | return scur.cstr(size_out); 247 | } 248 | default: 249 | throw value_type_mismatch("cannot read " + to_string(typ) + " as string"); 250 | } 251 | } 252 | 253 | section_offset 254 | value::as_sec_offset() const 255 | { 256 | // Prior to DWARF 4, sec_offsets were encoded as data4 or 257 | // data8. 258 | cursor cur(cu->data(), offset); 259 | switch (form) { 260 | case DW_FORM::data4: 261 | return cur.fixed(); 262 | case DW_FORM::data8: 263 | return cur.fixed(); 264 | case DW_FORM::sec_offset: 265 | return cur.offset(); 266 | default: 267 | throw value_type_mismatch("cannot read " + to_string(typ) + " as sec_offset"); 268 | } 269 | } 270 | 271 | void 272 | value::resolve_indirect(DW_AT name) 273 | { 274 | if (form != DW_FORM::indirect) 275 | return; 276 | 277 | cursor c(cu->data(), offset); 278 | DW_FORM form; 279 | do { 280 | form = (DW_FORM)c.uleb128(); 281 | } while (form == DW_FORM::indirect); 282 | typ = attribute_spec(name, form).type; 283 | offset = c.get_section_offset(); 284 | } 285 | 286 | string 287 | to_string(const value &v) 288 | { 289 | switch (v.get_type()) { 290 | case value::type::invalid: 291 | return ""; 292 | case value::type::address: 293 | return "0x" + to_hex(v.as_address()); 294 | case value::type::block: { 295 | size_t size; 296 | const char *b = (const char*)v.as_block(&size); 297 | string res = ::to_string(size) + " byte block:"; 298 | for (size_t pos = 0; pos < size; ++pos) { 299 | res += ' '; 300 | res += to_hex(b[pos]); 301 | } 302 | return res; 303 | } 304 | case value::type::constant: 305 | return "0x" + to_hex(v.as_uconstant()); 306 | case value::type::uconstant: 307 | return ::to_string(v.as_uconstant()); 308 | case value::type::sconstant: 309 | return ::to_string(v.as_sconstant()); 310 | case value::type::exprloc: 311 | // XXX 312 | return ""; 313 | case value::type::flag: 314 | return v.as_flag() ? "true" : "false"; 315 | case value::type::line: 316 | return ""; 317 | case value::type::loclist: 318 | return ""; 319 | case value::type::mac: 320 | return ""; 321 | case value::type::rangelist: 322 | return ""; 323 | case value::type::reference: { 324 | die d = v.as_reference(); 325 | auto tu = dynamic_cast(&d.get_unit()); 326 | if (tu) 327 | return "<.debug_types+0x" + to_hex(d.get_section_offset()) + ">"; 328 | return "<0x" + to_hex(d.get_section_offset()) + ">"; 329 | } 330 | case value::type::string: 331 | return v.as_string(); 332 | } 333 | return ""; 334 | } 335 | 336 | DWARFPP_END_NAMESPACE 337 | -------------------------------------------------------------------------------- /elf/elf.cc: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013 Austin T. Clements. All rights reserved. 2 | // Use of this source code is governed by an MIT license 3 | // that can be found in the LICENSE file. 4 | 5 | #include "elf++.hh" 6 | 7 | #include 8 | 9 | using namespace std; 10 | 11 | ELFPP_BEGIN_NAMESPACE 12 | 13 | template class Hdr> 14 | void canon_hdr(Hdr *out, const void *data, 15 | elfclass ei_class, elfdata ei_data) 16 | { 17 | switch (ei_class) { 18 | case elfclass::_32: 19 | switch (ei_data) { 20 | case elfdata::lsb: 21 | out->from(*(Hdr*)data); 22 | break; 23 | case elfdata::msb: 24 | out->from(*(Hdr*)data); 25 | break; 26 | } 27 | break; 28 | case elfclass::_64: 29 | switch (ei_data) { 30 | case elfdata::lsb: 31 | out->from(*(Hdr*)data); 32 | break; 33 | case elfdata::msb: 34 | out->from(*(Hdr*)data); 35 | return; 36 | } 37 | } 38 | } 39 | 40 | ////////////////////////////////////////////////////////////////// 41 | // class elf 42 | // 43 | 44 | struct elf::impl 45 | { 46 | impl(const shared_ptr &l) 47 | : l(l) { } 48 | 49 | const shared_ptr l; 50 | Ehdr<> hdr; 51 | vector
sections; 52 | vector segments; 53 | 54 | section invalid_section; 55 | segment invalid_segment; 56 | }; 57 | 58 | elf::elf(const std::shared_ptr &l) 59 | : m(make_shared(l)) 60 | { 61 | // Read the first six bytes to check the magic number, ELF 62 | // class, and byte order. 63 | struct core_hdr 64 | { 65 | char ei_magic[4]; 66 | elfclass ei_class; 67 | elfdata ei_data; 68 | unsigned char ei_version; 69 | } *core_hdr = (struct core_hdr*)l->load(0, sizeof *core_hdr); 70 | 71 | // Check basic header 72 | if (strncmp(core_hdr->ei_magic, "\x7f" "ELF", 4) != 0) 73 | throw format_error("bad ELF magic number"); 74 | if (core_hdr->ei_version != 1) 75 | throw format_error("unknown ELF version"); 76 | if (core_hdr->ei_class != elfclass::_32 && 77 | core_hdr->ei_class != elfclass::_64) 78 | throw format_error("bad ELF class"); 79 | if (core_hdr->ei_data != elfdata::lsb && 80 | core_hdr->ei_data != elfdata::msb) 81 | throw format_error("bad ELF data order"); 82 | 83 | // Read in the real header and canonicalize it 84 | size_t hdr_size = (core_hdr->ei_class == elfclass::_32 ? 85 | sizeof(Ehdr) : sizeof(Ehdr)); 86 | const void *hdr = l->load(0, hdr_size); 87 | canon_hdr(&m->hdr, hdr, core_hdr->ei_class, core_hdr->ei_data); 88 | 89 | // More checks 90 | if (m->hdr.version != 1) 91 | throw format_error("bad section ELF version"); 92 | if (m->hdr.shnum && m->hdr.shstrndx >= m->hdr.shnum) 93 | throw format_error("bad section name string table index"); 94 | 95 | // Load segments 96 | const void *seg_data = l->load(m->hdr.phoff, 97 | m->hdr.phentsize * m->hdr.phnum); 98 | for (unsigned i = 0; i < m->hdr.phnum; i++) { 99 | const void *seg = ((const char*)seg_data) + i * m->hdr.phentsize; 100 | m->segments.push_back(segment(*this, seg)); 101 | } 102 | 103 | // Load sections 104 | const void *sec_data = l->load(m->hdr.shoff, 105 | m->hdr.shentsize * m->hdr.shnum); 106 | for (unsigned i = 0; i < m->hdr.shnum; i++) { 107 | const void *sec = ((const char*)sec_data) + i * m->hdr.shentsize; 108 | // XXX Circular reference. Maybe this should be 109 | // constructed on the fly? Canonicalizing the header 110 | // isn't super-cheap. 111 | m->sections.push_back(section(*this, sec)); 112 | } 113 | } 114 | 115 | const Ehdr<> & 116 | elf::get_hdr() const 117 | { 118 | return m->hdr; 119 | } 120 | 121 | shared_ptr 122 | elf::get_loader() const 123 | { 124 | return m->l; 125 | } 126 | 127 | const std::vector
& 128 | elf::sections() const 129 | { 130 | return m->sections; 131 | } 132 | 133 | const std::vector & 134 | elf::segments() const 135 | { 136 | return m->segments; 137 | } 138 | 139 | const section & 140 | elf::get_section(const std::string &name) const 141 | { 142 | for (auto &sec : sections()) 143 | if (name == sec.get_name(nullptr)) 144 | return sec; 145 | return m->invalid_section; 146 | } 147 | 148 | const section & 149 | elf::get_section(unsigned index) const 150 | { 151 | if (index >= sections().size()) 152 | return m->invalid_section; 153 | return sections().at(index); 154 | } 155 | 156 | const segment& 157 | elf::get_segment(unsigned index) const 158 | { 159 | if (index >= segments().size()) 160 | return m->invalid_segment; 161 | return segments().at(index); 162 | } 163 | 164 | ////////////////////////////////////////////////////////////////// 165 | // class segment 166 | // 167 | 168 | struct segment::impl { 169 | impl(const elf &f) 170 | : f(f) { } 171 | 172 | const elf f; 173 | Phdr<> hdr; 174 | // const char *name; 175 | // size_t name_len; 176 | const void *data; 177 | }; 178 | 179 | segment::segment(const elf &f, const void *hdr) 180 | : m(make_shared(f)) { 181 | canon_hdr(&m->hdr, hdr, f.get_hdr().ei_class, f.get_hdr().ei_data); 182 | } 183 | 184 | const Phdr<> & 185 | segment::get_hdr() const { 186 | return m->hdr; 187 | } 188 | 189 | const void * 190 | segment::data() const { 191 | if (!m->data) 192 | m->data = m->f.get_loader()->load(m->hdr.offset, 193 | m->hdr.filesz); 194 | return m->data; 195 | } 196 | 197 | size_t 198 | segment::file_size() const { 199 | return m->hdr.filesz; 200 | } 201 | 202 | size_t 203 | segment::mem_size() const { 204 | return m->hdr.memsz; 205 | } 206 | 207 | ////////////////////////////////////////////////////////////////// 208 | // class section 209 | // 210 | 211 | std::string 212 | enums::to_string(shn v) 213 | { 214 | if (v == shn::undef) 215 | return "undef"; 216 | if (v == shn::abs) 217 | return "abs"; 218 | if (v == shn::common) 219 | return "common"; 220 | return std::to_string(v); 221 | } 222 | 223 | struct section::impl 224 | { 225 | impl(const elf &f) 226 | : f(f), name(nullptr), data(nullptr) { } 227 | 228 | const elf f; 229 | Shdr<> hdr; 230 | const char *name; 231 | size_t name_len; 232 | const void *data; 233 | }; 234 | 235 | section::section(const elf &f, const void *hdr) 236 | : m(make_shared(f)) 237 | { 238 | canon_hdr(&m->hdr, hdr, f.get_hdr().ei_class, f.get_hdr().ei_data); 239 | } 240 | 241 | const Shdr<> & 242 | section::get_hdr() const 243 | { 244 | return m->hdr; 245 | } 246 | 247 | const char * 248 | section::get_name(size_t *len_out) const 249 | { 250 | // XXX Should the section name strtab be cached? 251 | if (!m->name) 252 | m->name = m->f.get_section(m->f.get_hdr().shstrndx) 253 | .as_strtab().get(m->hdr.name, &m->name_len); 254 | if (len_out) 255 | *len_out = m->name_len; 256 | return m->name; 257 | } 258 | 259 | string 260 | section::get_name() const 261 | { 262 | return get_name(nullptr); 263 | } 264 | 265 | const void * 266 | section::data() const 267 | { 268 | if (m->hdr.type == sht::nobits) 269 | return nullptr; 270 | if (!m->data) 271 | m->data = m->f.get_loader()->load(m->hdr.offset, m->hdr.size); 272 | return m->data; 273 | } 274 | 275 | size_t 276 | section::size() const 277 | { 278 | return m->hdr.size; 279 | } 280 | 281 | strtab 282 | section::as_strtab() const 283 | { 284 | if (m->hdr.type != sht::strtab) 285 | throw section_type_mismatch("cannot use section as strtab"); 286 | return strtab(m->f, data(), size()); 287 | } 288 | 289 | symtab 290 | section::as_symtab() const 291 | { 292 | if (m->hdr.type != sht::symtab && m->hdr.type != sht::dynsym) 293 | throw section_type_mismatch("cannot use section as symtab"); 294 | return symtab(m->f, data(), size(), 295 | m->f.get_section(get_hdr().link).as_strtab()); 296 | } 297 | 298 | ////////////////////////////////////////////////////////////////// 299 | // class strtab 300 | // 301 | 302 | struct strtab::impl 303 | { 304 | impl(const elf &f, const char *data, const char *end) 305 | : f(f), data(data), end(end) { } 306 | 307 | const elf f; 308 | const char *data, *end; 309 | }; 310 | 311 | strtab::strtab(elf f, const void *data, size_t size) 312 | : m(make_shared(f, (const char*)data, (const char *)data + size)) 313 | { 314 | } 315 | 316 | const char * 317 | strtab::get(Elf64::Off offset, size_t *len_out) const 318 | { 319 | const char *start = m->data + offset; 320 | 321 | if (start >= m->end) 322 | throw range_error("string offset " + std::to_string(offset) + " exceeds section size"); 323 | 324 | // Find the null terminator 325 | const char *p = start; 326 | while (p < m->end && *p) 327 | p++; 328 | if (p == m->end) 329 | throw format_error("unterminated string"); 330 | 331 | if (len_out) 332 | *len_out = p - start; 333 | return start; 334 | } 335 | 336 | std::string 337 | strtab::get(Elf64::Off offset) const 338 | { 339 | return get(offset, nullptr); 340 | } 341 | 342 | ////////////////////////////////////////////////////////////////// 343 | // class sym 344 | // 345 | 346 | sym::sym(elf f, const void *data, strtab strs) 347 | : strs(strs) 348 | { 349 | canon_hdr(&this->data, data, f.get_hdr().ei_class, f.get_hdr().ei_data); 350 | } 351 | 352 | const char * 353 | sym::get_name(size_t *len_out) const 354 | { 355 | return strs.get(get_data().name, len_out); 356 | } 357 | 358 | std::string 359 | sym::get_name() const 360 | { 361 | return strs.get(get_data().name); 362 | } 363 | 364 | ////////////////////////////////////////////////////////////////// 365 | // class symtab 366 | // 367 | 368 | struct symtab::impl 369 | { 370 | impl(const elf &f, const char *data, const char *end, strtab strs) 371 | : f(f), data(data), end(end), strs(strs) { } 372 | 373 | const elf f; 374 | const char *data, *end; 375 | const strtab strs; 376 | }; 377 | 378 | symtab::symtab(elf f, const void *data, size_t size, strtab strs) 379 | : m(make_shared(f, (const char*)data, (const char *)data + size, 380 | strs)) 381 | { 382 | } 383 | 384 | symtab::iterator::iterator(const symtab &tab, const char *pos) 385 | : f(tab.m->f), strs(tab.m->strs), pos(pos) 386 | { 387 | if (f.get_hdr().ei_class == elfclass::_32) 388 | stride = sizeof(Sym); 389 | else 390 | stride = sizeof(Sym); 391 | } 392 | 393 | symtab::iterator 394 | symtab::begin() const 395 | { 396 | return iterator(*this, m->data); 397 | } 398 | 399 | symtab::iterator 400 | symtab::end() const 401 | { 402 | return iterator(*this, m->end); 403 | } 404 | 405 | ELFPP_END_NAMESPACE 406 | -------------------------------------------------------------------------------- /dwarf/dwarf.cc: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013 Austin T. Clements. All rights reserved. 2 | // Use of this source code is governed by an MIT license 3 | // that can be found in the LICENSE file. 4 | 5 | #include "internal.hh" 6 | 7 | using namespace std; 8 | 9 | DWARFPP_BEGIN_NAMESPACE 10 | 11 | ////////////////////////////////////////////////////////////////// 12 | // class dwarf 13 | // 14 | 15 | struct dwarf::impl 16 | { 17 | impl(const std::shared_ptr &l) 18 | : l(l), have_type_units(false) { } 19 | 20 | std::shared_ptr l; 21 | 22 | std::shared_ptr
sec_info; 23 | std::shared_ptr
sec_abbrev; 24 | 25 | std::vector compilation_units; 26 | 27 | std::unordered_map type_units; 28 | bool have_type_units; 29 | 30 | std::map > sections; 31 | }; 32 | 33 | dwarf::dwarf(const std::shared_ptr &l) 34 | : m(make_shared(l)) 35 | { 36 | const void *data; 37 | size_t size; 38 | 39 | // Get required sections 40 | data = l->load(section_type::info, &size); 41 | if (!data) 42 | throw format_error("required .debug_info section missing"); 43 | m->sec_info = make_shared
(section_type::info, data, size, byte_order::lsb); 44 | 45 | // Sniff the endianness from the version field of the first 46 | // CU. This is always a small but non-zero integer. 47 | cursor endcur(m->sec_info); 48 | // Skip length. 49 | section_length length = endcur.fixed(); 50 | if (length == 0xffffffff) 51 | endcur.fixed(); 52 | // Get version in both little and big endian. 53 | uhalf version = endcur.fixed(); 54 | uhalf versionbe = (version >> 8) | ((version & 0xFF) << 8); 55 | if (versionbe < version) { 56 | m->sec_info = make_shared
(section_type::info, data, size, byte_order::msb); 57 | } 58 | 59 | data = l->load(section_type::abbrev, &size); 60 | if (!data) 61 | throw format_error("required .debug_abbrev section missing"); 62 | m->sec_abbrev = make_shared
(section_type::abbrev, data, size, m->sec_info->ord); 63 | 64 | // Get compilation units. Everything derives from these, so 65 | // there's no point in doing it lazily. 66 | cursor infocur(m->sec_info); 67 | while (!infocur.end()) { 68 | // XXX Circular reference. Given that we now require 69 | // the dwarf object to stick around for DIEs, maybe we 70 | // might as well require that for units, too. 71 | m->compilation_units.emplace_back( 72 | *this, infocur.get_section_offset()); 73 | infocur.subsection(); 74 | } 75 | } 76 | 77 | dwarf::~dwarf() 78 | { 79 | } 80 | 81 | const std::vector & 82 | dwarf::compilation_units() const 83 | { 84 | static std::vector empty; 85 | if (!m) 86 | return empty; 87 | return m->compilation_units; 88 | } 89 | 90 | const type_unit & 91 | dwarf::get_type_unit(uint64_t type_signature) const 92 | { 93 | if (!m->have_type_units) { 94 | cursor tucur(get_section(section_type::types)); 95 | while (!tucur.end()) { 96 | // XXX Circular reference 97 | type_unit tu(*this, tucur.get_section_offset()); 98 | m->type_units[tu.get_type_signature()] = tu; 99 | tucur.subsection(); 100 | } 101 | m->have_type_units = true; 102 | } 103 | if (!m->type_units.count(type_signature)) 104 | throw out_of_range("type signature 0x" + to_hex(type_signature)); 105 | return m->type_units[type_signature]; 106 | } 107 | 108 | std::shared_ptr
109 | dwarf::get_section(section_type type) const 110 | { 111 | if (type == section_type::info) 112 | return m->sec_info; 113 | if (type == section_type::abbrev) 114 | return m->sec_abbrev; 115 | 116 | auto it = m->sections.find(type); 117 | if (it != m->sections.end()) 118 | return it->second; 119 | 120 | size_t size; 121 | const void *data = m->l->load(type, &size); 122 | if (!data) 123 | throw format_error(std::string(elf::section_type_to_name(type)) 124 | + " section missing"); 125 | m->sections[type] = std::make_shared
(section_type::str, data, size, m->sec_info->ord); 126 | return m->sections[type]; 127 | } 128 | 129 | ////////////////////////////////////////////////////////////////// 130 | // class unit 131 | // 132 | 133 | /** 134 | * Implementation of a unit. 135 | */ 136 | struct unit::impl 137 | { 138 | const dwarf file; 139 | const section_offset offset; 140 | const std::shared_ptr
subsec; 141 | const section_offset debug_abbrev_offset; 142 | const section_offset root_offset; 143 | 144 | // Type unit-only values 145 | const uint64_t type_signature; 146 | const section_offset type_offset; 147 | 148 | // Lazily constructed root and type DIEs 149 | die root, type; 150 | 151 | // Lazily constructed line table 152 | line_table lt; 153 | 154 | // Map from abbrev code to abbrev. If the map is dense, it 155 | // will be stored in the vector; otherwise it will be stored 156 | // in the map. 157 | bool have_abbrevs; 158 | std::vector abbrevs_vec; 159 | std::unordered_map abbrevs_map; 160 | 161 | impl(const dwarf &file, section_offset offset, 162 | const std::shared_ptr
&subsec, 163 | section_offset debug_abbrev_offset, section_offset root_offset, 164 | uint64_t type_signature = 0, section_offset type_offset = 0) 165 | : file(file), offset(offset), subsec(subsec), 166 | debug_abbrev_offset(debug_abbrev_offset), 167 | root_offset(root_offset), type_signature(type_signature), 168 | type_offset(type_offset), have_abbrevs(false) { } 169 | 170 | void force_abbrevs(); 171 | }; 172 | 173 | unit::~unit() 174 | { 175 | } 176 | 177 | const dwarf & 178 | unit::get_dwarf() const 179 | { 180 | return m->file; 181 | } 182 | 183 | section_offset 184 | unit::get_section_offset() const 185 | { 186 | return m->offset; 187 | } 188 | 189 | const die& 190 | unit::root() const 191 | { 192 | if (!m->root.valid()) { 193 | m->force_abbrevs(); 194 | m->root = die(this); 195 | m->root.read(m->root_offset); 196 | } 197 | return m->root; 198 | } 199 | 200 | const std::shared_ptr
& 201 | unit::data() const 202 | { 203 | return m->subsec; 204 | } 205 | 206 | const abbrev_entry & 207 | unit::get_abbrev(abbrev_code acode) const 208 | { 209 | if (!m->have_abbrevs) 210 | m->force_abbrevs(); 211 | 212 | if (!m->abbrevs_vec.empty()) { 213 | if (acode >= m->abbrevs_vec.size()) 214 | goto unknown; 215 | const abbrev_entry &entry = m->abbrevs_vec[acode]; 216 | if (entry.code == 0) 217 | goto unknown; 218 | return entry; 219 | } else { 220 | auto it = m->abbrevs_map.find(acode); 221 | if (it == m->abbrevs_map.end()) 222 | goto unknown; 223 | return it->second; 224 | } 225 | 226 | unknown: 227 | throw format_error("unknown abbrev code 0x" + to_hex(acode)); 228 | } 229 | 230 | void 231 | unit::impl::force_abbrevs() 232 | { 233 | // XXX Compilation units can share abbrevs. Parse each table 234 | // at most once. 235 | if (have_abbrevs) 236 | return; 237 | 238 | // Section 7.5.3 239 | cursor c(file.get_section(section_type::abbrev), 240 | debug_abbrev_offset); 241 | abbrev_entry entry; 242 | abbrev_code highest = 0; 243 | while (entry.read(&c)) { 244 | abbrevs_map[entry.code] = entry; 245 | if (entry.code > highest) 246 | highest = entry.code; 247 | } 248 | 249 | // Typically, abbrev codes are assigned linearly, so it's more 250 | // space efficient and time efficient to store the table in a 251 | // vector. Convert to a vector if it's dense enough, by some 252 | // rough estimate of "enough". 253 | if (highest * 10 < abbrevs_map.size() * 15) { 254 | // Move the map into the vector 255 | abbrevs_vec.resize(highest + 1); 256 | for (auto &entry : abbrevs_map) 257 | abbrevs_vec[entry.first] = move(entry.second); 258 | abbrevs_map.clear(); 259 | } 260 | 261 | have_abbrevs = true; 262 | } 263 | 264 | ////////////////////////////////////////////////////////////////// 265 | // class compilation_unit 266 | // 267 | 268 | compilation_unit::compilation_unit(const dwarf &file, section_offset offset) 269 | { 270 | // Read the CU header (DWARF4 section 7.5.1.1) 271 | cursor cur(file.get_section(section_type::info), offset); 272 | std::shared_ptr
subsec = cur.subsection(); 273 | cursor sub(subsec); 274 | sub.skip_initial_length(); 275 | uhalf version = sub.fixed(); 276 | if (version < 2 || version > 4) 277 | throw format_error("unknown compilation unit version " + std::to_string(version)); 278 | // .debug_abbrev-relative offset of this unit's abbrevs 279 | section_offset debug_abbrev_offset = sub.offset(); 280 | ubyte address_size = sub.fixed(); 281 | subsec->addr_size = address_size; 282 | 283 | m = make_shared(file, offset, subsec, debug_abbrev_offset, 284 | sub.get_section_offset()); 285 | } 286 | 287 | const line_table & 288 | compilation_unit::get_line_table() const 289 | { 290 | if (!m->lt.valid()) { 291 | const die &d = root(); 292 | if (!d.has(DW_AT::stmt_list) || !d.has(DW_AT::name)) 293 | goto done; 294 | 295 | shared_ptr
sec; 296 | try { 297 | sec = m->file.get_section(section_type::line); 298 | } catch (format_error &e) { 299 | goto done; 300 | } 301 | 302 | auto comp_dir = d.has(DW_AT::comp_dir) ? at_comp_dir(d) : ""; 303 | 304 | m->lt = line_table(sec, d[DW_AT::stmt_list].as_sec_offset(), 305 | m->subsec->addr_size, comp_dir, 306 | at_name(d)); 307 | } 308 | done: 309 | return m->lt; 310 | } 311 | 312 | ////////////////////////////////////////////////////////////////// 313 | // class type_unit 314 | // 315 | 316 | type_unit::type_unit(const dwarf &file, section_offset offset) 317 | { 318 | // Read the type unit header (DWARF4 section 7.5.1.2) 319 | cursor cur(file.get_section(section_type::types), offset); 320 | std::shared_ptr
subsec = cur.subsection(); 321 | cursor sub(subsec); 322 | sub.skip_initial_length(); 323 | uhalf version = sub.fixed(); 324 | if (version != 4) 325 | throw format_error("unknown type unit version " + std::to_string(version)); 326 | // .debug_abbrev-relative offset of this unit's abbrevs 327 | section_offset debug_abbrev_offset = sub.offset(); 328 | ubyte address_size = sub.fixed(); 329 | subsec->addr_size = address_size; 330 | uint64_t type_signature = sub.fixed(); 331 | section_offset type_offset = sub.offset(); 332 | 333 | m = make_shared(file, offset, subsec, debug_abbrev_offset, 334 | sub.get_section_offset(), type_signature, 335 | type_offset); 336 | } 337 | 338 | uint64_t 339 | type_unit::get_type_signature() const 340 | { 341 | return m->type_signature; 342 | } 343 | 344 | const die & 345 | type_unit::type() const 346 | { 347 | if (!m->type.valid()) { 348 | m->force_abbrevs(); 349 | m->type = die(this); 350 | m->type.read(m->type_offset); 351 | } 352 | return m->type; 353 | } 354 | 355 | DWARFPP_END_NAMESPACE 356 | -------------------------------------------------------------------------------- /elf/elf++.hh: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013 Austin T. Clements. All rights reserved. 2 | // Use of this source code is governed by an MIT license 3 | // that can be found in the LICENSE file. 4 | 5 | #ifndef _ELFPP_HH_ 6 | #define _ELFPP_HH_ 7 | 8 | #include "common.hh" 9 | #include "data.hh" 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | ELFPP_BEGIN_NAMESPACE 17 | 18 | class elf; 19 | class loader; 20 | class section; 21 | class strtab; 22 | class symtab; 23 | class segment; 24 | // XXX Audit for binary compatibility 25 | 26 | // XXX Segments, other section types 27 | 28 | /** 29 | * An exception indicating malformed ELF data. 30 | */ 31 | class format_error : public std::runtime_error 32 | { 33 | public: 34 | explicit format_error(const std::string &what_arg) 35 | : std::runtime_error(what_arg) { } 36 | explicit format_error(const char *what_arg) 37 | : std::runtime_error(what_arg) { } 38 | }; 39 | 40 | /** 41 | * An ELF file. 42 | * 43 | * This class is internally reference counted and efficiently 44 | * copyable. 45 | * 46 | * Raw pointers to ELF data returned by any method of this object or 47 | * any object derived from this object point directly into loaded 48 | * section data. Hence, callers must ensure that the loader passed to 49 | * this file remains live as long as any such pointer is in use. 50 | * Keeping any object that can return such a pointer live is 51 | * sufficieint to keep the loader live. 52 | */ 53 | class elf 54 | { 55 | public: 56 | /** 57 | * Construct an ELF file that is backed by data read from the 58 | * given loader. 59 | */ 60 | explicit elf(const std::shared_ptr &l); 61 | 62 | /** 63 | * Construct an ELF file that is initially not valid. Calling 64 | * methods other than operator= and valid on this results in 65 | * undefined behavior. 66 | */ 67 | elf() = default; 68 | elf(const elf &o) = default; 69 | elf(elf &&o) = default; 70 | 71 | elf& operator=(const elf &o) = default; 72 | 73 | bool valid() const 74 | { 75 | return !!m; 76 | } 77 | 78 | /** 79 | * Return the ELF file header in canonical form (ELF64 in 80 | * native byte order). 81 | */ 82 | const Ehdr<> &get_hdr() const; 83 | 84 | /** 85 | * Return the loader used by this file. 86 | */ 87 | std::shared_ptr get_loader() const; 88 | 89 | /** 90 | * Return the segments in this file. 91 | */ 92 | const std::vector &segments() const; 93 | 94 | /** 95 | * Return the segment at the given index. If no such segment 96 | * is found, return an invalid segment. 97 | */ 98 | const segment &get_segment(unsigned index) const; 99 | 100 | /** 101 | * Return the sections in this file. 102 | */ 103 | const std::vector
§ions() const; 104 | 105 | /** 106 | * Return the section with the specified name. If no such 107 | * section is found, return an invalid section. 108 | */ 109 | const section &get_section(const std::string &name) const; 110 | 111 | /** 112 | * Return the section at the given index. If no such section 113 | * is found, return an invalid section. 114 | */ 115 | const section &get_section(unsigned index) const; 116 | 117 | private: 118 | struct impl; 119 | std::shared_ptr m; 120 | }; 121 | 122 | /** 123 | * An interface for loading sections of an ELF file. 124 | */ 125 | class loader 126 | { 127 | public: 128 | virtual ~loader() { } 129 | 130 | /** 131 | * Load the requested file section into memory and return a 132 | * pointer to the beginning of it. This memory must remain 133 | * valid and unchanged until the loader is destroyed. If the 134 | * loader cannot satisfy the full request for any reason 135 | * (including a premature EOF), it must throw an exception. 136 | */ 137 | virtual const void *load(off_t offset, size_t size) = 0; 138 | }; 139 | 140 | /** 141 | * An mmap-based loader that maps requested sections on demand. This 142 | * will close fd when done, so the caller should dup the file 143 | * descriptor if it intends to continue using it. 144 | */ 145 | std::shared_ptr create_mmap_loader(int fd); 146 | 147 | /** 148 | * An exception indicating that a section is not of the requested type. 149 | */ 150 | class section_type_mismatch : public std::logic_error 151 | { 152 | public: 153 | explicit section_type_mismatch(const std::string &what_arg) 154 | : std::logic_error(what_arg) { } 155 | explicit section_type_mismatch(const char *what_arg) 156 | : std::logic_error(what_arg) { } 157 | }; 158 | 159 | /** 160 | * An ELF segment. 161 | * 162 | * This class is internally reference counted and efficiently 163 | * copyable. 164 | */ 165 | class segment 166 | { 167 | public: 168 | /** 169 | * Construct a segment that is initially not valid. Calling 170 | * methods other than operator= and valid on this results in 171 | * undefined behavior. 172 | */ 173 | segment() { } 174 | 175 | segment(const elf &f, const void *hdr); 176 | segment(const segment &o) = default; 177 | segment(segment &&o) = default; 178 | 179 | /** 180 | * Return true if this segment is valid and corresponds to a 181 | * segment in the ELF file. 182 | */ 183 | bool valid() const 184 | { 185 | return !!m; 186 | } 187 | 188 | /** 189 | * Return the ELF section header in canonical form (ELF64 in 190 | * native byte order). 191 | */ 192 | const Phdr<> &get_hdr() const; 193 | 194 | /** 195 | * Return this segment's data. The returned buffer will 196 | * be file_size() bytes long. 197 | */ 198 | const void *data() const; 199 | 200 | /** 201 | * Return the on disk size of this segment in bytes. 202 | */ 203 | size_t file_size() const; 204 | 205 | /** 206 | * Return the in-memory size of this segment in bytes. 207 | * Bytes between file_size() and mem_size() are implicity zeroes. 208 | */ 209 | size_t mem_size() const; 210 | 211 | private: 212 | struct impl; 213 | std::shared_ptr m; 214 | }; 215 | 216 | /** 217 | * An ELF section. 218 | * 219 | * This class is internally reference counted and efficiently 220 | * copyable. 221 | */ 222 | class section 223 | { 224 | public: 225 | /** 226 | * Construct a section that is initially not valid. Calling 227 | * methods other than operator= and valid on this results in 228 | * undefined behavior. 229 | */ 230 | section() { } 231 | 232 | section(const elf &f, const void *hdr); 233 | section(const section &o) = default; 234 | section(section &&o) = default; 235 | 236 | /** 237 | * Return true if this section is valid and corresponds to a 238 | * section in the ELF file. 239 | */ 240 | bool valid() const 241 | { 242 | return !!m; 243 | } 244 | 245 | /** 246 | * Return the ELF section header in canonical form (ELF64 in 247 | * native byte order). 248 | */ 249 | const Shdr<> &get_hdr() const; 250 | 251 | /** 252 | * Return this section's name. 253 | */ 254 | const char *get_name(size_t *len_out) const; 255 | /** 256 | * Return this section's name. The returned string copies its 257 | * data, so loader liveness requirements don't apply. 258 | */ 259 | std::string get_name() const; 260 | 261 | /** 262 | * Return this section's data. If this is a NOBITS section, 263 | * return nullptr. 264 | */ 265 | const void *data() const; 266 | /** 267 | * Return the size of this section in bytes. 268 | */ 269 | size_t size() const; 270 | 271 | /** 272 | * Return this section as a strtab. Throws 273 | * section_type_mismatch if this section is not a string 274 | * table. 275 | */ 276 | strtab as_strtab() const; 277 | 278 | /** 279 | * Return this section as a symtab. Throws 280 | * section_type_mismatch if this section is not a symbol 281 | * table. 282 | */ 283 | symtab as_symtab() const; 284 | 285 | private: 286 | struct impl; 287 | std::shared_ptr m; 288 | }; 289 | 290 | /** 291 | * A string table. 292 | * 293 | * This class is internally reference counted and efficiently 294 | * copyable. 295 | */ 296 | class strtab 297 | { 298 | public: 299 | /** 300 | * Construct a strtab that is initially not valid. Calling 301 | * methods other than operator= and valid on this results in 302 | * undefined behavior. 303 | */ 304 | strtab() = default; 305 | strtab(elf f, const void *data, size_t size); 306 | 307 | bool valid() const 308 | { 309 | return !!m; 310 | } 311 | 312 | /** 313 | * Return the string at the given offset in this string table. 314 | * If the offset is out of bounds, throws std::range_error. 315 | * This is very efficient since the returned pointer points 316 | * directly into the loaded section, though this still 317 | * verifies that the returned string is NUL-terminated. 318 | */ 319 | const char *get(Elf64::Off offset, size_t *len_out) const; 320 | /** 321 | * Return the string at the given offset in this string table. 322 | */ 323 | std::string get(Elf64::Off offset) const; 324 | 325 | private: 326 | struct impl; 327 | std::shared_ptr m; 328 | }; 329 | 330 | /** 331 | * A symbol from a symbol table. 332 | */ 333 | class sym 334 | { 335 | const strtab strs; 336 | Sym<> data; 337 | 338 | public: 339 | sym(elf f, const void *data, strtab strs); 340 | 341 | /** 342 | * Return this symbol's raw data. 343 | */ 344 | const Sym<> &get_data() const 345 | { 346 | return data; 347 | } 348 | 349 | /** 350 | * Return this symbol's name. 351 | * 352 | * This returns a pointer into the string table and, as such, 353 | * is very efficient. If len_out is non-nullptr, *len_out 354 | * will be set the length of the returned string. 355 | */ 356 | const char *get_name(size_t *len_out) const; 357 | 358 | /** 359 | * Return this symbol's name as a string. 360 | */ 361 | std::string get_name() const; 362 | }; 363 | 364 | /** 365 | * A symbol table. 366 | * 367 | * This class is internally reference counted and efficiently 368 | * copyable. 369 | */ 370 | class symtab 371 | { 372 | public: 373 | /** 374 | * Construct a symtab that is initially not valid. Calling 375 | * methods other than operator= and valid on this results in 376 | * undefined behavior. 377 | */ 378 | symtab() = default; 379 | symtab(elf f, const void *data, size_t size, strtab strs); 380 | 381 | bool valid() const 382 | { 383 | return !!m; 384 | } 385 | 386 | class iterator 387 | { 388 | const elf f; 389 | const strtab strs; 390 | const char *pos; 391 | size_t stride; 392 | 393 | iterator(const symtab &tab, const char *pos); 394 | friend class symtab; 395 | 396 | public: 397 | sym operator*() const 398 | { 399 | return sym(f, pos, strs); 400 | } 401 | 402 | iterator& operator++() 403 | { 404 | return *this += 1; 405 | } 406 | 407 | iterator operator++(int) 408 | { 409 | iterator cur(*this); 410 | *this += 1; 411 | return cur; 412 | } 413 | 414 | iterator& operator+=(std::ptrdiff_t x) 415 | { 416 | pos += x * stride; 417 | return *this; 418 | } 419 | 420 | iterator& operator-=(std::ptrdiff_t x) 421 | { 422 | pos -= x * stride; 423 | return *this; 424 | } 425 | 426 | bool operator==(iterator &o) const 427 | { 428 | return pos == o.pos; 429 | } 430 | 431 | bool operator!=(iterator &o) const 432 | { 433 | return pos != o.pos; 434 | } 435 | }; 436 | 437 | /** 438 | * Return an iterator to the first symbol. 439 | */ 440 | iterator begin() const; 441 | 442 | /** 443 | * Return an iterator just past the last symbol. 444 | */ 445 | iterator end() const; 446 | 447 | private: 448 | struct impl; 449 | std::shared_ptr m; 450 | }; 451 | 452 | ELFPP_END_NAMESPACE 453 | 454 | #endif 455 | -------------------------------------------------------------------------------- /dwarf/line.cc: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013 Austin T. Clements. All rights reserved. 2 | // Use of this source code is governed by an MIT license 3 | // that can be found in the LICENSE file. 4 | 5 | #include "internal.hh" 6 | 7 | #include 8 | 9 | using namespace std; 10 | 11 | DWARFPP_BEGIN_NAMESPACE 12 | 13 | // The expected number of arguments for standard opcodes. This is 14 | // used to check the opcode_lengths header field for compatibility. 15 | static const int opcode_lengths[] = { 16 | 0, 17 | // DW_LNS::copy 18 | 0, 1, 1, 1, 1, 19 | // DW_LNS::negate_stmt 20 | 0, 0, 0, 1, 0, 21 | // DW_LNS::set_epilogue_begin 22 | 0, 1 23 | }; 24 | 25 | struct line_table::impl 26 | { 27 | shared_ptr
sec; 28 | 29 | // Header information 30 | section_offset program_offset; 31 | ubyte minimum_instruction_length; 32 | ubyte maximum_operations_per_instruction; 33 | bool default_is_stmt; 34 | sbyte line_base; 35 | ubyte line_range; 36 | ubyte opcode_base; 37 | vector standard_opcode_lengths; 38 | vector include_directories; 39 | vector file_names; 40 | 41 | // The offset in sec following the last read file name entry. 42 | // File name entries can appear both in the line table header 43 | // and in the line number program itself. Since we can 44 | // iterate over the line number program repeatedly, this keeps 45 | // track of how far we've gotten so we don't add the same 46 | // entry twice. 47 | section_offset last_file_name_end; 48 | // If an iterator has traversed the entire program, then we 49 | // know we've gathered all file names. 50 | bool file_names_complete; 51 | 52 | impl() : last_file_name_end(0), file_names_complete(false) {}; 53 | 54 | bool read_file_entry(cursor *cur, bool in_header); 55 | }; 56 | 57 | line_table::line_table(const shared_ptr
&sec, section_offset offset, 58 | unsigned cu_addr_size, const string &cu_comp_dir, 59 | const string &cu_name) 60 | : m(make_shared()) 61 | { 62 | // XXX DWARF2 and 3 give a weird specification for DW_AT_comp_dir 63 | 64 | string comp_dir, abs_path; 65 | if (cu_comp_dir.empty() || cu_comp_dir.back() == '/') 66 | comp_dir = cu_comp_dir; 67 | else 68 | comp_dir = cu_comp_dir + '/'; 69 | 70 | // Read the line table header (DWARF2 section 6.2.4, DWARF3 71 | // section 6.2.4, DWARF4 section 6.2.3) 72 | cursor cur(sec, offset); 73 | m->sec = cur.subsection(); 74 | cur = cursor(m->sec); 75 | cur.skip_initial_length(); 76 | m->sec->addr_size = cu_addr_size; 77 | 78 | // Basic header information 79 | uhalf version = cur.fixed(); 80 | if (version < 2 || version > 4) 81 | throw format_error("unknown line number table version " + 82 | std::to_string(version)); 83 | section_length header_length = cur.offset(); 84 | m->program_offset = cur.get_section_offset() + header_length; 85 | m->minimum_instruction_length = cur.fixed(); 86 | m->maximum_operations_per_instruction = 1; 87 | if (version == 4) 88 | m->maximum_operations_per_instruction = cur.fixed(); 89 | if (m->maximum_operations_per_instruction == 0) 90 | throw format_error("maximum_operations_per_instruction cannot" 91 | " be 0 in line number table"); 92 | m->default_is_stmt = cur.fixed(); 93 | m->line_base = cur.fixed(); 94 | m->line_range = cur.fixed(); 95 | if (m->line_range == 0) 96 | throw format_error("line_range cannot be 0 in line number table"); 97 | m->opcode_base = cur.fixed(); 98 | 99 | static_assert(sizeof(opcode_lengths) / sizeof(opcode_lengths[0]) == 13, 100 | "opcode_lengths table has wrong length"); 101 | 102 | // Opcode length table 103 | m->standard_opcode_lengths.resize(m->opcode_base); 104 | m->standard_opcode_lengths[0] = 0; 105 | for (unsigned i = 1; i < m->opcode_base; i++) { 106 | ubyte length = cur.fixed(); 107 | if (length != opcode_lengths[i]) 108 | // The spec never says what to do if the 109 | // opcode length of a standard opcode doesn't 110 | // match the header. Do the safe thing. 111 | throw format_error( 112 | "expected " + 113 | std::to_string(opcode_lengths[i]) + 114 | " arguments for line number opcode " + 115 | std::to_string(i) + ", got " + 116 | std::to_string(length)); 117 | m->standard_opcode_lengths[i] = length; 118 | } 119 | 120 | // Include directories list 121 | string incdir; 122 | // Include directory 0 is implicitly the compilation unit 123 | // current directory 124 | m->include_directories.push_back(comp_dir); 125 | while (true) { 126 | cur.string(incdir); 127 | if (incdir.empty()) 128 | break; 129 | if (incdir.back() != '/') 130 | incdir += '/'; 131 | if (incdir[0] == '/') 132 | m->include_directories.push_back(move(incdir)); 133 | else 134 | m->include_directories.push_back(comp_dir + incdir); 135 | } 136 | 137 | // File name list 138 | string file_name; 139 | // File name 0 is implicitly the compilation unit file name. 140 | // cu_name can be relative to comp_dir or absolute. 141 | if (!cu_name.empty() && cu_name[0] == '/') 142 | m->file_names.emplace_back(cu_name); 143 | else 144 | m->file_names.emplace_back(comp_dir + cu_name); 145 | while (m->read_file_entry(&cur, true)); 146 | } 147 | 148 | line_table::iterator 149 | line_table::begin() const 150 | { 151 | if (!valid()) 152 | return iterator(nullptr, 0); 153 | return iterator(this, m->program_offset); 154 | } 155 | 156 | line_table::iterator 157 | line_table::end() const 158 | { 159 | if (!valid()) 160 | return iterator(nullptr, 0); 161 | return iterator(this, m->sec->size()); 162 | } 163 | 164 | line_table::iterator 165 | line_table::find_address(taddr addr) const 166 | { 167 | iterator prev = begin(), e = end(); 168 | if (prev == e) 169 | return prev; 170 | 171 | iterator it = prev; 172 | for (++it; it != e; prev = it++) { 173 | if (prev->address <= addr && it->address > addr && 174 | !prev->end_sequence) 175 | return prev; 176 | } 177 | prev = e; 178 | return prev; 179 | } 180 | 181 | const line_table::file * 182 | line_table::get_file(unsigned index) const 183 | { 184 | if (index >= m->file_names.size()) { 185 | // It could be declared in the line table program. 186 | // This is unlikely, so we don't have to be 187 | // super-efficient about this. Just force our way 188 | // through the whole line table program. 189 | if (!m->file_names_complete) { 190 | for (auto &ent : *this) 191 | (void)ent; 192 | } 193 | if (index >= m->file_names.size()) 194 | throw out_of_range 195 | ("file name index " + std::to_string(index) + 196 | " exceeds file table size of " + 197 | std::to_string(m->file_names.size())); 198 | } 199 | return &m->file_names[index]; 200 | } 201 | 202 | bool 203 | line_table::impl::read_file_entry(cursor *cur, bool in_header) 204 | { 205 | assert(cur->sec == sec); 206 | 207 | string file_name; 208 | cur->string(file_name); 209 | if (in_header && file_name.empty()) 210 | return false; 211 | uint64_t dir_index = cur->uleb128(); 212 | uint64_t mtime = cur->uleb128(); 213 | uint64_t length = cur->uleb128(); 214 | 215 | // Have we already processed this file entry? 216 | if (cur->get_section_offset() <= last_file_name_end) 217 | return true; 218 | last_file_name_end = cur->get_section_offset(); 219 | 220 | if (file_name[0] == '/') 221 | file_names.emplace_back(move(file_name), mtime, length); 222 | else if (dir_index < include_directories.size()) 223 | file_names.emplace_back( 224 | include_directories[dir_index] + file_name, 225 | mtime, length); 226 | else 227 | throw format_error("file name directory index out of range: " + 228 | std::to_string(dir_index)); 229 | 230 | return true; 231 | } 232 | 233 | line_table::file::file(string path, uint64_t mtime, uint64_t length) 234 | : path(path), mtime(mtime), length(length) 235 | { 236 | } 237 | 238 | void 239 | line_table::entry::reset(bool is_stmt) 240 | { 241 | address = op_index = 0; 242 | file = nullptr; 243 | file_index = line = 1; 244 | column = 0; 245 | this->is_stmt = is_stmt; 246 | basic_block = end_sequence = prologue_end = epilogue_begin = false; 247 | isa = discriminator = 0; 248 | } 249 | 250 | string 251 | line_table::entry::get_description() const 252 | { 253 | string res = file->path; 254 | if (line) { 255 | res.append(":").append(std::to_string(line)); 256 | if (column) 257 | res.append(":").append(std::to_string(column)); 258 | } 259 | return res; 260 | } 261 | 262 | line_table::iterator::iterator(const line_table *table, section_offset pos) 263 | : table(table), pos(pos) 264 | { 265 | if (table) { 266 | regs.reset(table->m->default_is_stmt); 267 | ++(*this); 268 | } 269 | } 270 | 271 | line_table::iterator & 272 | line_table::iterator::operator++() 273 | { 274 | cursor cur(table->m->sec, pos); 275 | 276 | // Execute opcodes until we reach the end of the stream or an 277 | // opcode emits a line table row 278 | bool stepped = false, output = false; 279 | while (!cur.end() && !output) { 280 | output = step(&cur); 281 | stepped = true; 282 | } 283 | if (stepped && !output) 284 | throw format_error("unexpected end of line table"); 285 | if (stepped && cur.end()) { 286 | // Record that all file names must be known now 287 | table->m->file_names_complete = true; 288 | } 289 | if (output) { 290 | // Resolve file name of entry 291 | if (entry.file_index < table->m->file_names.size()) 292 | entry.file = &table->m->file_names[entry.file_index]; 293 | else 294 | throw format_error("bad file index " + 295 | std::to_string(entry.file_index) + 296 | " in line table"); 297 | } 298 | 299 | pos = cur.get_section_offset(); 300 | return *this; 301 | } 302 | 303 | bool 304 | line_table::iterator::step(cursor *cur) 305 | { 306 | struct line_table::impl *m = table->m.get(); 307 | 308 | // Read the opcode (DWARF4 section 6.2.3) 309 | ubyte opcode = cur->fixed(); 310 | if (opcode >= m->opcode_base) { 311 | // Special opcode (DWARF4 section 6.2.5.1) 312 | ubyte adjusted_opcode = opcode - m->opcode_base; 313 | unsigned op_advance = adjusted_opcode / m->line_range; 314 | signed line_inc = m->line_base + (signed)adjusted_opcode % m->line_range; 315 | 316 | regs.line += line_inc; 317 | regs.address += m->minimum_instruction_length * 318 | ((regs.op_index + op_advance) 319 | / m->maximum_operations_per_instruction); 320 | regs.op_index = (regs.op_index + op_advance) 321 | % m->maximum_operations_per_instruction; 322 | entry = regs; 323 | 324 | regs.basic_block = regs.prologue_end = 325 | regs.epilogue_begin = false; 326 | regs.discriminator = 0; 327 | 328 | return true; 329 | } else if (opcode != 0) { 330 | // Standard opcode (DWARF4 sections 6.2.3 and 6.2.5.2) 331 | // 332 | // According to the standard, any opcode between the 333 | // highest defined opcode for a given DWARF version 334 | // and opcode_base should be treated as a 335 | // vendor-specific opcode. However, the de facto 336 | // standard seems to be to process these as standard 337 | // opcodes even if they're from a later version of the 338 | // standard than the line table header claims. 339 | uint64_t uarg; 340 | #pragma GCC diagnostic push 341 | #pragma GCC diagnostic warning "-Wswitch-enum" 342 | switch ((DW_LNS)opcode) { 343 | case DW_LNS::copy: 344 | entry = regs; 345 | regs.basic_block = regs.prologue_end = 346 | regs.epilogue_begin = false; 347 | regs.discriminator = 0; 348 | break; 349 | case DW_LNS::advance_pc: 350 | // Opcode advance (as for special opcodes) 351 | uarg = cur->uleb128(); 352 | advance_pc: 353 | regs.address += m->minimum_instruction_length * 354 | ((regs.op_index + uarg) 355 | / m->maximum_operations_per_instruction); 356 | regs.op_index = (regs.op_index + uarg) 357 | % m->maximum_operations_per_instruction; 358 | break; 359 | case DW_LNS::advance_line: 360 | regs.line = (signed)regs.line + cur->sleb128(); 361 | break; 362 | case DW_LNS::set_file: 363 | regs.file_index = cur->uleb128(); 364 | break; 365 | case DW_LNS::set_column: 366 | regs.column = cur->uleb128(); 367 | break; 368 | case DW_LNS::negate_stmt: 369 | regs.is_stmt = !regs.is_stmt; 370 | break; 371 | case DW_LNS::set_basic_block: 372 | regs.basic_block = true; 373 | break; 374 | case DW_LNS::const_add_pc: 375 | uarg = (255 - m->opcode_base) / m->line_range; 376 | goto advance_pc; 377 | case DW_LNS::fixed_advance_pc: 378 | regs.address += cur->fixed(); 379 | regs.op_index = 0; 380 | break; 381 | case DW_LNS::set_prologue_end: 382 | regs.prologue_end = true; 383 | break; 384 | case DW_LNS::set_epilogue_begin: 385 | regs.epilogue_begin = true; 386 | break; 387 | case DW_LNS::set_isa: 388 | regs.isa = cur->uleb128(); 389 | break; 390 | default: 391 | // XXX Vendor extensions 392 | throw format_error("unknown line number opcode " + 393 | to_string((DW_LNS)opcode)); 394 | } 395 | return ((DW_LNS)opcode == DW_LNS::copy); 396 | } else { // opcode == 0 397 | // Extended opcode (DWARF4 sections 6.2.3 and 6.2.5.3) 398 | assert(opcode == 0); 399 | uint64_t length = cur->uleb128(); 400 | section_offset end = cur->get_section_offset() + length; 401 | opcode = cur->fixed(); 402 | switch ((DW_LNE)opcode) { 403 | case DW_LNE::end_sequence: 404 | regs.end_sequence = true; 405 | entry = regs; 406 | regs.reset(m->default_is_stmt); 407 | break; 408 | case DW_LNE::set_address: 409 | regs.address = cur->address(); 410 | regs.op_index = 0; 411 | break; 412 | case DW_LNE::define_file: 413 | m->read_file_entry(cur, false); 414 | break; 415 | case DW_LNE::set_discriminator: 416 | // XXX Only DWARF4 417 | regs.discriminator = cur->uleb128(); 418 | break; 419 | case DW_LNE::lo_user...DW_LNE::hi_user: 420 | // XXX Vendor extensions 421 | throw runtime_error("vendor line number opcode " + 422 | to_string((DW_LNE)opcode) + 423 | " not implemented"); 424 | default: 425 | // XXX Prior to DWARF4, any opcode number 426 | // could be a vendor extension 427 | throw format_error("unknown line number opcode " + 428 | to_string((DW_LNE)opcode)); 429 | } 430 | #pragma GCC diagnostic pop 431 | if (cur->get_section_offset() > end) 432 | throw format_error("extended line number opcode exceeded its size"); 433 | cur += end - cur->get_section_offset(); 434 | return ((DW_LNE)opcode == DW_LNE::end_sequence); 435 | } 436 | } 437 | 438 | DWARFPP_END_NAMESPACE 439 | -------------------------------------------------------------------------------- /dwarf/expr.cc: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013 Austin T. Clements. All rights reserved. 2 | // Use of this source code is governed by an MIT license 3 | // that can be found in the LICENSE file. 4 | 5 | #include "internal.hh" 6 | 7 | using namespace std; 8 | 9 | DWARFPP_BEGIN_NAMESPACE 10 | 11 | expr_context no_expr_context; 12 | 13 | expr::expr(const unit *cu, 14 | section_offset offset, section_length len) 15 | : cu(cu), offset(offset), len(len) 16 | { 17 | } 18 | 19 | expr_result 20 | expr::evaluate(expr_context *ctx) const 21 | { 22 | return evaluate(ctx, {}); 23 | } 24 | 25 | expr_result 26 | expr::evaluate(expr_context *ctx, taddr argument) const 27 | { 28 | return evaluate(ctx, {argument}); 29 | } 30 | 31 | expr_result 32 | expr::evaluate(expr_context *ctx, const std::initializer_list &arguments) const 33 | { 34 | // The stack machine's stack. The top of the stack is 35 | // stack.back(). 36 | // XXX This stack must be in target machine representation, 37 | // since I see both (DW_OP_breg0 (eax): -28; DW_OP_stack_value) 38 | // and (DW_OP_lit1; DW_OP_stack_value). 39 | small_vector stack; 40 | 41 | // Create the initial stack. arguments are in reverse order 42 | // (that is, element 0 is TOS), so reverse it. 43 | if (arguments.size() > 0) { 44 | stack.reserve(arguments.size()); 45 | for (const taddr* elt = arguments.end() - 1; 46 | elt >= arguments.begin(); elt--) 47 | stack.push_back(*elt); 48 | } 49 | 50 | // Create a subsection for just this expression so we can 51 | // easily detect the end (including premature end). 52 | auto cusec = cu->data(); 53 | shared_ptr
subsec 54 | (make_shared
(cusec->type, 55 | cusec->begin + offset, len, 56 | cusec->ord, cusec->fmt, 57 | cusec->addr_size)); 58 | cursor cur(subsec); 59 | 60 | // Prepare the expression result. Some location descriptions 61 | // create the result directly, rather than using the top of 62 | // stack. 63 | expr_result result; 64 | 65 | // 2.6.1.1.4 Empty location descriptions 66 | if (cur.end()) { 67 | result.location_type = expr_result::type::empty; 68 | result.value = 0; 69 | return result; 70 | } 71 | 72 | // Assume the result is an address for now and should be 73 | // grabbed from the top of stack at the end. 74 | result.location_type = expr_result::type::address; 75 | 76 | // Execute! 77 | while (!cur.end()) { 78 | #define CHECK() do { if (stack.empty()) goto underflow; } while (0) 79 | #define CHECKN(n) do { if (stack.size() < n) goto underflow; } while (0) 80 | union 81 | { 82 | uint64_t u; 83 | int64_t s; 84 | } tmp1, tmp2, tmp3; 85 | static_assert(sizeof(tmp1) == sizeof(taddr), "taddr is not 64 bits"); 86 | 87 | // Tell GCC to warn us about missing switch cases, 88 | // even though we have a default case. 89 | #pragma GCC diagnostic push 90 | #pragma GCC diagnostic warning "-Wswitch-enum" 91 | DW_OP op = (DW_OP)cur.fixed(); 92 | switch (op) { 93 | // 2.5.1.1 Literal encodings 94 | case DW_OP::lit0...DW_OP::lit31: 95 | stack.push_back((unsigned)op - (unsigned)DW_OP::lit0); 96 | break; 97 | case DW_OP::addr: 98 | stack.push_back(cur.address()); 99 | break; 100 | case DW_OP::const1u: 101 | stack.push_back(cur.fixed()); 102 | break; 103 | case DW_OP::const2u: 104 | stack.push_back(cur.fixed()); 105 | break; 106 | case DW_OP::const4u: 107 | stack.push_back(cur.fixed()); 108 | break; 109 | case DW_OP::const8u: 110 | stack.push_back(cur.fixed()); 111 | break; 112 | case DW_OP::const1s: 113 | stack.push_back(cur.fixed()); 114 | break; 115 | case DW_OP::const2s: 116 | stack.push_back(cur.fixed()); 117 | break; 118 | case DW_OP::const4s: 119 | stack.push_back(cur.fixed()); 120 | break; 121 | case DW_OP::const8s: 122 | stack.push_back(cur.fixed()); 123 | break; 124 | case DW_OP::constu: 125 | stack.push_back(cur.uleb128()); 126 | break; 127 | case DW_OP::consts: 128 | stack.push_back(cur.sleb128()); 129 | break; 130 | 131 | // 2.5.1.2 Register based addressing 132 | case DW_OP::fbreg: 133 | // XXX 134 | throw runtime_error("DW_OP_fbreg not implemented"); 135 | case DW_OP::breg0...DW_OP::breg31: 136 | tmp1.u = (unsigned)op - (unsigned)DW_OP::breg0; 137 | tmp2.s = cur.sleb128(); 138 | stack.push_back((int64_t)ctx->reg(tmp1.u) + tmp2.s); 139 | break; 140 | case DW_OP::bregx: 141 | tmp1.u = cur.uleb128(); 142 | tmp2.s = cur.sleb128(); 143 | stack.push_back((int64_t)ctx->reg(tmp1.u) + tmp2.s); 144 | break; 145 | 146 | // 2.5.1.3 Stack operations 147 | case DW_OP::dup: 148 | CHECK(); 149 | stack.push_back(stack.back()); 150 | break; 151 | case DW_OP::drop: 152 | CHECK(); 153 | stack.pop_back(); 154 | break; 155 | case DW_OP::pick: 156 | tmp1.u = cur.fixed(); 157 | CHECKN(tmp1.u); 158 | stack.push_back(stack.revat(tmp1.u)); 159 | break; 160 | case DW_OP::over: 161 | CHECKN(2); 162 | stack.push_back(stack.revat(1)); 163 | break; 164 | case DW_OP::swap: 165 | CHECKN(2); 166 | tmp1.u = stack.back(); 167 | stack.back() = stack.revat(1); 168 | stack.revat(1) = tmp1.u; 169 | break; 170 | case DW_OP::rot: 171 | CHECKN(3); 172 | tmp1.u = stack.back(); 173 | stack.back() = stack.revat(1); 174 | stack.revat(1) = stack.revat(2); 175 | stack.revat(2) = tmp1.u; 176 | break; 177 | case DW_OP::deref: 178 | tmp1.u = subsec->addr_size; 179 | goto deref_common; 180 | case DW_OP::deref_size: 181 | tmp1.u = cur.fixed(); 182 | if (tmp1.u > subsec->addr_size) 183 | throw expr_error("DW_OP_deref_size operand exceeds address size"); 184 | deref_common: 185 | CHECK(); 186 | stack.back() = ctx->deref_size(stack.back(), tmp1.u); 187 | break; 188 | case DW_OP::xderef: 189 | tmp1.u = subsec->addr_size; 190 | goto xderef_common; 191 | case DW_OP::xderef_size: 192 | tmp1.u = cur.fixed(); 193 | if (tmp1.u > subsec->addr_size) 194 | throw expr_error("DW_OP_xderef_size operand exceeds address size"); 195 | xderef_common: 196 | CHECKN(2); 197 | tmp2.u = stack.back(); 198 | stack.pop_back(); 199 | stack.back() = ctx->xderef_size(tmp2.u, stack.back(), tmp1.u); 200 | break; 201 | case DW_OP::push_object_address: 202 | // XXX 203 | throw runtime_error("DW_OP_push_object_address not implemented"); 204 | case DW_OP::form_tls_address: 205 | CHECK(); 206 | stack.back() = ctx->form_tls_address(stack.back()); 207 | break; 208 | case DW_OP::call_frame_cfa: 209 | // XXX 210 | throw runtime_error("DW_OP_call_frame_cfa not implemented"); 211 | 212 | // 2.5.1.4 Arithmetic and logical operations 213 | #define UBINOP(binop) \ 214 | do { \ 215 | CHECKN(2); \ 216 | tmp1.u = stack.back(); \ 217 | stack.pop_back(); \ 218 | tmp2.u = stack.back(); \ 219 | stack.back() = tmp2.u binop tmp1.u; \ 220 | } while (0) 221 | case DW_OP::abs: 222 | CHECK(); 223 | tmp1.u = stack.back(); 224 | if (tmp1.s < 0) 225 | tmp1.s = -tmp1.s; 226 | stack.back() = tmp1.u; 227 | break; 228 | case DW_OP::and_: 229 | UBINOP(&); 230 | break; 231 | case DW_OP::div: 232 | CHECKN(2); 233 | tmp1.u = stack.back(); 234 | stack.pop_back(); 235 | tmp2.u = stack.back(); 236 | tmp3.s = tmp1.s / tmp2.s; 237 | stack.back() = tmp3.u; 238 | break; 239 | case DW_OP::minus: 240 | UBINOP(-); 241 | break; 242 | case DW_OP::mod: 243 | UBINOP(%); 244 | break; 245 | case DW_OP::mul: 246 | UBINOP(*); 247 | break; 248 | case DW_OP::neg: 249 | CHECK(); 250 | tmp1.u = stack.back(); 251 | tmp1.s = -tmp1.s; 252 | stack.back() = tmp1.u; 253 | break; 254 | case DW_OP::not_: 255 | CHECK(); 256 | stack.back() = ~stack.back(); 257 | break; 258 | case DW_OP::or_: 259 | UBINOP(|); 260 | break; 261 | case DW_OP::plus: 262 | UBINOP(+); 263 | break; 264 | case DW_OP::plus_uconst: 265 | tmp1.u = cur.uleb128(); 266 | CHECK(); 267 | stack.back() += tmp1.u; 268 | break; 269 | case DW_OP::shl: 270 | CHECKN(2); 271 | tmp1.u = stack.back(); 272 | stack.pop_back(); 273 | tmp2.u = stack.back(); 274 | // C++ does not define what happens if you 275 | // shift by more bits than the width of the 276 | // type, so we handle this case specially 277 | if (tmp1.u < sizeof(tmp2.u)*8) 278 | stack.back() = tmp2.u << tmp1.u; 279 | else 280 | stack.back() = 0; 281 | break; 282 | case DW_OP::shr: 283 | CHECKN(2); 284 | tmp1.u = stack.back(); 285 | stack.pop_back(); 286 | tmp2.u = stack.back(); 287 | // Same as above 288 | if (tmp1.u < sizeof(tmp2.u)*8) 289 | stack.back() = tmp2.u >> tmp1.u; 290 | else 291 | stack.back() = 0; 292 | break; 293 | case DW_OP::shra: 294 | CHECKN(2); 295 | tmp1.u = stack.back(); 296 | stack.pop_back(); 297 | tmp2.u = stack.back(); 298 | // Shifting a negative number is 299 | // implementation-defined in C++. 300 | tmp3.u = (tmp2.s < 0); 301 | if (tmp3.u) 302 | tmp2.s = -tmp2.s; 303 | if (tmp1.u < sizeof(tmp2.u)*8) 304 | tmp2.u >>= tmp1.u; 305 | else 306 | tmp2.u = 0; 307 | // DWARF implies that over-shifting a negative 308 | // number should result in 0, not ~0. 309 | if (tmp3.u) 310 | tmp2.s = -tmp2.s; 311 | stack.back() = tmp2.u; 312 | break; 313 | case DW_OP::xor_: 314 | UBINOP(^); 315 | break; 316 | #undef UBINOP 317 | 318 | // 2.5.1.5 Control flow operations 319 | #define SRELOP(relop) \ 320 | do { \ 321 | CHECKN(2); \ 322 | tmp1.u = stack.back(); \ 323 | stack.pop_back(); \ 324 | tmp2.u = stack.back(); \ 325 | stack.back() = (tmp2.s <= tmp1.s) ? 1 : 0; \ 326 | } while (0) 327 | case DW_OP::le: 328 | SRELOP(<=); 329 | break; 330 | case DW_OP::ge: 331 | SRELOP(>=); 332 | break; 333 | case DW_OP::eq: 334 | SRELOP(==); 335 | break; 336 | case DW_OP::lt: 337 | SRELOP(<); 338 | break; 339 | case DW_OP::gt: 340 | SRELOP(>); 341 | break; 342 | case DW_OP::ne: 343 | SRELOP(!=); 344 | break; 345 | case DW_OP::skip: 346 | tmp1.s = cur.fixed(); 347 | goto skip_common; 348 | case DW_OP::bra: 349 | tmp1.s = cur.fixed(); 350 | CHECK(); 351 | tmp2.u = stack.back(); 352 | stack.pop_back(); 353 | if (tmp2.u == 0) 354 | break; 355 | skip_common: 356 | cur = cursor(subsec, (int64_t)cur.get_section_offset() + tmp1.s); 357 | break; 358 | case DW_OP::call2: 359 | case DW_OP::call4: 360 | case DW_OP::call_ref: 361 | // XXX 362 | throw runtime_error(to_string(op) + " not implemented"); 363 | #undef SRELOP 364 | 365 | // 2.5.1.6 Special operations 366 | case DW_OP::nop: 367 | break; 368 | 369 | // 2.6.1.1.2 Register location descriptions 370 | case DW_OP::reg0...DW_OP::reg31: 371 | result.location_type = expr_result::type::reg; 372 | result.value = (unsigned)op - (unsigned)DW_OP::reg0; 373 | break; 374 | case DW_OP::regx: 375 | result.location_type = expr_result::type::reg; 376 | result.value = cur.uleb128(); 377 | break; 378 | 379 | // 2.6.1.1.3 Implicit location descriptions 380 | case DW_OP::implicit_value: 381 | result.location_type = expr_result::type::implicit; 382 | result.implicit_len = cur.uleb128(); 383 | cur.ensure(result.implicit_len); 384 | result.implicit = cur.pos; 385 | break; 386 | case DW_OP::stack_value: 387 | CHECK(); 388 | result.location_type = expr_result::type::literal; 389 | result.value = stack.back(); 390 | break; 391 | 392 | // 2.6.1.2 Composite location descriptions 393 | case DW_OP::piece: 394 | case DW_OP::bit_piece: 395 | // XXX 396 | throw runtime_error(to_string(op) + " not implemented"); 397 | 398 | case DW_OP::lo_user...DW_OP::hi_user: 399 | // XXX We could let the context evaluate this, 400 | // but it would need access to the cursor. 401 | throw expr_error("unknown user op " + to_string(op)); 402 | 403 | default: 404 | throw expr_error("bad operation " + to_string(op)); 405 | } 406 | #pragma GCC diagnostic pop 407 | #undef CHECK 408 | #undef CHECKN 409 | } 410 | 411 | if (result.location_type == expr_result::type::address) { 412 | // The result type is still and address, so we should 413 | // fetch it from the top of stack. 414 | if (stack.empty()) 415 | throw expr_error("final stack is empty; no result given"); 416 | result.value = stack.back(); 417 | } 418 | 419 | return result; 420 | 421 | underflow: 422 | throw expr_error("stack underflow evaluating DWARF expression"); 423 | } 424 | 425 | DWARFPP_END_NAMESPACE 426 | -------------------------------------------------------------------------------- /elf/data.hh: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013 Austin T. Clements. All rights reserved. 2 | // Use of this source code is governed by an MIT license 3 | // that can be found in the LICENSE file. 4 | 5 | #ifndef _ELFPP_DATA_HH_ 6 | #define _ELFPP_DATA_HH_ 7 | 8 | #include "common.hh" 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | ELFPP_BEGIN_NAMESPACE 15 | 16 | // Object file classes (ELF64 table 3) 17 | enum class elfclass : unsigned char 18 | { 19 | _32 = 1, // 32-bit objects 20 | _64 = 2, // 64-bit objects 21 | }; 22 | 23 | std::string 24 | to_string(elfclass v); 25 | 26 | // Common basic data types 27 | struct ElfTypes 28 | { 29 | typedef std::uint16_t Half; 30 | typedef std::uint32_t Word; 31 | typedef std::int32_t Sword; 32 | }; 33 | 34 | struct Elf32 : public ElfTypes 35 | { 36 | // ELF class 37 | static const elfclass cls = elfclass::_32; 38 | 39 | // Basic data types (ELF32 figure 1-2) 40 | typedef std::uint32_t Addr; 41 | typedef std::uint32_t Off; 42 | 43 | // Predicated types 44 | typedef Word Word32_Xword64; 45 | 46 | template 47 | struct pick 48 | { 49 | typedef t32 t; 50 | }; 51 | }; 52 | 53 | struct Elf64 : ElfTypes 54 | { 55 | // ELF class 56 | static const elfclass cls = elfclass::_64; 57 | 58 | // Basic data types (ELF64 table 1) 59 | typedef std::uint64_t Addr; 60 | typedef std::uint64_t Off; 61 | typedef std::uint64_t Xword; 62 | typedef std::int64_t Sxword; 63 | 64 | // Predicated types 65 | typedef Xword Word32_Xword64; 66 | 67 | template 68 | struct pick 69 | { 70 | typedef t64 t; 71 | }; 72 | }; 73 | 74 | // Data encodings (ELF64 table 4) 75 | enum class elfdata : unsigned char 76 | { 77 | lsb = 1, 78 | msb = 2, 79 | }; 80 | 81 | std::string 82 | to_string(elfdata v); 83 | 84 | // Operating system and ABI identifiers (ELF64 table 5) 85 | enum class elfosabi : unsigned char 86 | { 87 | sysv = 0, 88 | hpux = 1, 89 | standalone = 255, 90 | }; 91 | 92 | std::string 93 | to_string(elfosabi v); 94 | 95 | // Object file types (ELF64 table 6) 96 | enum class et : ElfTypes::Half 97 | { 98 | none = 0, // No file type 99 | rel = 1, // Relocatable object file 100 | exec = 2, // Executable file 101 | dyn = 3, // Shared object file 102 | core = 4, // Core file 103 | loos = 0xfe00, // Environment-specific use 104 | hios = 0xfeff, 105 | loproc = 0xff00, // Processor-specific use 106 | hiproc = 0xffff, 107 | }; 108 | 109 | std::string 110 | to_string(et v); 111 | 112 | // ELF header (ELF32 figure 1-3, ELF64 figure 2) 113 | template 114 | struct Ehdr 115 | { 116 | typedef E types; 117 | static const byte_order order = Order; 118 | 119 | // ELF identification 120 | unsigned char ei_magic[4]; 121 | elfclass ei_class; 122 | elfdata ei_data; 123 | unsigned char ei_version; 124 | elfosabi ei_osabi; 125 | unsigned char ei_abiversion; 126 | unsigned char ei_pad[7]; 127 | 128 | et type; // Object file type 129 | typename E::Half machine; // Machine type 130 | typename E::Word version; // Object file version 131 | typename E::Addr entry; // Entry point address 132 | typename E::Off phoff; // Program header offset 133 | typename E::Off shoff; // Section header offset 134 | typename E::Word flags; // Processor-specific flags 135 | typename E::Half ehsize; // ELF header size 136 | typename E::Half phentsize; // Size of program header entry 137 | typename E::Half phnum; // Number of program header entries 138 | typename E::Half shentsize; // Size of section header entry 139 | typename E::Half shnum; // Number of section header entries 140 | typename E::Half shstrndx; // Section name string table index 141 | 142 | template 143 | void from(const E2 &o) 144 | { 145 | std::memcpy(ei_magic, o.ei_magic, sizeof(ei_magic)); 146 | ei_class = swizzle(o.ei_class, o.order, order); 147 | ei_data = swizzle(o.ei_data, o.order, order); 148 | ei_version = swizzle(o.ei_version, o.order, order); 149 | ei_osabi = swizzle(o.ei_osabi, o.order, order); 150 | ei_abiversion = swizzle(o.ei_abiversion, o.order, order); 151 | std::memcpy(ei_pad, o.ei_pad, sizeof(ei_pad)); 152 | 153 | type = swizzle(o.type, o.order, order); 154 | machine = swizzle(o.machine, o.order, order); 155 | version = swizzle(o.version, o.order, order); 156 | entry = swizzle(o.entry, o.order, order); 157 | phoff = swizzle(o.phoff, o.order, order); 158 | shoff = swizzle(o.shoff, o.order, order); 159 | flags = swizzle(o.flags, o.order, order); 160 | ehsize = swizzle(o.ehsize, o.order, order); 161 | phentsize = swizzle(o.phentsize, o.order, order); 162 | phnum = swizzle(o.phnum, o.order, order); 163 | shentsize = swizzle(o.shentsize, o.order, order); 164 | shnum = swizzle(o.shnum, o.order, order); 165 | shstrndx = swizzle(o.shstrndx, o.order, order); 166 | } 167 | }; 168 | 169 | // Special section indices (ELF32 figure 1-7, ELF64 table 7) 170 | // 171 | // This is an integer with a few special values, so this is a regular 172 | // enum, rather than a type-safe enum. However, this is declared in a 173 | // namespace and then used to avoid polluting the elf:: namespace. 174 | namespace enums { 175 | enum shn : ElfTypes::Half // This is a Word in Shdr and Half in Sym. 176 | { 177 | undef = 0, // Undefined or meaningless 178 | 179 | loproc = 0xff00, // Processor-specific use 180 | hiproc = 0xff1f, 181 | loos = 0xff20, // Environment-specific use 182 | hios = 0xff3f, 183 | 184 | abs = 0xfff1, // Reference is an absolute value 185 | common = 0xfff2, // Symbol declared as a common block 186 | }; 187 | 188 | std::string 189 | to_string(shn v); 190 | } 191 | 192 | using enums::shn; 193 | 194 | // Section types (ELF64 table 8) 195 | enum class sht : ElfTypes::Word 196 | { 197 | null = 0, // Marks an unseen section header 198 | progbits = 1, // Contains information defined by the program 199 | symtab = 2, // Contains a linker symbol table 200 | strtab = 3, // Contains a string table 201 | rela = 4, // Contains "Rela" type relocation entries 202 | hash = 5, // Contains a symbol hash table 203 | dynamic = 6, // Contains dynamic linking tables 204 | note = 7, // Contains note information 205 | nobits = 8, // Contains uninitialized space; 206 | // does not occupy any space in the file 207 | rel = 9, // Contains "Rel" type relocation entries 208 | shlib = 10, // Reserved 209 | dynsym = 11, // Contains a dynamic loader symbol table 210 | loos = 0x60000000, // Environment-specific use 211 | hios = 0x6FFFFFFF, 212 | loproc = 0x70000000, // Processor-specific use 213 | hiproc = 0x7FFFFFFF, 214 | }; 215 | 216 | std::string 217 | to_string(sht v); 218 | 219 | // Section attributes (ELF64 table 9). Note: This is an Elf32_Word in 220 | // ELF32. We use the larger ELF64 type for the canonical 221 | // representation and switch it out for a plain Elf32_Word in the 222 | // ELF32 format. 223 | enum class shf : Elf64::Xword 224 | { 225 | write = 0x1, // Section contains writable data 226 | alloc = 0x2, // Section is allocated in memory image of program 227 | execinstr = 0x4, // Section contains executable instructions 228 | maskos = 0x0F000000, // Environment-specific use 229 | maskproc = 0xF0000000, // Processor-specific use 230 | }; 231 | 232 | std::string 233 | to_string(shf v); 234 | 235 | static inline constexpr shf operator&(shf a, shf b) 236 | { 237 | return (shf)((std::uint64_t)a & (std::uint64_t)b); 238 | } 239 | 240 | static inline constexpr shf operator|(shf a, shf b) 241 | { 242 | return (shf)((std::uint64_t)a | (std::uint64_t)b); 243 | } 244 | 245 | static inline constexpr shf operator^(shf a, shf b) 246 | { 247 | return (shf)((std::uint64_t)a ^ (std::uint64_t)b); 248 | } 249 | 250 | static inline constexpr shf operator~(shf a) 251 | { 252 | return (shf)~((std::uint64_t)a); 253 | } 254 | 255 | static inline shf& operator&=(shf &a, shf b) 256 | { 257 | a = a & b; 258 | return a; 259 | } 260 | 261 | static inline shf& operator|=(shf &a, shf b) 262 | { 263 | a = a | b; 264 | return a; 265 | } 266 | 267 | static inline shf& operator^=(shf &a, shf b) 268 | { 269 | a = a ^ b; 270 | return a; 271 | } 272 | 273 | // Section header (ELF32 figure 1-8, ELF64 figure 3) 274 | template 275 | struct Shdr 276 | { 277 | typedef E types; 278 | static const byte_order order = Order; 279 | // Section numbers are half-words in some structures and full 280 | // words in others. Here we declare a local shn type that is 281 | // elf::shn for the native byte order, but the full word for 282 | // specific encoding byte orders. 283 | typedef typename internal::OrderPick::T shn; 284 | 285 | typename E::Word name; // Section name 286 | sht type; // Section type 287 | typename E::template pick::t flags; // Section attributes 288 | typename E::Addr addr; // Virtual address in memory 289 | typename E::Off offset; // Offset in file 290 | typename E::Word32_Xword64 size; // Size of section 291 | shn link; // Link to other section 292 | typename E::Word info; // Miscellaneous information 293 | typename E::Word32_Xword64 addralign; // Address alignment boundary 294 | typename E::Word32_Xword64 entsize; // Size of entries, if section has table 295 | 296 | template 297 | void from(const E2 &o) 298 | { 299 | name = swizzle(o.name, o.order, order); 300 | type = swizzle(o.type, o.order, order); 301 | flags = (decltype(flags))swizzle(o.flags, o.order, order); 302 | addr = swizzle(o.addr, o.order, order); 303 | offset = swizzle(o.offset, o.order, order); 304 | size = swizzle(o.size, o.order, order); 305 | link = (decltype(link))swizzle((typename E::Word)o.link, o.order, order); 306 | info = swizzle(o.info, o.order, order); 307 | addralign = swizzle(o.addralign, o.order, order); 308 | entsize = swizzle(o.entsize, o.order, order); 309 | } 310 | }; 311 | 312 | // Segment types (ELF64 table 16) 313 | enum class pt : ElfTypes::Word 314 | { 315 | null = 0, // Unused entry 316 | load = 1, // Loadable segment 317 | dynamic = 2, // Dynamic linking tables 318 | interp = 3, // Program interpreter path name 319 | note = 4, // Note sections 320 | shlib = 5, // Reserved 321 | phdr = 6, // Program header table 322 | loos = 0x60000000, // Environment-specific use 323 | hios = 0x6FFFFFFF, 324 | loproc = 0x70000000, // Processor-specific use 325 | hiproc = 0x7FFFFFFF, 326 | }; 327 | 328 | std::string 329 | to_string(pt v); 330 | 331 | // Segment attributes 332 | enum class pf : ElfTypes::Word 333 | { 334 | x = 0x1, // Execute permission 335 | w = 0x2, // Write permission 336 | r = 0x4, // Read permission 337 | maskos = 0x00FF0000, // Environment-specific use 338 | maskproc = 0xFF000000, // Processor-specific use 339 | }; 340 | 341 | std::string 342 | to_string(pf v); 343 | 344 | static inline constexpr pf operator&(pf a, pf b) 345 | { 346 | return (pf)((std::uint64_t)a & (std::uint64_t)b); 347 | } 348 | 349 | static inline constexpr pf operator|(pf a, pf b) 350 | { 351 | return (pf)((std::uint64_t)a | (std::uint64_t)b); 352 | } 353 | 354 | static inline constexpr pf operator^(pf a, pf b) 355 | { 356 | return (pf)((std::uint64_t)a ^ (std::uint64_t)b); 357 | } 358 | 359 | static inline constexpr pf operator~(pf a) 360 | { 361 | return (pf)~((std::uint64_t)a); 362 | } 363 | 364 | static inline pf& operator&=(pf &a, pf b) 365 | { 366 | a = a & b; 367 | return a; 368 | } 369 | 370 | static inline pf& operator|=(pf &a, pf b) 371 | { 372 | a = a | b; 373 | return a; 374 | } 375 | 376 | static inline pf& operator^=(pf &a, pf b) 377 | { 378 | a = a ^ b; 379 | return a; 380 | } 381 | 382 | // Program header (ELF32 figure 2-1, ELF64 figure 6) 383 | template 384 | struct Phdr; 385 | 386 | template 387 | struct Phdr 388 | { 389 | typedef Elf32 types; 390 | static const byte_order order = Order; 391 | 392 | pt type; // Type of segment 393 | Elf32::Off offset; // Offset in file 394 | Elf32::Addr vaddr; // Virtual address in memory 395 | Elf32::Addr paddr; // Reserved 396 | Elf32::Word filesz; // Size of segment in file 397 | Elf32::Word memsz; // Size of segment in memory 398 | pf flags; // Segment attributes 399 | Elf32::Word align; // Alignment of segment 400 | 401 | template 402 | void from(const E2 &o) 403 | { 404 | type = swizzle(o.type, o.order, order); 405 | offset = swizzle(o.offset, o.order, order); 406 | vaddr = swizzle(o.vaddr, o.order, order); 407 | paddr = swizzle(o.paddr, o.order, order); 408 | filesz = swizzle(o.filesz, o.order, order); 409 | memsz = swizzle(o.memsz, o.order, order); 410 | flags = swizzle(o.flags, o.order, order); 411 | align = swizzle(o.align, o.order, order); 412 | } 413 | }; 414 | 415 | template 416 | struct Phdr 417 | { 418 | typedef Elf64 types; 419 | static const byte_order order = Order; 420 | 421 | pt type; // Type of segment 422 | pf flags; // Segment attributes 423 | Elf64::Off offset; // Offset in file 424 | Elf64::Addr vaddr; // Virtual address in memory 425 | Elf64::Addr paddr; // Reserved 426 | Elf64::Xword filesz; // Size of segment in file 427 | Elf64::Xword memsz; // Size of segment in memory 428 | Elf64::Xword align; // Alignment of segment 429 | 430 | template 431 | void from(const E2 &o) 432 | { 433 | type = swizzle(o.type, o.order, order); 434 | offset = swizzle(o.offset, o.order, order); 435 | vaddr = swizzle(o.vaddr, o.order, order); 436 | paddr = swizzle(o.paddr, o.order, order); 437 | filesz = swizzle(o.filesz, o.order, order); 438 | memsz = swizzle(o.memsz, o.order, order); 439 | flags = swizzle(o.flags, o.order, order); 440 | align = swizzle(o.align, o.order, order); 441 | } 442 | }; 443 | 444 | // Symbol bindings (ELF32 figure 1-16, ELF64 table 14) 445 | enum class stb : unsigned char 446 | { 447 | local = 0, // Not visible outside the object file 448 | global = 1, // Global symbol 449 | weak = 2, // Global scope, but with lower 450 | // precedence than global symbols 451 | loos = 10, // Environment-specific use 452 | hios = 12, 453 | loproc = 13, // Processor-specific use 454 | hiproc = 15, 455 | }; 456 | 457 | std::string 458 | to_string(stb v); 459 | 460 | // Symbol types (ELF32 figure 1-17, ELF64 table 15) 461 | enum class stt : unsigned char 462 | { 463 | notype = 0, // No type (e.g., absolute symbol) 464 | object = 1, // Data object 465 | func = 2, // Function entry point 466 | section = 3, // Symbol is associated with a section 467 | file = 4, // Source file associated with the 468 | // object file 469 | loos = 10, // Environment-specific use 470 | hios = 12, 471 | loproc = 13, // Processor-specific use 472 | hiproc = 15, 473 | }; 474 | 475 | std::string 476 | to_string(stt v); 477 | 478 | // Symbol table (ELF32 figure 1-15, ELF64 figure 4) 479 | template 480 | struct Sym; 481 | 482 | template 483 | struct Sym 484 | { 485 | typedef Elf32 types; 486 | static const byte_order order = Order; 487 | 488 | Elf32::Word name; // Symbol name (strtab offset) 489 | Elf32::Addr value; // Symbol value (address) 490 | Elf32::Word size; // Size of object 491 | unsigned char info; // Type and binding attributes 492 | unsigned char other; // Reserved 493 | shn shnxd; // Section table index 494 | 495 | template 496 | void from(const E2 &o) 497 | { 498 | name = swizzle(o.name, o.order, order); 499 | value = swizzle(o.value, o.order, order); 500 | size = swizzle(o.size, o.order, order); 501 | info = o.info; 502 | other = o.other; 503 | shnxd = swizzle(o.shnxd, o.order, order); 504 | } 505 | 506 | stb binding() const 507 | { 508 | return (stb)(info >> 4); 509 | } 510 | 511 | void set_binding(stb v) 512 | { 513 | info = (info & 0x0F) | ((unsigned char)v << 4); 514 | } 515 | 516 | stt type() const 517 | { 518 | return (stt)(info & 0xF); 519 | } 520 | 521 | void set_type(stt v) 522 | { 523 | info = (info & 0xF0) | (unsigned char)v; 524 | } 525 | }; 526 | 527 | template 528 | struct Sym 529 | { 530 | typedef Elf64 types; 531 | static const byte_order order = Order; 532 | 533 | Elf64::Word name; // Symbol name (strtab offset) 534 | unsigned char info; // Type and binding attributes 535 | unsigned char other; // Reserved 536 | shn shnxd; // Section table index 537 | Elf64::Addr value; // Symbol value (address) 538 | Elf64::Xword size; // Size of object 539 | 540 | template 541 | void from(const E2 &o) 542 | { 543 | name = swizzle(o.name, o.order, order); 544 | value = swizzle(o.value, o.order, order); 545 | size = swizzle(o.size, o.order, order); 546 | info = o.info; 547 | other = o.other; 548 | shnxd = swizzle(o.shnxd, o.order, order); 549 | } 550 | 551 | stb binding() const 552 | { 553 | return (stb)(info >> 4); 554 | } 555 | 556 | void set_binding(stb v) 557 | { 558 | info = (info & 0xF) | ((unsigned char)v << 4); 559 | } 560 | 561 | stt type() const 562 | { 563 | return (stt)(info & 0xF); 564 | } 565 | 566 | void set_type(stt v) 567 | { 568 | info = (info & 0xF0) | (unsigned char)v; 569 | } 570 | }; 571 | 572 | ELFPP_END_NAMESPACE 573 | 574 | #endif 575 | --------------------------------------------------------------------------------