├── .gitignore ├── CMakeLists.txt ├── LICENSE.txt ├── README.md ├── ext ├── catch2 │ └── catch.hpp ├── cmdline │ └── cmdline.hpp ├── easylogging │ ├── easylogging++.cc │ └── easylogging++.h ├── gsl │ └── gsl-lite.hpp ├── json │ └── json.hpp └── libelfin │ ├── LICENSE │ ├── README.md │ └── elf │ ├── common.hh │ ├── data.hh │ ├── elf++.hh │ ├── elf.cc │ ├── mmap_loader.cc │ ├── to_hex.hh │ └── to_string.cc ├── install.sh ├── src ├── core │ ├── BasicBlock.cpp │ ├── BasicBlock.hpp │ ├── CSInstWrapper.cpp │ ├── CSInstWrapper.hpp │ ├── Common.cpp │ ├── Common.hpp │ ├── Disassembler.cpp │ ├── Disassembler.hpp │ ├── FileLoader.cpp │ ├── FileLoader.hpp │ ├── Function.cpp │ ├── Function.hpp │ ├── FunctionBuilder.cpp │ ├── FunctionBuilder.hpp │ ├── MCInst.cpp │ ├── MCInst.hpp │ ├── Region.cpp │ └── Region.hpp ├── dump │ ├── patch.c │ └── patch.h ├── dwarf │ ├── Data.cpp │ ├── Data.hpp │ ├── EhFrame.cpp │ ├── EhFrame.hpp │ ├── LEB128.hpp │ ├── PrettyPrint.cpp │ └── PrettyPrint.hpp ├── elf │ ├── ElfExtender.cpp │ ├── ElfExtender.hpp │ ├── ElfModule.cpp │ ├── ElfModule.hpp │ ├── ElfParser.cpp │ ├── ElfParser.hpp │ ├── ElfPatchManager.cpp │ ├── ElfPatchManager.hpp │ └── Util.hpp ├── flax │ ├── Emulator.cpp │ ├── Emulator.hpp │ ├── Flax.cpp │ └── Flax.hpp ├── graph │ ├── CFG.cpp │ ├── CFG.hpp │ ├── DirectedGraph.hpp │ ├── DominatorTree.cpp │ ├── DominatorTree.hpp │ ├── Dot.cpp │ ├── Dot.hpp │ ├── SuperBlock.cpp │ └── SuperBlock.hpp ├── main │ └── main.cpp ├── util │ ├── BcovConfig.cpp │ ├── BcovConfig.hpp │ ├── BcovConfigParser.cpp │ ├── BcovConfigParser.hpp │ ├── Demangler.cpp │ ├── Demangler.hpp │ ├── ElfData.hpp │ ├── FileUtil.hpp │ ├── Logging.cpp │ ├── Logging.hpp │ ├── ProgOptions.cpp │ └── ProgOptions.hpp └── x64 │ ├── Arch.cpp │ ├── Arch.hpp │ ├── Asm.cpp │ ├── Asm.hpp │ ├── Inst.cpp │ ├── Inst.hpp │ ├── JumpTabAnalyzer.cpp │ └── JumpTabAnalyzer.hpp └── tools └── bcov-rt ├── CMakeLists.txt ├── bcov-rt.c └── bcov-rt.h /.gitignore: -------------------------------------------------------------------------------- 1 | # Build directories 2 | /build 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | 22 | # Compiled Static libraries 23 | *.lai 24 | *.la 25 | *.a 26 | *.lib 27 | 28 | # Executables 29 | *.exe 30 | *.out 31 | *.app 32 | 33 | # Idea Project 34 | .idea 35 | 36 | # Vim backups 37 | *~ 38 | 39 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.2) 2 | project(bcov) 3 | 4 | set(CMAKE_CXX_STANDARD 14) 5 | 6 | set(CMAKE_VERBOSE_MAKEFILE ON) 7 | 8 | ############################################# 9 | ############ compiler flags 10 | 11 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -pedantic -pipe ") 12 | 13 | set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fno-omit-frame-pointer \ 14 | -Wno-unused-function -Wno-unused-variable ") 15 | 16 | set(CMAKE_CXX_FLAGS_ASAN "${CMAKE_CXX_FLAGS_DEBUG} -fsanitize=address ") 17 | 18 | set(CMAKE_CXX_FLAGS_UBSAN "${CMAKE_CXX_FLAGS_DEBUG} -fsanitize=undefined ") 19 | 20 | set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O2 ") 21 | 22 | ############################################# 23 | ############ find dependencies 24 | 25 | include_directories(src ext ${CMAKE_INCLUDE_PATH}) 26 | link_directories(${CMAKE_LIBRARY_PATH}) 27 | 28 | get_property(BCOV_INCLUDE_DIRS DIRECTORY ${CMAKE_SRC_DIR} PROPERTY INCLUDE_DIRECTORIES) 29 | 30 | get_property(BCOV_LINK_DIRS DIRECTORY ${CMAKE_SRC_DIR} PROPERTY LINK_DIRECTORIES) 31 | 32 | find_path(CAPSTONE_INCLUDE_DIR NAMES capstone PATHS ${BCOV_INCLUDE_DIRS}) 33 | if (NOT CAPSTONE_INCLUDE_DIR) 34 | message(FATAL_ERROR "capstone include files were not found!") 35 | endif () 36 | 37 | find_library(CAPSTONE_DYNAMIC_LIB NAMES capstone PATHS ${BCOV_LINK_DIRS}) 38 | if (NOT CAPSTONE_DYNAMIC_LIB) 39 | message(FATAL_ERROR "capstone library was not found!") 40 | endif () 41 | 42 | find_path(UNICORN_INCLUDE_DIR NAMES unicorn PATHS ${BCOV_INCLUDE_DIRS}) 43 | if (NOT UNICORN_INCLUDE_DIR) 44 | message(FATAL_ERROR "unicorn include files were not found!") 45 | endif () 46 | 47 | find_library(UNICORN_DYNAMIC_LIB NAMES unicorn PATHS ${BCOV_LINK_DIRS}) 48 | if (NOT UNICORN_DYNAMIC_LIB) 49 | message(FATAL_ERROR "unicorn library was not found!") 50 | endif () 51 | 52 | ############################################# 53 | ############## define sources 54 | 55 | set(BCOV_GRAPH_SOURCES 56 | src/graph/Dot.cpp 57 | src/graph/CFG.cpp 58 | src/graph/DominatorTree.cpp 59 | src/graph/SuperBlock.cpp 60 | ) 61 | 62 | set(BCOV_X64_SOURCES 63 | src/x64/Inst.cpp 64 | src/x64/JumpTabAnalyzer.cpp 65 | src/x64/Arch.cpp 66 | src/x64/Asm.cpp 67 | ) 68 | 69 | set(BCOV_UTIL_SOURCES 70 | src/util/Logging.cpp 71 | src/util/BcovConfig.cpp 72 | src/util/BcovConfigParser.cpp 73 | src/util/ProgOptions.cpp 74 | src/util/Demangler.cpp 75 | ) 76 | 77 | set(BCOV_FLAX_SOURCES 78 | src/flax/Flax.cpp 79 | src/flax/Emulator.cpp 80 | ) 81 | 82 | set(BCOV_CORE_SOURCES 83 | src/core/Common.cpp 84 | src/core/CSInstWrapper.cpp 85 | src/core/MCInst.cpp 86 | src/core/Function.cpp 87 | src/core/BasicBlock.cpp 88 | src/core/FunctionBuilder.cpp 89 | src/core/FileLoader.cpp 90 | src/core/Region.cpp 91 | src/core/Disassembler.cpp 92 | ) 93 | 94 | set(BCOV_ELF_SOURCES 95 | src/elf/ElfParser.cpp 96 | src/elf/ElfModule.cpp 97 | src/elf/ElfExtender.cpp 98 | src/elf/ElfPatchManager.cpp 99 | src/dwarf/EhFrame.cpp 100 | src/dwarf/Data.cpp 101 | src/dwarf/PrettyPrint.cpp 102 | ) 103 | 104 | set(BCOV_DUMP_SOURCES 105 | src/dump/patch.c 106 | ) 107 | 108 | ############################################# 109 | ############## setup static libs 110 | 111 | add_library(easylogging STATIC ext/easylogging/easylogging++.cc) 112 | set_target_properties(easylogging PROPERTIES COMPILE_FLAGS 113 | "-DELPP_DEFAULT_LOG_FILE='\"bcov.log\"' -DELPP_FRESH_LOG_FILE \ 114 | -DELPP_DISABLE_DEFAULT_CRASH_HANDLING \ 115 | -DELPP_DISABLE_FATAL_LOGS \ 116 | -DELPP_FEATURE_PERFORMANCE_TRACKING \ 117 | -Wno-unused-function -Wno-unused-variable" 118 | ) 119 | 120 | if (CMAKE_BUILD_TYPE STREQUAL "UBSAN") 121 | get_target_property(EASYLOGGING_COMPILE_FLAGS easylogging COMPILE_FLAGS) 122 | string(REPLACE "-fsanitize=undefined" "" UPDATED_EASYLOGGING_FLAGS ${EASYLOGGING_COMPILE_FLAGS}) 123 | # workaround as easylogging has ub instances 124 | set_target_properties(easylogging PROPERTIES COMPILE_FLAGS 125 | "${UPDATED_EASYLOGGING_FLAGS} -fno-sanitize=undefined -fsanitize=unreachable") 126 | endif() 127 | 128 | 129 | set(LIBELFIN_DIR ${CMAKE_SOURCE_DIR}/ext/libelfin/elf) 130 | 131 | add_library(elf++ STATIC 132 | ${LIBELFIN_DIR}/mmap_loader.cc 133 | ${LIBELFIN_DIR}/to_string.cc 134 | ${LIBELFIN_DIR}/elf.cc 135 | ) 136 | 137 | ############################################# 138 | ############## builds 139 | 140 | add_library(bcov_static STATIC 141 | ${BCOV_UTIL_SOURCES} 142 | ${BCOV_CORE_SOURCES} 143 | ${BCOV_FLAX_SOURCES} 144 | ${BCOV_ELF_SOURCES} 145 | ${BCOV_GRAPH_SOURCES} 146 | ${BCOV_X64_SOURCES} 147 | ${BCOV_DUMP_SOURCES} 148 | ) 149 | 150 | ############################################## 151 | ############### 152 | add_subdirectory(tools/bcov-rt) 153 | 154 | add_executable(bcov src/main/main.cpp ${BCOV_SOURCES}) 155 | add_dependencies(bcov bcov_static easylogging elf++) 156 | target_link_libraries(bcov 157 | bcov_static 158 | easylogging 159 | elf++ 160 | capstone 161 | unicorn 162 | pthread 163 | ) 164 | 165 | ############################################## 166 | ############### installation 167 | 168 | install(TARGETS bcov 169 | RUNTIME DESTINATION bin 170 | LIBRARY DESTINATION lib 171 | ) 172 | 173 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2018-2019 Technische Universität Kaiserslautern 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | 21 | This license does not apply to software found under folder "ext" which is 22 | distributed under different licenses. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | ## bcov 3 | 4 | A tool for efficient binary-level coverage analysis. **bcov** statically 5 | instruments x86-64 ELF binaries without compiler support. It features probe 6 | pruning, precise CFG analyses, and sophisticated instrumentation techniques. 7 | We summarized this research in a 2-min teaser [video][link-teaser]. 8 | 9 | ## Resources 10 | 11 | - Details are available in our ESEC/FSE'20 [paper][link-paper]. You can find a slightly expanded pre-print [here][link-preprint]. 12 | - We have a [post][link-llvmdev] on the llvm-dev mailing list. It briefly introduces **bcov** and discusses potential future work. 13 | - This blog [post][link-post1] elaborates on the availability of function definitions in stripped binaries. 14 | - You can find our ESEC/FSE'20 talk [here][link-talk]. The slide deck (with transcript) is also [available][link-slide-deck]. 15 | - A sample set of binaries that we patched using `bcov` is available [here][link-samples]. However, the complete set of benchmarks is available on our archival [repository][link-benchmarks]. 16 | 17 | 18 | ## Software prerequisites 19 | 20 | The following software packages must be available: 21 | - [capstone][link-capstone] branch `next` commit #c3b4ce1901 22 | - [unicorn][link-unicorn] branch `master` commit #536c4e77c4 23 | 24 | Later versions of both frameworks should work in principle but have not been 25 | tested yet. The script `install.sh` can be used for installation. 26 | 27 | ## Research reproducibility 28 | 29 | We provide a [Dockerfile][link-dockerfile] which installs **bcov** and runs a coverage analysis experiment. 30 | Please checkout the supplemental [artifacts][link-artifacts] for more details. 31 | 32 | ## Usage 33 | 34 | The tool supports the following operation modes which are set using the option `--mode` (or simply `-m`): 35 | 36 | - `patch`. Patch a given binary. 37 | - `report`. Report coverage given a patched binary and a coverage data file. 38 | - `dump`. Dump various program graphs for a given function. For example, dump the CFG and dominator trees. 39 | 40 | The following command can be issued to patch a binary, 41 | 42 | ```shell script 43 | bcov -m patch -p any -v 5 -i perl -o perl.any 44 | ``` 45 | 46 | The instrumentation policy can be set to `any`, which refers to the any-node policy, or `all` which 47 | refers to the leaf-node policy. 48 | 49 | Coverage data can be dumped by injecting `libbcov-rt.so` using the `LD_PRELOAD` 50 | mechanism. For example, you can try the sample binary `perl.any` which can be 51 | found in the artifacts repository, 52 | 53 | ```shell script 54 | export BCOV_OPTIONS="coverage_dir=$PWD" # sets the directory for dumping coverage data. Defaults to $PWD 55 | export LD_PRELOAD="[full-path-to-bcov-rt]/libbcov-rt.so" 56 | ./perl.any -e 'print "Hello, bcov!\n"' 57 | ``` 58 | 59 | This will produce a dump file that has the extension '.bcov' in your current 60 | directory. This file can be supplied to **bcov** for coverage reporting, 61 | 62 | ```shell script 63 | bcov -m report -p any -i ./perl -d perl.any.1588260679.1816.bcov > report.out 64 | ``` 65 | 66 | Currently, **bcov** can not persist analysis results to disk. Therefore, the original binary must be 67 | re-analyzed to report coverage. Coverage will be reported for each basic block in the file 68 | `report.out`. The data in each line lists: 69 | - BB address 70 | - BB instruction count 71 | - is covered 72 | - is fallthrough (i.e., does not terminate with a branch) 73 | 74 | Also, a coverage summary is reported for each function. For example, it shows the basic block and instruction coverage ratios. 75 | 76 | For a given function, it is possible to selectively dump various program graphs like the CFG and superblock dominator graph. 77 | For example, consider function `S_search_const` in `perl`, 78 | 79 | ```shell script 80 | bcov -m dump -f "S_search_const" -i ./perl 81 | ``` 82 | 83 | This command will dump the following files: 84 | 85 | - func_421d90.cfg.dot. The CFG of the function. 86 | - func_421d90.rev.cfg.dot. Similar to the CFG but with all edges reversed. 87 | - func_421d90.pre.dom.dot. Predominator tree. 88 | - func_421d90.post.dom.dot. Postdominator tree. 89 | - func_421d90.sb.dom.dot. Superblock dominator graph. 90 | 91 | Graphs are dumped in the standard DOT format and can be viewed using a dot viewer like `xdot`. 92 | Please refer to this [blog post][link-post2] for additional details. 93 | 94 | ## Citing 95 | 96 | For citation in an academic work please use: 97 | 98 | @inproceedings{BenKhadra:FSE2020, 99 | address = {Virtual Event, USA}, 100 | author = {{Ben Khadra}, M. Ammar and Stoffel, Dominik and Kunz, Wolfgang}, 101 | booktitle = {ACM Joint European Software Engineering Conference and Symposium on the Foundations of Software Engineering - ESEC/FSE'20}, 102 | doi = {10.1145/3368089.3409694}, 103 | pages = {1153--1164}, 104 | publisher = {ACM Press}, 105 | title = {{Efficient Binary-Level Coverage Analysis}}, 106 | year = {2020}, 107 | month = {nov}, 108 | day = {6--13} 109 | } 110 | 111 | ## License 112 | 113 | This software is distributed under the MIT license. See `LICENSE.txt` for details. 114 | 115 | [link-capstone]: https://github.com/aquynh/capstone 116 | [link-unicorn]: https://github.com/unicorn-engine/unicorn 117 | [link-preprint]: https://arxiv.org/pdf/2004.14191.pdf 118 | [link-artifacts]: https://github.com/abenkhadra/bcov-artifacts 119 | [link-post1]: https://blog.formallyapplied.com/2020/05/function-identification/ 120 | [link-post2]: https://blog.formallyapplied.com/2020/06/bcov-program-graphs/ 121 | [link-dockerfile]: https://github.com/abenkhadra/bcov-artifacts/blob/master/Dockerfile 122 | [link-llvmdev]: http://lists.llvm.org/pipermail/llvm-dev/2020-June/142821.html 123 | [link-slide-deck]: https://docs.google.com/presentation/d/1BgcOaw06AcfCzc8PMPkhYANKUkevbxAPFQkbERpXBJ4/edit?usp=sharing 124 | [link-talk]: https://youtu.be/pns0lJ5FwwA 125 | [link-teaser]: https://youtu.be/AT29eitsChc 126 | [link-paper]: https://doi.org/10.1145/3368089.3409694 127 | [link-samples]: https://github.com/abenkhadra/bcov-artifacts/tree/master/sample-binaries 128 | [link-benchmarks]: https://doi.org/10.5281/zenodo.3876047 129 | -------------------------------------------------------------------------------- /ext/libelfin/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 | -------------------------------------------------------------------------------- /ext/libelfin/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 | -------------------------------------------------------------------------------- /ext/libelfin/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 | -------------------------------------------------------------------------------- /ext/libelfin/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() override 41 | { 42 | munmap(base, lim); 43 | } 44 | 45 | const void *load(off_t offset, size_t size) override 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 | -------------------------------------------------------------------------------- /ext/libelfin/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 | -------------------------------------------------------------------------------- /ext/libelfin/elf/to_string.cc: -------------------------------------------------------------------------------- 1 | // Automatically generated by make at Fri Mar 2 14:07:31 CET 2018 2 | // DO NOT EDIT 3 | 4 | #include "data.hh" 5 | #include "to_hex.hh" 6 | 7 | ELFPP_BEGIN_NAMESPACE 8 | 9 | std::string 10 | to_string(elfclass v) 11 | { 12 | switch (v) { 13 | case elfclass::_32: return "32"; 14 | case elfclass::_64: return "64"; 15 | } 16 | return "(elfclass)0x" + to_hex((int)v); 17 | } 18 | 19 | std::string 20 | to_string(elfdata v) 21 | { 22 | switch (v) { 23 | case elfdata::lsb: return "lsb"; 24 | case elfdata::msb: return "msb"; 25 | } 26 | return "(elfdata)0x" + to_hex((int)v); 27 | } 28 | 29 | std::string 30 | to_string(elfosabi v) 31 | { 32 | switch (v) { 33 | case elfosabi::sysv: return "sysv"; 34 | case elfosabi::hpux: return "hpux"; 35 | case elfosabi::standalone: return "standalone"; 36 | } 37 | return "(elfosabi)0x" + to_hex((int)v); 38 | } 39 | 40 | std::string 41 | to_string(et v) 42 | { 43 | switch (v) { 44 | case et::none: return "none"; 45 | case et::rel: return "rel"; 46 | case et::exec: return "exec"; 47 | case et::dyn: return "dyn"; 48 | case et::core: return "core"; 49 | case et::loos: break; 50 | case et::hios: break; 51 | case et::loproc: break; 52 | case et::hiproc: break; 53 | } 54 | return "(et)0x" + to_hex((int)v); 55 | } 56 | 57 | std::string 58 | to_string(sht v) 59 | { 60 | switch (v) { 61 | case sht::null: return "null"; 62 | case sht::progbits: return "progbits"; 63 | case sht::symtab: return "symtab"; 64 | case sht::strtab: return "strtab"; 65 | case sht::rela: return "rela"; 66 | case sht::hash: return "hash"; 67 | case sht::dynamic: return "dynamic"; 68 | case sht::note: return "note"; 69 | case sht::nobits: return "nobits"; 70 | case sht::rel: return "rel"; 71 | case sht::shlib: return "shlib"; 72 | case sht::dynsym: return "dynsym"; 73 | case sht::loos: break; 74 | case sht::hios: break; 75 | case sht::loproc: break; 76 | case sht::hiproc: break; 77 | } 78 | return "(sht)0x" + to_hex((int)v); 79 | } 80 | 81 | std::string 82 | to_string(shf v) 83 | { 84 | std::string res; 85 | if ((v & shf::write) == shf::write) { res += "write|"; v &= ~shf::write; } 86 | if ((v & shf::alloc) == shf::alloc) { res += "alloc|"; v &= ~shf::alloc; } 87 | if ((v & shf::execinstr) == shf::execinstr) { res += "execinstr|"; v &= ~shf::execinstr; } 88 | if ((v & shf::maskos) == shf::maskos) { res += "maskos|"; v &= ~shf::maskos; } 89 | if ((v & shf::maskproc) == shf::maskproc) { res += "maskproc|"; v &= ~shf::maskproc; } 90 | if (res.empty() || v != (shf)0) res += "(shf)0x" + to_hex((int)v); 91 | else res.pop_back(); 92 | return res; 93 | } 94 | 95 | std::string 96 | to_string(pt v) 97 | { 98 | switch (v) { 99 | case pt::null: return "null"; 100 | case pt::load: return "load"; 101 | case pt::dynamic: return "dynamic"; 102 | case pt::interp: return "interp"; 103 | case pt::note: return "note"; 104 | case pt::shlib: return "shlib"; 105 | case pt::phdr: return "phdr"; 106 | case pt::tls: return "tls"; 107 | case pt::gnu_eh_frame: return "gnu_eh_frame"; 108 | case pt::gnu_stack: return "gnu_stack"; 109 | case pt::gnu_relro: return "gnu_relro"; 110 | case pt::loos: break; 111 | case pt::hios: break; 112 | case pt::loproc: break; 113 | case pt::hiproc: break; 114 | } 115 | return "(pt)0x" + to_hex((int)v); 116 | } 117 | 118 | std::string 119 | to_string(pf v) 120 | { 121 | std::string res; 122 | if ((v & pf::x) == pf::x) { res += "x|"; v &= ~pf::x; } 123 | if ((v & pf::w) == pf::w) { res += "w|"; v &= ~pf::w; } 124 | if ((v & pf::r) == pf::r) { res += "r|"; v &= ~pf::r; } 125 | if ((v & pf::maskos) == pf::maskos) { res += "maskos|"; v &= ~pf::maskos; } 126 | if ((v & pf::maskproc) == pf::maskproc) { res += "maskproc|"; v &= ~pf::maskproc; } 127 | if (res.empty() || v != (pf)0) res += "(pf)0x" + to_hex((int)v); 128 | else res.pop_back(); 129 | return res; 130 | } 131 | 132 | std::string 133 | to_string(stb v) 134 | { 135 | switch (v) { 136 | case stb::local: return "local"; 137 | case stb::global: return "global"; 138 | case stb::weak: return "weak"; 139 | case stb::loos: break; 140 | case stb::hios: break; 141 | case stb::loproc: break; 142 | case stb::hiproc: break; 143 | } 144 | return "(stb)0x" + to_hex((int)v); 145 | } 146 | 147 | std::string 148 | to_string(stt v) 149 | { 150 | switch (v) { 151 | case stt::notype: return "notype"; 152 | case stt::object: return "object"; 153 | case stt::func: return "func"; 154 | case stt::section: return "section"; 155 | case stt::file: return "file"; 156 | case stt::loos: break; 157 | case stt::hios: break; 158 | case stt::loproc: break; 159 | case stt::hiproc: break; 160 | } 161 | return "(stt)0x" + to_hex((int)v); 162 | } 163 | 164 | ELFPP_END_NAMESPACE 165 | -------------------------------------------------------------------------------- /install.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # This script will install bcov along with its software prerequisites capstone 4 | # and unicorn. It does not affect the already installed versions of both libraries. 5 | # 6 | # After successful completion, the tool should be found in ./local/bin/bcov 7 | # and the run-time library in ./local/lib/libbcov-rt.so 8 | # 9 | # To run the tool, you need to properly set the library path 10 | # 11 | # export LD_LIBRARY_PATH="$BCOV_HOME_DIR/local/lib;$BCOV_HOME_DIR/local/usr/lib" 12 | # 13 | 14 | function setup_build() { 15 | export BCOV_HOME_DIR="$PWD" 16 | export PACKAGE_HOME_DIR="${BCOV_HOME_DIR}/packages" 17 | export INSTALL_PREFIX_DIR="${BCOV_HOME_DIR}/local" 18 | 19 | if [[ -d $INSTALL_PREFIX_DIR ]]; then 20 | rm -rf $INSTALL_PREFIX_DIR 21 | fi 22 | 23 | mkdir $INSTALL_PREFIX_DIR 24 | 25 | if [[ -d $PACKAGE_HOME_DIR ]]; then 26 | rm -rf $PACKAGE_HOME_DIR 27 | fi 28 | 29 | mkdir $PACKAGE_HOME_DIR 30 | } 31 | 32 | 33 | function install_capstone() { 34 | git clone -n --single-branch --branch next https://github.com/aquynh/capstone 35 | cd capstone 36 | git checkout -b bcov_artifact c3b4ce1901 37 | echo "building capstone ... " 38 | mkdir build 39 | cd build 40 | cmake -DCMAKE_INSTALL_PREFIX="$INSTALL_PREFIX_DIR" .. && make -j 4 41 | echo "installing capstone ... " 42 | make install 43 | } 44 | 45 | 46 | function install_unicorn() { 47 | git clone -n --single-branch --branch master https://github.com/unicorn-engine/unicorn 48 | cd unicorn 49 | git checkout -b bcov_artifact 536c4e77c4 50 | echo "building unicorn ... " 51 | make -j 4 52 | echo "installing unicorn ... " 53 | make install DESTDIR="$INSTALL_PREFIX_DIR" 54 | } 55 | 56 | 57 | function install_bcov() { 58 | git clone https://github.com/abenkhadra/bcov 59 | cd bcov 60 | echo "building bcov ... " 61 | if [[ -d ./build ]]; then 62 | rm -rf ./build 63 | fi 64 | mkdir build 65 | cd build 66 | # Set the build type to "Debug" for a more verbose output 67 | cmake \ 68 | -DCMAKE_BUILD_TYPE=Release \ 69 | -DCMAKE_INSTALL_PREFIX="$INSTALL_PREFIX_DIR" \ 70 | -DCMAKE_INCLUDE_PATH="$INSTALL_PREFIX_DIR/include/;$INSTALL_PREFIX_DIR/usr/include/" \ 71 | -DCMAKE_LIBRARY_PATH="$INSTALL_PREFIX_DIR/lib/;$INSTALL_PREFIX_DIR/usr/lib/" .. 72 | make -j 4 73 | echo "installing bcov ... " 74 | make install 75 | } 76 | 77 | 78 | setup_build 79 | cd $PACKAGE_HOME_DIR && install_capstone 80 | cd $PACKAGE_HOME_DIR && install_unicorn 81 | cd $PACKAGE_HOME_DIR && install_bcov 82 | 83 | -------------------------------------------------------------------------------- /src/core/BasicBlock.cpp: -------------------------------------------------------------------------------- 1 | /* **************************************************************************** 2 | * Copyright (c) 2018 University of Kaiserslautern. All rights reserved. 3 | * 4 | * This file is distributed under MIT license. See LICENSE.txt for details. 5 | * 6 | * ****************************************************************************/ 7 | /** 8 | * \brief 9 | */ 10 | #include "BasicBlock.hpp" 11 | 12 | namespace bcov { 13 | 14 | BasicBlock::BasicBlock() : 15 | m_kind(BasicBlockKind::kUnknown), 16 | m_idx(0), 17 | m_insts(), 18 | m_byte_size(0) 19 | { 20 | 21 | } 22 | 23 | BasicBlockKind 24 | BasicBlock::kind() const noexcept 25 | { 26 | return m_kind; 27 | } 28 | 29 | void 30 | BasicBlock::kind(BasicBlockKind kind) noexcept 31 | { 32 | m_kind = kind; 33 | } 34 | 35 | BasicBlock::Idx 36 | BasicBlock::id() const noexcept 37 | { 38 | return m_idx; 39 | } 40 | 41 | void 42 | BasicBlock::id(size_t id) noexcept 43 | { 44 | m_idx = (BasicBlock::Idx) id; 45 | } 46 | 47 | addr_t 48 | BasicBlock::address() const noexcept 49 | { 50 | return is_virtual() ? (addr_t) m_kind : m_insts.front().address(); 51 | } 52 | 53 | addr_t 54 | BasicBlock::end() const noexcept 55 | { 56 | return address() + m_byte_size; 57 | } 58 | 59 | size_t 60 | BasicBlock::size() const noexcept 61 | { 62 | return m_insts.size(); 63 | } 64 | 65 | size_t 66 | BasicBlock::byte_size() const noexcept 67 | { 68 | return m_byte_size; 69 | } 70 | 71 | span 72 | BasicBlock::instructions() const noexcept 73 | { 74 | return m_insts; 75 | } 76 | 77 | bool 78 | BasicBlock::is_branching() const noexcept 79 | { 80 | return (m_kind & BasicBlockKind::kBranch) == BasicBlockKind::kBranch; 81 | } 82 | 83 | bool 84 | BasicBlock::is_padding() const noexcept 85 | { 86 | return (m_kind & BasicBlockKind::kPadding) == BasicBlockKind::kPadding; 87 | } 88 | 89 | bool 90 | BasicBlock::is_landing_pad() const noexcept 91 | { 92 | return (m_kind & BasicBlockKind::kLandingPad) == BasicBlockKind::kLandingPad; 93 | } 94 | 95 | bool 96 | BasicBlock::is_fallthrough() const noexcept 97 | { 98 | return (m_kind & BasicBlockKind::kFallthrough) == BasicBlockKind::kFallthrough; 99 | } 100 | 101 | bool 102 | BasicBlock::is_virtual() const noexcept 103 | { 104 | return ((unsigned) m_kind & 0x10U) != 0; 105 | } 106 | 107 | bool 108 | BasicBlock::is_inside(addr_t address) const noexcept 109 | { 110 | return this->address() <= address && address < end(); 111 | } 112 | 113 | } // bcov 114 | -------------------------------------------------------------------------------- /src/core/BasicBlock.hpp: -------------------------------------------------------------------------------- 1 | /* **************************************************************************** 2 | * Copyright (c) 2018 University of Kaiserslautern. All rights reserved. 3 | * 4 | * This file is distributed under MIT license. See LICENSE.txt for details. 5 | * 6 | * ****************************************************************************/ 7 | /** 8 | * \brief 9 | */ 10 | 11 | #pragma once 12 | 13 | #include "core/Common.hpp" 14 | #include "core/MCInst.hpp" 15 | 16 | #define MAX_BASIC_BLOCK_COUNT (0xFFFF) 17 | 18 | namespace bcov { 19 | 20 | enum class BasicBlockKind : uint8_t { 21 | kUnknown = 0x00, 22 | kFallthrough = 0x01, 23 | kBranch = 0x02, 24 | kLandingPad = 0x04, 25 | kPadding = 0x08, // fallthrough bb inside a function to improve alignment 26 | kDangling = 0x28, // fallthrough bb at function end representing UB 27 | kEntry = 0x10, 28 | kExit = 0x12 29 | }; 30 | 31 | static inline BasicBlockKind operator&(BasicBlockKind a, BasicBlockKind b) 32 | { 33 | return (BasicBlockKind) ((uint8_t) a & (uint8_t) b); 34 | } 35 | 36 | static inline BasicBlockKind operator|(BasicBlockKind a, BasicBlockKind b) 37 | { 38 | return (BasicBlockKind) ((uint8_t) a | (uint8_t) b); 39 | } 40 | 41 | class BasicBlock { 42 | friend class FunctionBuilder; 43 | 44 | public: 45 | using Idx = uint16_t; // should be enough for sane binaries 46 | 47 | public: 48 | BasicBlock(); 49 | 50 | BasicBlock(const BasicBlock &other) = default; 51 | 52 | BasicBlock &operator=(const BasicBlock &other) = default; 53 | 54 | BasicBlock(BasicBlock &&other) noexcept = default; 55 | 56 | BasicBlock &operator=(BasicBlock &&other) noexcept = default; 57 | 58 | ~BasicBlock() = default; 59 | 60 | //=============================================== 61 | 62 | BasicBlockKind kind() const noexcept; 63 | 64 | void kind(BasicBlockKind kind) noexcept; 65 | 66 | Idx id() const noexcept; 67 | 68 | void id(size_t id) noexcept; 69 | 70 | addr_t address() const noexcept; 71 | 72 | addr_t end() const noexcept; 73 | 74 | size_t size() const noexcept; 75 | 76 | size_t byte_size() const noexcept; 77 | 78 | span instructions() const noexcept; 79 | 80 | bool is_branching() const noexcept; 81 | 82 | bool is_fallthrough() const noexcept; 83 | 84 | bool is_padding() const noexcept; 85 | 86 | bool is_landing_pad() const noexcept; 87 | 88 | /// a basic block inserted for analysis purposes only 89 | bool is_virtual() const noexcept; 90 | 91 | bool is_inside(addr_t address) const noexcept; 92 | 93 | private: 94 | BasicBlockKind m_kind; 95 | Idx m_idx; 96 | span m_insts; 97 | unsigned short m_byte_size; 98 | }; 99 | 100 | static inline bool operator<(const BasicBlock &a, const BasicBlock &b) 101 | { 102 | return (!a.is_virtual() && !b.is_virtual() && a.address() < b.address()); 103 | } 104 | 105 | /// valid equality iff a and b belong to the same function. 106 | /// for comparison across functions use start addresses 107 | static inline bool operator==(const BasicBlock &a, const BasicBlock &b) 108 | { 109 | return a.id() == b.id(); 110 | } 111 | 112 | static inline bool operator!=(const BasicBlock &a, const BasicBlock &b) 113 | { 114 | return !(a == b); 115 | } 116 | 117 | } // bcov 118 | -------------------------------------------------------------------------------- /src/core/CSInstWrapper.cpp: -------------------------------------------------------------------------------- 1 | /* **************************************************************************** 2 | * Copyright (c) 2018 University of Kaiserslautern. All rights reserved. 3 | * 4 | * This file is distributed under MIT license. See LICENSE.txt for details. 5 | * 6 | * ****************************************************************************/ 7 | /** 8 | * \brief 9 | */ 10 | 11 | #include "CSInstWrapper.hpp" 12 | #include "Common.hpp" 13 | 14 | namespace bcov { 15 | 16 | CSInstWrapper::CSInstWrapper() 17 | { 18 | m_buf = new uint8_t[sizeof(cs_insn) + sizeof(cs_detail)]; 19 | auto inst = (cs_insn *) m_buf; 20 | inst->detail = (cs_detail *) (m_buf + sizeof(cs_insn)); 21 | } 22 | 23 | cs_insn * 24 | CSInstWrapper::get() const noexcept 25 | { 26 | return (cs_insn *) m_buf; 27 | } 28 | 29 | CSInstWrapper::CSInstWrapper(CSInstWrapper &&other) noexcept 30 | { 31 | this->m_buf = other.m_buf; 32 | other.m_buf = nullptr; 33 | } 34 | 35 | CSInstWrapper & 36 | CSInstWrapper::operator=(CSInstWrapper &&other) noexcept 37 | { 38 | this->m_buf = other.m_buf; 39 | other.m_buf = nullptr; 40 | return *this; 41 | } 42 | 43 | CSInstWrapper::~CSInstWrapper() 44 | { 45 | delete[]m_buf; 46 | } 47 | 48 | std::string 49 | to_string(const CSInstWrapper &inst) 50 | { 51 | return sstring(to_hex(inst.get()->address) 52 | + " " + inst.get()->mnemonic + " " + inst.get()->op_str); 53 | } 54 | } //bcov 55 | -------------------------------------------------------------------------------- /src/core/CSInstWrapper.hpp: -------------------------------------------------------------------------------- 1 | /* **************************************************************************** 2 | * Copyright (c) 2018 University of Kaiserslautern. All rights reserved. 3 | * 4 | * This file is distributed under MIT license. See LICENSE.txt for details. 5 | * 6 | * ****************************************************************************/ 7 | /** 8 | * \brief simple wrapper for capstone's cs_insn and cs_detail. 9 | */ 10 | 11 | #pragma once 12 | 13 | #include "Common.hpp" 14 | #include 15 | 16 | namespace bcov { 17 | 18 | class CSInstWrapper { 19 | public: 20 | CSInstWrapper(); 21 | 22 | CSInstWrapper(const CSInstWrapper &other) = delete; 23 | 24 | CSInstWrapper &operator=(const CSInstWrapper &other) = delete; 25 | 26 | CSInstWrapper(CSInstWrapper &&other) noexcept; 27 | 28 | CSInstWrapper &operator=(CSInstWrapper &&other) noexcept; 29 | 30 | ~CSInstWrapper(); 31 | 32 | cs_insn *get() const noexcept; 33 | 34 | private: 35 | uint8_t *m_buf; 36 | }; 37 | 38 | sstring to_string(const CSInstWrapper &inst); 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/core/Common.cpp: -------------------------------------------------------------------------------- 1 | /* **************************************************************************** 2 | * Copyright (c) 2018 University of Kaiserslautern. All rights reserved. 3 | * 4 | * This file is distributed under MIT license. See LICENSE.txt for details. 5 | * 6 | * ****************************************************************************/ 7 | /** 8 | * \brief 9 | */ 10 | 11 | #include "Common.hpp" 12 | #include 13 | 14 | namespace bcov { 15 | 16 | sstring 17 | hex_dump(const void *a, size_t len) 18 | { 19 | std::stringstream stream; 20 | for (unsigned i = 0; i < len; i++) { 21 | stream << std::setfill('0') << std::setw(2) << std::hex 22 | << int(*((uint8_t *) a + i)) << " "; 23 | } 24 | return stream.str(); 25 | } 26 | } // bcov 27 | -------------------------------------------------------------------------------- /src/core/Common.hpp: -------------------------------------------------------------------------------- 1 | /* **************************************************************************** 2 | * Copyright (c) 2018 University of Kaiserslautern. All rights reserved. 3 | * 4 | * This file is distributed under MIT license. See LICENSE.txt for details. 5 | * 6 | * ****************************************************************************/ 7 | /** 8 | * \brief 9 | */ 10 | 11 | #pragma once 12 | 13 | #include 14 | #include 15 | #include "gsl/gsl-lite.hpp" 16 | 17 | #define UNUSED(x) (void)(x) 18 | 19 | namespace bcov { 20 | 21 | #define BCOV_FORWARD(OBJ) \ 22 | namespace bcov { \ 23 | class OBJ; \ 24 | } 25 | 26 | #define BCOV_FORWARD_T(OBJ) \ 27 | namespace bcov { \ 28 | template \ 29 | class OBJ; \ 30 | } 31 | 32 | #ifdef NDEBUG 33 | #define BCOV_DEBUG(x) void() 34 | #else 35 | #define BCOV_DEBUG(x) x 36 | #endif 37 | 38 | #ifdef NDEBUG 39 | #define BCOV_UNREACHABLE __builtin_unreachable(); 40 | #else 41 | #define BCOV_UNREACHABLE assert("unreachable!"); 42 | #endif 43 | 44 | using addr_t = uint64_t; 45 | 46 | using uoffset_t = uint64_t; 47 | 48 | using soffset_t = int64_t; 49 | 50 | using buffer_t = const uint8_t *; 51 | 52 | using czstring = gsl::czstring; 53 | 54 | using sstring = std::string; 55 | 56 | using sstring_view = gsl::cstring_span; 57 | 58 | template using span = gsl::span; 59 | 60 | template 61 | std::string to_hex(T v); 62 | 63 | sstring hex_dump(const void *a, size_t len); 64 | 65 | template 66 | std::string 67 | to_hex(T v) 68 | { 69 | // copied from libelfin/elf/to_hex.hh 70 | static_assert(std::is_integral::value, 71 | "to_hex applied to non-integral type"); 72 | if (v == 0) 73 | return std::string("0"); 74 | char buf[sizeof(T) * 2 + 1]; 75 | char *pos = &buf[sizeof(buf) - 1]; 76 | *pos-- = '\0'; 77 | while (v && pos >= buf) { 78 | int digit = v & 0xf; 79 | if (digit < 10) 80 | *pos = '0' + digit; 81 | else 82 | *pos = 'a' + (digit - 10); 83 | pos--; 84 | v >>= 4; 85 | } 86 | return std::string(pos + 1); 87 | } 88 | 89 | template 90 | constexpr auto to_integral(T e) -> typename std::underlying_type::type 91 | { 92 | return static_cast::type>(e); 93 | } 94 | 95 | template 96 | typename T::iterator get_forward_iter(T &vec, typename T::reverse_iterator rev_it) 97 | { 98 | return vec.begin() + (vec.rend() - rev_it) - 1; 99 | } 100 | 101 | template 102 | typename T::reverse_iterator get_reverse_iter(T &vec, typename T::iterator fwd_it) 103 | { 104 | return vec.rbegin() + (vec.end() - fwd_it) - 1; 105 | } 106 | 107 | } // bcov 108 | -------------------------------------------------------------------------------- /src/core/Disassembler.cpp: -------------------------------------------------------------------------------- 1 | /* **************************************************************************** 2 | * Copyright (c) 2018 University of Kaiserslautern. All rights reserved. 3 | * 4 | * This file is distributed under MIT license. See LICENSE.txt for details. 5 | * 6 | * ****************************************************************************/ 7 | /** 8 | * \brief 9 | */ 10 | 11 | #include 12 | #include 13 | #include "Disassembler.hpp" 14 | 15 | namespace bcov { 16 | 17 | Disassembler::Disassembler() : m_disasm(0) 18 | { } 19 | 20 | void 21 | Disassembler::init(DisasmArch arch, DisasmMode mode) 22 | { 23 | cs_err err = cs_open((cs_arch) arch, (cs_mode) mode, &m_disasm); 24 | if (err != CS_ERR_OK) { 25 | throw std::runtime_error("failed to initialize capstone!"); 26 | } 27 | cs_option(m_disasm, CS_OPT_DETAIL, CS_OPT_ON); 28 | if (arch == DisasmArch::kX86) { 29 | cs_option(m_disasm, CS_OPT_SYNTAX, CS_OPT_SYNTAX_INTEL); 30 | } 31 | } 32 | 33 | bool 34 | Disassembler::is_valid() const noexcept 35 | { 36 | return m_disasm != 0; 37 | } 38 | 39 | csh 40 | Disassembler::get() const noexcept 41 | { 42 | return m_disasm; 43 | } 44 | 45 | Disassembler::~Disassembler() 46 | { 47 | if (m_disasm != 0) { 48 | cs_close(&m_disasm); 49 | } 50 | } 51 | } // bcov 52 | -------------------------------------------------------------------------------- /src/core/Disassembler.hpp: -------------------------------------------------------------------------------- 1 | /* **************************************************************************** 2 | * Copyright (c) 2018 University of Kaiserslautern. All rights reserved. 3 | * 4 | * This file is distributed under MIT license. See LICENSE.txt for details. 5 | * 6 | * ****************************************************************************/ 7 | /** 8 | * \brief 9 | */ 10 | 11 | #pragma once 12 | 13 | #include "Common.hpp" 14 | 15 | namespace bcov { 16 | 17 | // Capstone's handle used with all API 18 | typedef size_t csh; 19 | 20 | // following enums are consistent with their corresponding ones in capstone 21 | enum class DisasmArch { 22 | kARM = 0, // ARM architecture (including Thumb, Thumb-2) 23 | kARM64, // ARM-64, also called AArch64 24 | KMIPS, // Mips architecture 25 | kX86, // X86 architecture (including x86 & x86-64) 26 | kPPC, // PowerPC architecture 27 | kSPARC, // Sparc architecture 28 | kSYSZ, // SystemZ architecture 29 | kXCORE, // XCore architecture 30 | }; 31 | 32 | enum class DisasmMode { 33 | kLITTLE_ENDIAN = 0, // little-endian mode (default mode) 34 | kARM = 0, // 32-bit ARM 35 | k16 = 1U << 1U, // 16-bit mode (X86) 36 | k32 = 1U << 2U, // 32-bit mode (X86) 37 | k64 = 1U << 3U, // 64-bit mode (X86, PPC) 38 | kTHUMB = 1U << 4U, // ARM's Thumb mode, including Thumb-2 39 | kMCLASS = 1U << 5U, // ARM's Cortex-M series 40 | kV8 = 1U << 6U, // ARMv8 A32 encodings for ARM 41 | kMICRO = 1U << 4U, // MicroMips mode (MIPS) 42 | kMIPS3 = 1U << 5U, // Mips III ISA 43 | kMIPS32R6 = 1U << 6U, // Mips32r6 ISA 44 | kMIPS2 = 1U << 7U, // Mips II ISA 45 | kV9 = 1U << 4U, // SparcV9 mode (Sparc) 46 | kQPX = 1U << 4U // Quad Processing eXtensions mode (PPC) 47 | }; 48 | 49 | class Disassembler { 50 | public: 51 | 52 | Disassembler(); 53 | 54 | Disassembler(const Disassembler &other) = default; 55 | 56 | Disassembler &operator=(const Disassembler &other) = default; 57 | 58 | Disassembler(Disassembler &&other) noexcept = default; 59 | 60 | Disassembler &operator=(Disassembler &&other) noexcept = default; 61 | 62 | void init(DisasmArch arch, DisasmMode mode); 63 | 64 | bool is_valid() const noexcept; 65 | 66 | csh get() const noexcept; 67 | 68 | ~Disassembler(); 69 | 70 | private: 71 | csh m_disasm; 72 | }; 73 | 74 | } // bcov 75 | -------------------------------------------------------------------------------- /src/core/FileLoader.cpp: -------------------------------------------------------------------------------- 1 | /* **************************************************************************** 2 | * Copyright (c) 2013 Austin T. Clements. All rights reserved. 3 | * 4 | * Copyright (c) 2018 University of Kaiserslautern. All rights reserved. 5 | * 6 | * This file is distributed under MIT license. See LICENSE.txt for details. 7 | * 8 | * ****************************************************************************/ 9 | /** 10 | * \brief 11 | */ 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include "FileLoader.hpp" 20 | 21 | namespace bcov { 22 | 23 | using namespace std; 24 | 25 | class MMAPLoader : public FileLoader { 26 | public: 27 | MMAPLoader(int fd, FileAccess access) 28 | { 29 | off_t end = lseek(fd, 0, SEEK_END); 30 | if (end == (off_t) -1) 31 | throw std::system_error(errno, system_category(), 32 | "finding file length"); 33 | m_limit = (size_t) end; 34 | 35 | int prot = (access == FileAccess::kRO) ? 36 | PROT_READ : PROT_READ | PROT_WRITE; 37 | 38 | m_base = mmap(nullptr, m_limit, prot, MAP_SHARED, fd, 0); 39 | if (m_base == MAP_FAILED) 40 | throw std::system_error(errno, system_category(), 41 | "mmap'ing file "); 42 | } 43 | 44 | ~MMAPLoader() override 45 | { 46 | munmap(m_base, m_limit); 47 | } 48 | 49 | const void *load(off_t offset, size_t size) override 50 | { 51 | if (offset + size > m_limit) { 52 | throw std::range_error("offset exceeds file size"); 53 | } 54 | return (char *) (m_base) + offset; 55 | } 56 | 57 | size_t size() const noexcept override 58 | { 59 | return m_limit; 60 | } 61 | 62 | const void *base() const noexcept override 63 | { 64 | return m_base; 65 | } 66 | 67 | private: 68 | void *m_base; 69 | size_t m_limit; 70 | }; 71 | 72 | FileLoader::MMapedFile 73 | FileLoader::create(sstring_view file_path, FileAccess mode) 74 | { 75 | FileAccessor accessor; 76 | accessor.open(file_path, mode); 77 | return make_shared(accessor.fd(), mode); 78 | } 79 | 80 | FileAccessor::~FileAccessor() 81 | { 82 | if (m_fd >= 0) { 83 | ::close(m_fd); 84 | } 85 | } 86 | 87 | void FileAccessor::open(sstring_view file_path, FileAccess mode) 88 | { 89 | int flags = (mode == FileAccess::kRO) ? O_RDONLY : O_RDWR; 90 | m_fd = ::open(file_path.data(), flags); 91 | if (m_fd < 0) { 92 | throw std::system_error(errno, system_category(), 93 | "error opening file: " + to_string(file_path)); 94 | } 95 | } 96 | 97 | int FileAccessor::fd() 98 | { 99 | return m_fd; 100 | } 101 | 102 | } // bcov 103 | -------------------------------------------------------------------------------- /src/core/FileLoader.hpp: -------------------------------------------------------------------------------- 1 | /* **************************************************************************** 2 | * Copyright (c) 2018 University of Kaiserslautern. All rights reserved. 3 | * 4 | * This file is distributed under MIT license. See LICENSE.txt for details. 5 | * 6 | * ****************************************************************************/ 7 | /** 8 | * \brief 9 | */ 10 | 11 | #pragma once 12 | 13 | #include "libelfin/elf/elf++.hh" 14 | #include "Common.hpp" 15 | 16 | namespace bcov { 17 | 18 | enum class FileAccess { 19 | kRO, 20 | kRW, 21 | }; 22 | 23 | class FileAccessor { 24 | public: 25 | FileAccessor() = default; 26 | 27 | ~FileAccessor(); 28 | 29 | void open(sstring_view file_path, FileAccess mode); 30 | 31 | int fd(); 32 | 33 | private: 34 | int m_fd = -1; 35 | }; 36 | 37 | class FileLoader : public elf::loader { 38 | public: 39 | 40 | using MMapedFile = std::shared_ptr; 41 | 42 | ~FileLoader() override = default; 43 | 44 | const void *load(off_t offset, size_t size) override = 0; 45 | 46 | virtual size_t size() const noexcept = 0; 47 | 48 | virtual const void *base() const noexcept = 0; 49 | 50 | static MMapedFile create(sstring_view file_path, FileAccess mode); 51 | }; 52 | 53 | } // bcov 54 | -------------------------------------------------------------------------------- /src/core/FunctionBuilder.hpp: -------------------------------------------------------------------------------- 1 | /* **************************************************************************** 2 | * Copyright (c) 2018 University of Kaiserslautern. All rights reserved. 3 | * 4 | * This file is distributed under MIT license. See LICENSE.txt for details. 5 | * 6 | * ****************************************************************************/ 7 | /** 8 | * \brief 9 | */ 10 | 11 | #pragma once 12 | 13 | #include 14 | #include "Common.hpp" 15 | #include "Function.hpp" 16 | #include "elf/ElfModule.hpp" // XXX: breaks modularity 17 | #include "flax/Flax.hpp" 18 | #include "util/BcovConfig.hpp" 19 | 20 | 21 | BCOV_FORWARD(CSInstWrapper) 22 | 23 | namespace bcov { 24 | 25 | class FunctionBuilder { 26 | public: 27 | 28 | FunctionBuilder(); 29 | 30 | FunctionBuilder(const FunctionBuilder &other) = default; 31 | 32 | FunctionBuilder &operator=(const FunctionBuilder &other) = default; 33 | 34 | FunctionBuilder(FunctionBuilder &&other) noexcept = default; 35 | 36 | FunctionBuilder &operator=(FunctionBuilder &&other) noexcept = default; 37 | 38 | virtual ~FunctionBuilder() = default; 39 | 40 | void set_build_dominator_trees(); 41 | 42 | void set_function_info(IFunction::Idx idx, sstring_view name, addr_t address, 43 | size_t byte_size, const uint8_t *data); 44 | 45 | IFunction 46 | build(const ElfModule *module, flax::FlaxManager *microx_mgr = nullptr); 47 | 48 | protected: 49 | const MCInst *add_instruction(const CSInstWrapper *cs_inst); 50 | 51 | void build_instructions_and_basic_block_entries(std::set &bb_entries); 52 | 53 | void build_basic_blocks(const std::set &bb_entries); 54 | 55 | void build_cfg(const ElfModule *module); 56 | 57 | void build_jump_tabs_and_update_cfg(const ElfModule *module, 58 | std::set &bb_entries); 59 | 60 | void finalize_cfg(const ElfModule *module, 61 | const ElfFunction::LandingPads &landing_pads); 62 | 63 | void cfg_link_landing_pads(const ElfFunction::LandingPads &landing_pads); 64 | 65 | void cfg_link_setjmps(const ElfModule *module); 66 | 67 | void build_dominator_trees(); 68 | 69 | private: 70 | struct Impl; 71 | std::shared_ptr m_impl; 72 | }; 73 | 74 | class FunctionBuilderException : public std::logic_error { 75 | public: 76 | explicit FunctionBuilderException(const std::string &what_arg); 77 | 78 | explicit FunctionBuilderException(const char *what_arg); 79 | 80 | ~FunctionBuilderException() override = default; 81 | }; 82 | 83 | } // bcov 84 | -------------------------------------------------------------------------------- /src/core/MCInst.cpp: -------------------------------------------------------------------------------- 1 | /* **************************************************************************** 2 | * Copyright (c) 2018 University of Kaiserslautern. All rights reserved. 3 | * 4 | * This file is distributed under MIT license. See LICENSE.txt for details. 5 | * 6 | * ****************************************************************************/ 7 | /** 8 | * \brief 9 | */ 10 | #include "MCInst.hpp" 11 | 12 | namespace bcov { 13 | 14 | MCInst::MCInst(uint32_t idx, uint32_t cs_id, addr_t addr, uint16_t size, 15 | MCInstAttr attr) : 16 | m_idx(idx), m_csid(cs_id), m_addr(addr), m_size(size), m_attr(attr) 17 | { } 18 | 19 | sstring_view 20 | MCInst::text() const noexcept 21 | { 22 | return m_text; 23 | } 24 | 25 | sstring 26 | to_string(const MCInst &inst) 27 | { 28 | return to_hex(inst.address()) + " " + inst.text().data(); 29 | } 30 | 31 | } //bcov 32 | -------------------------------------------------------------------------------- /src/core/MCInst.hpp: -------------------------------------------------------------------------------- 1 | /* **************************************************************************** 2 | * Copyright (c) 2018 University of Kaiserslautern. All rights reserved. 3 | * 4 | * This file is distributed under MIT license. See LICENSE.txt for details. 5 | * 6 | * ****************************************************************************/ 7 | /** 8 | * \brief 9 | */ 10 | 11 | #pragma once 12 | 13 | #include "Common.hpp" 14 | 15 | namespace bcov { 16 | 17 | class MCInst; 18 | 19 | using MCInstId = uint32_t; 20 | using MCInstPtr = const MCInst *; 21 | using MCInstPtrVec =std::vector; 22 | 23 | enum class MCInstAttr : uint16_t { 24 | kNone = 0U, // uninitialized or ordinary instruction 25 | kJump = 1U << 0U, // all jump instructions (conditional+direct+indirect jumps) 26 | kCall = 1U << 1U, // all call instructions 27 | kReturn = 1U << 2U, // all return instructions 28 | kInterrupt = 1U << 3U, // all interrupt instructions 29 | kRelative = 1U << 4U, // explicitly or implicitly depend on program counter (rip) 30 | kPrivilege = 1U << 5U, // all privileged instructions 31 | kDirect = 1U << 6U, // instruction has one immediate operand 32 | kCond = 1U << 7U, // all conditional instructions 33 | kPCRel = 1U << 8U, // explicitly accesses program counter (rip) 34 | kIntRet = 1U << 9U // interrupt return 35 | }; 36 | 37 | static inline MCInstAttr operator&(MCInstAttr a, MCInstAttr b) 38 | { 39 | return (MCInstAttr) (static_cast(a) & static_cast(b)); 40 | } 41 | 42 | static inline MCInstAttr operator|(MCInstAttr a, MCInstAttr b) 43 | { 44 | return (MCInstAttr) (static_cast(a) | static_cast(b)); 45 | } 46 | 47 | static inline void operator|=(MCInstAttr &a, MCInstAttr b) 48 | { 49 | a = a | b; 50 | } 51 | 52 | static inline void operator&=(MCInstAttr &a, MCInstAttr b) 53 | { 54 | a = a & b; 55 | } 56 | 57 | class MCInst { 58 | friend class FunctionBuilder; 59 | 60 | public: 61 | 62 | MCInst() = default; 63 | 64 | MCInst(const MCInst &other) = default; 65 | 66 | MCInst &operator=(const MCInst &other) = default; 67 | 68 | MCInst(MCInst &&other) noexcept = default; 69 | 70 | MCInst &operator=(MCInst &&other) noexcept = default; 71 | 72 | ~MCInst() = default; 73 | 74 | // operator overloads 75 | bool operator<(const MCInst &other) const noexcept 76 | { 77 | return m_addr < other.m_addr; 78 | } 79 | 80 | bool operator==(const MCInst &other) const noexcept 81 | { 82 | return m_addr == other.m_addr; 83 | } 84 | 85 | // public methods 86 | /// capstone's instruction identifier 87 | inline unsigned cs_id() const noexcept 88 | { 89 | return m_csid; 90 | } 91 | 92 | /// unique instruction identifier inside a function 93 | inline MCInstId id() const noexcept 94 | { 95 | return m_idx; 96 | } 97 | 98 | inline addr_t address() const noexcept 99 | { 100 | return m_addr; 101 | } 102 | 103 | inline addr_t end() const noexcept 104 | { 105 | return m_addr + m_size; 106 | } 107 | 108 | inline uint16_t size() const noexcept 109 | { 110 | return m_size; 111 | } 112 | 113 | inline MCInstAttr kind() const noexcept 114 | { 115 | return m_attr; 116 | } 117 | 118 | inline bool is_branch() const noexcept 119 | { 120 | return ((unsigned) m_attr & 0xFU) != 0; 121 | } 122 | 123 | inline bool is_jump() const noexcept 124 | { 125 | return (m_attr & MCInstAttr::kJump) == MCInstAttr::kJump; 126 | } 127 | 128 | inline bool is_call() const noexcept 129 | { 130 | return (m_attr & MCInstAttr::kCall) == MCInstAttr::kCall; 131 | } 132 | 133 | inline bool is_interrupt() const noexcept 134 | { 135 | return (m_attr & MCInstAttr::kInterrupt) == MCInstAttr::kInterrupt; 136 | } 137 | 138 | inline bool is_return() const noexcept 139 | { 140 | return (m_attr & MCInstAttr::kReturn) == MCInstAttr::kReturn; 141 | } 142 | 143 | inline bool is_conditional() const noexcept 144 | { 145 | return (m_attr & MCInstAttr::kCond) == MCInstAttr::kCond; 146 | } 147 | 148 | inline bool is_direct() const noexcept 149 | { 150 | return (m_attr & MCInstAttr::kDirect) == MCInstAttr::kDirect; 151 | } 152 | 153 | inline bool is_relative() const noexcept 154 | { 155 | return (m_attr & MCInstAttr::kRelative) == MCInstAttr::kRelative; 156 | } 157 | 158 | inline bool is_pc_relative() const noexcept 159 | { 160 | return (m_attr & MCInstAttr::kPCRel) == MCInstAttr::kPCRel; 161 | } 162 | 163 | sstring_view text() const noexcept; 164 | 165 | protected: 166 | 167 | MCInst(MCInstId idx, uint32_t cs_id, addr_t addr, uint16_t size, 168 | MCInstAttr attr); 169 | 170 | private: 171 | MCInstId m_idx; 172 | uint32_t m_csid; // capstone's instruction id 173 | addr_t m_addr; // virtual address 174 | uint16_t m_size; // size in bytes 175 | MCInstAttr m_attr; 176 | sstring m_text; 177 | }; 178 | 179 | sstring to_string(const MCInst &inst); 180 | 181 | } //bcov 182 | 183 | namespace std { 184 | 185 | template<> 186 | struct hash { 187 | auto operator()(const bcov::MCInst &x) const noexcept 188 | { 189 | return std::hash{}(x.address()); 190 | } 191 | }; 192 | 193 | } 194 | -------------------------------------------------------------------------------- /src/core/Region.cpp: -------------------------------------------------------------------------------- 1 | /* **************************************************************************** 2 | * Copyright (c) 2018 University of Kaiserslautern. All rights reserved. 3 | * 4 | * This file is distributed under MIT license. See LICENSE.txt for details. 5 | * 6 | * ****************************************************************************/ 7 | /** 8 | * \brief 9 | */ 10 | 11 | #include 12 | #include "Region.hpp" 13 | 14 | namespace bcov { 15 | 16 | sstring 17 | to_string(const FileRegion ®ion) 18 | { 19 | return "pos:" + to_hex((addr_t) region.base_pos) + 20 | ", size:" + to_hex(region.size); 21 | } 22 | 23 | //=============================================== 24 | 25 | MemoryRegion::MemoryRegion(buffer_t pos, addr_t addr, size_t size) : 26 | m_base_pos(pos), 27 | m_base_address(addr), 28 | m_size(size) 29 | { } 30 | 31 | sstring 32 | to_string(const MemoryRegion ®ion) 33 | { 34 | return "base_pos:" + to_hex((addr_t) region.base_pos()) + 35 | ", base_addr:" + to_hex(region.base_address()) + 36 | ", size:" + to_hex(region.size()); 37 | } 38 | 39 | //=============================================== 40 | 41 | void 42 | MMappedFileRegion::init(void *base_pos, addr_t base_addr, size_t size) 43 | { 44 | this->m_base_pos = (ptr_type) base_pos; 45 | this->m_cur_pos = (ptr_type) base_pos; 46 | this->m_base_address = base_addr; 47 | this->m_size = size; 48 | } 49 | 50 | void 51 | MMappedFileRegion::write(const void *buf, size_t size) 52 | { 53 | std::memcpy(m_cur_pos, buf, size); 54 | m_cur_pos += size; 55 | } 56 | 57 | sstring 58 | to_string(const MMappedFileRegion ®ion) 59 | { 60 | return "base_pos:" + to_hex((addr_t) region.m_base_pos) + 61 | ", cur_pos:" + to_hex((addr_t) region.m_cur_pos) + 62 | ", base_addr:" + to_hex((addr_t) region.m_base_address) + 63 | ", size:" + to_hex(region.m_size); 64 | } 65 | 66 | } // bcov 67 | -------------------------------------------------------------------------------- /src/core/Region.hpp: -------------------------------------------------------------------------------- 1 | /* **************************************************************************** 2 | * Copyright (c) 2018 University of Kaiserslautern. All rights reserved. 3 | * 4 | * This file is distributed under MIT license. See LICENSE.txt for details. 5 | * 6 | * ****************************************************************************/ 7 | /** 8 | * \brief 9 | */ 10 | 11 | #pragma once 12 | 13 | #include "Common.hpp" 14 | 15 | namespace bcov { 16 | 17 | // XXX permissions are consistent with that of unicorn, which deviates from the common 18 | // Linux convention where, e.g., X = 1 19 | enum class Permissions : uint8_t { 20 | None = 0x0, 21 | R = 0x1, 22 | W = 0x2, 23 | X = 0x4, 24 | All = 0x7 25 | }; 26 | 27 | constexpr size_t kTaggedPermMaskSize = (sizeof(size_t) * 8 - 3); 28 | 29 | constexpr size_t kTaggedPermMask = ((size_t) 7) << kTaggedPermMaskSize; 30 | 31 | constexpr size_t kTaggedSizeMask = ~kTaggedPermMask; 32 | 33 | static inline Permissions get_permissions(size_t tagged_size) 34 | { 35 | return (Permissions) (tagged_size >> kTaggedPermMaskSize); 36 | } 37 | 38 | static inline size_t get_real_size(size_t tagged_size) 39 | { 40 | return tagged_size & kTaggedSizeMask; 41 | } 42 | 43 | static inline size_t set_permissions(size_t size, Permissions perm) 44 | { 45 | return get_real_size(size) | (((size_t) perm) << kTaggedPermMaskSize); 46 | } 47 | 48 | static inline size_t set_real_size(size_t tagged_size, size_t size) 49 | { 50 | return (tagged_size & kTaggedPermMask) | size; 51 | } 52 | 53 | static inline Permissions operator&(Permissions a, Permissions b) 54 | { 55 | return (Permissions) ((unsigned) a & ((unsigned) b)); 56 | } 57 | 58 | static inline Permissions operator|(Permissions a, Permissions b) 59 | { 60 | return (Permissions) ((unsigned) a | ((unsigned) b)); 61 | } 62 | 63 | static inline bool has_read(Permissions perm) 64 | { 65 | return (perm & Permissions::R) == Permissions::R; 66 | } 67 | 68 | static inline bool has_write(Permissions perm) 69 | { 70 | return (perm & Permissions::W) == Permissions::W; 71 | } 72 | 73 | static inline bool has_exec(Permissions perm) 74 | { 75 | return (perm & Permissions::X) == Permissions::X; 76 | } 77 | 78 | //============================================================================== 79 | 80 | struct FileRegion { 81 | 82 | inline int64_t offset(const char *pos) 83 | { 84 | return pos - base_pos; 85 | } 86 | 87 | inline bool is_inside(const char *pos) 88 | { 89 | return base_pos <= pos && pos < (base_pos + size); 90 | } 91 | 92 | const char *base_pos = nullptr; 93 | size_t size = 0; 94 | }; 95 | 96 | sstring to_string(const FileRegion ®ion); 97 | 98 | //=============================================== 99 | 100 | struct MMappedFileRegion { 101 | 102 | using ptr_type = uint8_t *; 103 | using const_ptr_type = const uint8_t *; 104 | 105 | inline addr_t get_address(const_ptr_type pos) const 106 | { 107 | return m_base_address + (pos - m_base_pos); 108 | } 109 | 110 | inline ptr_type get_pos(addr_t address) const 111 | { 112 | return m_base_pos + (address - m_base_address); 113 | } 114 | 115 | inline int64_t offset(addr_t address) const 116 | { 117 | return address - m_base_address; 118 | } 119 | 120 | inline int64_t offset(const_ptr_type pos) const 121 | { 122 | return pos - m_base_pos; 123 | } 124 | 125 | inline bool is_inside(addr_t address) const 126 | { 127 | return m_base_address <= address && address < (m_base_address + m_size); 128 | } 129 | 130 | inline bool is_inside(const_ptr_type pos) const 131 | { 132 | return m_base_pos <= pos && pos < (m_base_pos + m_size); 133 | } 134 | 135 | inline addr_t current_address() const 136 | { 137 | return m_base_address + offset(m_cur_pos); 138 | } 139 | 140 | ptr_type current_pos() const 141 | { 142 | return m_cur_pos; 143 | } 144 | 145 | void seekp(soffset_t off) 146 | { 147 | m_cur_pos += off; 148 | } 149 | 150 | void seekp(ptr_type pos) 151 | { 152 | m_cur_pos = pos; 153 | } 154 | 155 | addr_t base_address() const 156 | { 157 | return m_base_address; 158 | } 159 | 160 | ptr_type base_pos() const 161 | { 162 | return m_base_pos; 163 | } 164 | 165 | bool valid() const noexcept 166 | { 167 | return m_base_pos != nullptr; 168 | } 169 | 170 | size_t size() const noexcept 171 | { 172 | return m_size; 173 | } 174 | 175 | void init(void *base_pos, addr_t base_addr, size_t size); 176 | 177 | void write(const void *buf, size_t size); 178 | 179 | friend sstring to_string(const MMappedFileRegion ®ion); 180 | 181 | private: 182 | ptr_type m_base_pos = nullptr; 183 | ptr_type m_cur_pos; 184 | addr_t m_base_address; 185 | size_t m_size; 186 | }; 187 | 188 | sstring to_string(const MMappedFileRegion ®ion); 189 | 190 | //=============================================== 191 | 192 | class MemoryRegion { 193 | public: 194 | MemoryRegion() = default; 195 | 196 | MemoryRegion(buffer_t pos, addr_t addr, size_t size); 197 | 198 | inline int64_t offset(buffer_t pos) const 199 | { 200 | return pos - m_base_pos; 201 | } 202 | 203 | inline bool is_inside(buffer_t pos) const 204 | { 205 | return m_base_pos <= pos and pos < (m_base_pos + size()); 206 | } 207 | 208 | inline bool is_inside(addr_t address) const 209 | { 210 | return m_base_address <= address && address < (m_base_address + size()); 211 | } 212 | 213 | inline buffer_t get_buffer(addr_t address) const 214 | { 215 | return m_base_pos + (address - m_base_address); 216 | } 217 | 218 | inline addr_t get_address(buffer_t pos) const 219 | { 220 | return m_base_address + (pos - m_base_pos); 221 | } 222 | 223 | inline buffer_t base_pos() const 224 | { 225 | return m_base_pos; 226 | } 227 | 228 | inline addr_t base_address() const 229 | { 230 | return m_base_address; 231 | } 232 | 233 | inline size_t size() const 234 | { 235 | return get_real_size(m_size); 236 | } 237 | 238 | inline Permissions permissions() const 239 | { 240 | return get_permissions(m_size); 241 | } 242 | 243 | inline bool valid() const 244 | { 245 | return m_base_pos != nullptr; 246 | } 247 | 248 | inline void base_pos(buffer_t buffer) 249 | { 250 | m_base_pos = buffer; 251 | } 252 | 253 | inline void base_address(addr_t address) 254 | { 255 | m_base_address = address; 256 | } 257 | 258 | inline void size(size_t size) 259 | { 260 | m_size = set_real_size(m_size, size); 261 | } 262 | 263 | inline void permissions(Permissions perm) 264 | { 265 | m_size = set_permissions(m_size, perm); 266 | } 267 | 268 | private: 269 | buffer_t m_base_pos = nullptr; 270 | addr_t m_base_address; 271 | size_t m_size; 272 | }; 273 | 274 | class MemoryArea { 275 | public: 276 | MemoryArea() = default; 277 | 278 | inline addr_t start() const 279 | { 280 | return m_start_address; 281 | } 282 | 283 | inline addr_t end() const 284 | { 285 | return m_start_address + m_size; 286 | } 287 | 288 | inline size_t size() const 289 | { 290 | return m_size; 291 | } 292 | 293 | inline bool valid() const 294 | { 295 | return m_start_address != 0; 296 | } 297 | 298 | inline void start(addr_t address) 299 | { 300 | m_start_address = address; 301 | } 302 | 303 | inline void size(size_t size) 304 | { 305 | m_size = size; 306 | } 307 | 308 | private: 309 | addr_t m_start_address = 0; 310 | size_t m_size; 311 | }; 312 | 313 | static inline bool operator==(const MemoryRegion &a, const MemoryRegion &b) 314 | { 315 | return a.base_address() == b.base_address() && a.size() == b.size(); 316 | } 317 | 318 | static inline bool operator==(const MemoryArea &a, const MemoryArea &b) 319 | { 320 | return a.start() == b.start() && a.end() == b.end(); 321 | } 322 | 323 | sstring to_string(const MemoryRegion ®ion); 324 | 325 | } // bcov 326 | 327 | -------------------------------------------------------------------------------- /src/dump/patch.c: -------------------------------------------------------------------------------- 1 | /* **************************************************************************** 2 | * Copyright (c) 2018 University of Kaiserslautern. All rights reserved. 3 | * 4 | * This file is distributed under MIT license. See LICENSE.txt for details. 5 | * 6 | * ****************************************************************************/ 7 | /** 8 | * \brief 9 | */ 10 | 11 | #include 12 | #include 13 | #include 14 | #include "patch.h" 15 | 16 | #ifdef __cplusplus 17 | extern "C" { 18 | namespace bcov { 19 | #endif 20 | 21 | #define BCOV_BASE_ADDR_OFFSET (BCOV_DATA_MAGIC_SIZE) 22 | #define BCOV_PROBE_COUNT_OFFSET (BCOV_BASE_ADDR_OFFSET + 8) 23 | #define BCOV_PID_OFFSET (BCOV_PROBE_COUNT_OFFSET + 4) 24 | 25 | const uint8_t kBcovDataSegMagic[BCOV_DATA_MAGIC_SIZE] = 26 | {0x2E, 0x42, 0x43, 0x4F, 0x56, 0x55, 0x55, 0x55}; // ".BCOV***" 27 | 28 | void 29 | bcov_write_base_address(uint8_t *begin, uint64_t address) 30 | { 31 | *((uint64_t *) (begin + BCOV_BASE_ADDR_OFFSET)) = address; 32 | } 33 | 34 | uint64_t 35 | bcov_read_base_address(const uint8_t *begin) 36 | { 37 | return *((const uint64_t *) (begin + BCOV_BASE_ADDR_OFFSET)); 38 | } 39 | 40 | void 41 | bcov_write_probe_count(uint8_t *begin, size_t probe_count) 42 | { 43 | *((uint32_t *) (begin + BCOV_PROBE_COUNT_OFFSET)) = (uint32_t) probe_count; 44 | assert(probe_count == bcov_read_probe_count(begin)); 45 | } 46 | 47 | size_t 48 | bcov_read_probe_count(const uint8_t *begin) 49 | { 50 | return *((const uint32_t *) (begin + BCOV_PROBE_COUNT_OFFSET)); 51 | } 52 | 53 | void 54 | bcov_write_magic(uint8_t *begin) 55 | { 56 | memcpy(begin, kBcovDataSegMagic, BCOV_DATA_MAGIC_SIZE); 57 | } 58 | 59 | bool 60 | bcov_has_valid_magic(const uint8_t *begin) 61 | { 62 | return memcmp(begin, kBcovDataSegMagic, BCOV_DATA_MAGIC_SIZE) == 0; 63 | } 64 | 65 | const uint8_t * 66 | bcov_get_magic_data() 67 | { 68 | return kBcovDataSegMagic; 69 | } 70 | 71 | void 72 | bcov_write_process_id(uint8_t *begin) 73 | { 74 | int id = getpid(); 75 | *((int32_t *) (begin + BCOV_PID_OFFSET)) = id; 76 | } 77 | 78 | int 79 | bcov_read_process_id(const uint8_t *begin) 80 | { 81 | return *((const int32_t *) (begin + BCOV_PID_OFFSET)); 82 | } 83 | 84 | #ifdef __cplusplus 85 | } // bcov 86 | } 87 | #endif 88 | -------------------------------------------------------------------------------- /src/dump/patch.h: -------------------------------------------------------------------------------- 1 | /* **************************************************************************** 2 | * Copyright (c) 2018 University of Kaiserslautern. All rights reserved. 3 | * 4 | * This file is distributed under MIT license. See LICENSE.txt for details. 5 | * 6 | * ****************************************************************************/ 7 | /** 8 | * \brief 9 | */ 10 | 11 | #pragma once 12 | 13 | #include 14 | #include 15 | 16 | #define BCOV_DATA_HDR_SIZE (24) 17 | #define BCOV_DATA_MAGIC_SIZE (8) 18 | 19 | #ifdef __cplusplus 20 | extern "C" { 21 | namespace bcov { 22 | #endif 23 | 24 | /* 25 | * bcov header format. 26 | * 8 byte magic. 27 | * 8 byte base address 28 | * 4 byte probe count 29 | * 4 byte pid 30 | */ 31 | 32 | void bcov_write_base_address(uint8_t *begin, uint64_t address); 33 | 34 | uint64_t bcov_read_base_address(const uint8_t *begin); 35 | 36 | void bcov_write_probe_count(uint8_t *begin, size_t probe_count); 37 | 38 | size_t bcov_read_probe_count(const uint8_t *begin); 39 | 40 | void bcov_write_magic(uint8_t *begin); 41 | 42 | bool bcov_has_valid_magic(const uint8_t *begin); 43 | 44 | const uint8_t *bcov_get_magic_data(); 45 | 46 | void bcov_write_process_id(uint8_t *begin); 47 | 48 | int bcov_read_process_id(const uint8_t *begin); 49 | 50 | #ifdef __cplusplus 51 | } // bcov 52 | } 53 | #endif 54 | -------------------------------------------------------------------------------- /src/dwarf/Data.cpp: -------------------------------------------------------------------------------- 1 | /* **************************************************************************** 2 | * Copyright (c) 2018 University of Kaiserslautern. All rights reserved. 3 | * 4 | * This file is distributed under MIT license. See LICENSE.txt for details. 5 | * 6 | * ****************************************************************************/ 7 | /** 8 | * \brief 9 | */ 10 | 11 | #include "Data.hpp" 12 | 13 | namespace bcov { 14 | namespace dwarf { 15 | 16 | sstring 17 | to_string(DwarfPointerEncoding encoding) 18 | { 19 | if (encoding == DW_EH_PE_omit) { 20 | return " omitted"; 21 | } 22 | 23 | sstring result; 24 | switch (encoding & 0x70) { 25 | case DW_EH_PE_pcrel: result = " rel-pc |"; break; 26 | case DW_EH_PE_textrel: result = " rel-text |"; break; 27 | case DW_EH_PE_datarel: result = " rel-data |"; break; 28 | case DW_EH_PE_funcrel: result = " rel-func |"; break; 29 | case DW_EH_PE_aligned: result = " aligned |"; break; 30 | default: break; 31 | } 32 | 33 | switch (encoding & 0x0f) { 34 | case DW_EH_PE_absptr : result += " absolute"; break; 35 | case DW_EH_PE_uleb128 : result += " uleb128"; break; 36 | case DW_EH_PE_udata2 : result += " udata2"; break; 37 | case DW_EH_PE_udata4 : result += " udata4"; break; 38 | case DW_EH_PE_udata8 : result += " udata8"; break; 39 | case DW_EH_PE_signed : result += " signed"; break; 40 | case DW_EH_PE_sleb128 : result += " sleb128"; break; 41 | case DW_EH_PE_sdata2 : result += " sdata2"; break; 42 | case DW_EH_PE_sdata4 : result += " sdata4"; break; 43 | case DW_EH_PE_sdata8 : result += " sdata8"; break; 44 | default: result = "unknown"; break; 45 | } 46 | 47 | return ((encoding & 0x80) == DW_EH_PE_indirect)? result += " | indirect": result; 48 | } 49 | 50 | size_t 51 | size_of_encoded_value(DwarfPointerEncoding encoding, buffer_t pp) noexcept 52 | { 53 | { 54 | if (encoding == DW_EH_PE_omit) { 55 | return 0; 56 | } 57 | size_t len; 58 | switch (encoding & 0x0f) { 59 | case DW_EH_PE_uleb128: 60 | case DW_EH_PE_sleb128:skip_leb128(&pp, &len); 61 | return len; 62 | case DW_EH_PE_udata2: 63 | case DW_EH_PE_sdata2:return 2; 64 | case DW_EH_PE_udata4: 65 | case DW_EH_PE_sdata4:return 4; 66 | case DW_EH_PE_udata8: 67 | case DW_EH_PE_sdata8:return 8; 68 | // XXX: will not work for 32-bit machines 69 | default:return sizeof(void *); 70 | } 71 | } 72 | } 73 | } // dwarf 74 | } // bcov 75 | -------------------------------------------------------------------------------- /src/dwarf/Data.hpp: -------------------------------------------------------------------------------- 1 | /* **************************************************************************** 2 | * Copyright (c) 2018 University of Kaiserslautern. All rights reserved. 3 | * 4 | * This file is distributed under MIT license. See LICENSE.txt for details. 5 | * 6 | * ****************************************************************************/ 7 | /** 8 | * \brief 9 | */ 10 | 11 | #include 12 | 13 | #define MAX_LEB128_SIZE (16) 14 | 15 | namespace bcov { 16 | namespace dwarf { 17 | 18 | static inline bool 19 | skip_leb128(buffer_t *pp, buffer_t end, size_t *len = nullptr) 20 | { 21 | if (*pp == nullptr) { 22 | return false; 23 | } 24 | bool result = false; 25 | buffer_t st_pp = *pp; 26 | for (auto cur_pp = *pp; cur_pp < end; ++cur_pp) { 27 | if ((*cur_pp & 0x80) == 0x0) { 28 | *pp = cur_pp + 1; 29 | result = true; 30 | break; 31 | } 32 | } 33 | if (len != nullptr) { 34 | *len = *pp - st_pp; 35 | } 36 | return result; 37 | } 38 | 39 | static inline bool 40 | skip_leb128(buffer_t *pp, size_t *len = nullptr) 41 | { 42 | const uint8_t *end = *pp + MAX_LEB128_SIZE; 43 | return skip_leb128(pp, end, len); 44 | } 45 | 46 | enum class Endianness { 47 | kBig, 48 | kLittle 49 | }; 50 | 51 | enum DwarfPointerEncoding { 52 | DW_EH_PE_absptr = 0x00, 53 | DW_EH_PE_omit = 0xff, 54 | 55 | DW_EH_PE_uleb128 = 0x01, 56 | DW_EH_PE_udata2 = 0x02, 57 | DW_EH_PE_udata4 = 0x03, 58 | DW_EH_PE_udata8 = 0x04, 59 | DW_EH_PE_signed = 0x08, 60 | DW_EH_PE_sleb128 = 0x09, 61 | DW_EH_PE_sdata2 = 0x0a, 62 | DW_EH_PE_sdata4 = 0x0b, 63 | DW_EH_PE_sdata8 = 0x0c, 64 | 65 | DW_EH_PE_pcrel = 0x10, 66 | DW_EH_PE_textrel = 0x20, 67 | DW_EH_PE_datarel = 0x30, 68 | DW_EH_PE_funcrel = 0x40, 69 | DW_EH_PE_aligned = 0x50, 70 | 71 | DW_EH_PE_indirect = 0x80 72 | }; 73 | 74 | class DwarfPointerEncodingTy { 75 | public: 76 | 77 | DwarfPointerEncodingTy() = default; 78 | 79 | ~DwarfPointerEncodingTy() = default; 80 | 81 | implicit DwarfPointerEncodingTy(DwarfPointerEncoding encoding) 82 | { 83 | value = (uint8_t) encoding; 84 | } 85 | 86 | implicit DwarfPointerEncodingTy(uint8_t encoding) 87 | { 88 | value = encoding; 89 | } 90 | 91 | DwarfPointerEncodingTy &operator=(DwarfPointerEncoding encoding) 92 | { 93 | value = encoding; 94 | return *this; 95 | } 96 | 97 | DwarfPointerEncodingTy &operator=(uint8_t encoding) 98 | { 99 | value = encoding; 100 | return *this; 101 | } 102 | 103 | inline friend bool operator==(DwarfPointerEncodingTy a, DwarfPointerEncoding b) 104 | { return a.value == b; } 105 | 106 | inline friend bool operator!=(DwarfPointerEncodingTy a, DwarfPointerEncoding b) 107 | { return !(a.value == b); } 108 | 109 | operator DwarfPointerEncoding() const 110 | { return (DwarfPointerEncoding) value; } 111 | 112 | private: 113 | uint8_t value; 114 | }; 115 | 116 | static inline bool 117 | is_leb128(DwarfPointerEncoding encoding) noexcept 118 | { 119 | return (encoding & 0x0f) == DW_EH_PE_sleb128 || 120 | (encoding & 0x0f) == DW_EH_PE_uleb128; 121 | } 122 | 123 | static inline DwarfPointerEncoding 124 | absolute(DwarfPointerEncoding encoding) noexcept 125 | { 126 | return DwarfPointerEncodingTy(encoding & 0x0f); 127 | } 128 | 129 | size_t 130 | size_of_encoded_value(DwarfPointerEncoding encoding, 131 | buffer_t pp) noexcept __attribute__ ((const)); 132 | 133 | static inline bool 134 | is_valid_encoding(DwarfPointerEncoding encoding) 135 | { 136 | if (encoding == DW_EH_PE_omit) return true; 137 | if (encoding == DW_EH_PE_aligned) return true; 138 | if ((encoding & 0x7) > DW_EH_PE_udata8) 139 | return false; 140 | return !((encoding & 0x70) > DW_EH_PE_funcrel); 141 | } 142 | 143 | static inline bool 144 | is_indirect(DwarfPointerEncoding encoding) 145 | { 146 | return ((encoding & 0x80) == DW_EH_PE_indirect); 147 | } 148 | 149 | inline uint64_t 150 | round_to_aligned_lower(uint64_t value, uint64_t alignment = sizeof(void *)) noexcept 151 | { 152 | return (value & ~(alignment - 1)); 153 | } 154 | 155 | inline uint64_t 156 | round_to_aligned_higher(uint64_t value, uint64_t alignment = sizeof(void *)) noexcept 157 | { 158 | return round_to_aligned_lower(value + alignment - 1, alignment); 159 | } 160 | 161 | sstring to_string(DwarfPointerEncoding encoding); 162 | 163 | } // dwarf 164 | } // bcov 165 | -------------------------------------------------------------------------------- /src/dwarf/EhFrame.hpp: -------------------------------------------------------------------------------- 1 | /* **************************************************************************** 2 | * Copyright (c) 2018 University of Kaiserslautern. All rights reserved. 3 | * 4 | * This file is distributed under MIT license. See LICENSE.txt for details. 5 | * 6 | * ****************************************************************************/ 7 | 8 | // 9 | // DwarfPointerReader is based on code of ByteReader. ByteReader code is part 10 | // of "breakpad" project. ByteReader is distributed under the following license: 11 | // 12 | // Copyright (c) 2010 Google Inc. All Rights Reserved. 13 | // 14 | // Redistribution and use in source and binary forms, with or without 15 | // modification, are permitted provided that the following conditions are 16 | // met: 17 | // 18 | // * Redistributions of source code must retain the above copyright 19 | // notice, this list of conditions and the following disclaimer. 20 | // * Redistributions in binary form must reproduce the above 21 | // copyright notice, this list of conditions and the following disclaimer 22 | // in the documentation and/or other materials provided with the 23 | // distribution. 24 | // * Neither the name of Google Inc. nor the names of its 25 | // contributors may be used to endorse or promote products derived from 26 | // this software without specific prior written permission. 27 | // 28 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 29 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 30 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 31 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 32 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 33 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 34 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 35 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 36 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 37 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 38 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 39 | // 40 | 41 | /** 42 | * \brief 43 | */ 44 | 45 | 46 | #pragma once 47 | 48 | #include "core/Common.hpp" 49 | #include "Data.hpp" 50 | #include 51 | 52 | namespace bcov { 53 | namespace dwarf { 54 | 55 | using Addr64 = uint64_t; 56 | using Addr32 = uint32_t; 57 | 58 | class EHRecord { 59 | public: 60 | 61 | EHRecord(); 62 | 63 | explicit EHRecord(buffer_t buffer, size_t offset = 0); 64 | 65 | EHRecord(const EHRecord &other) = default; 66 | 67 | EHRecord &operator=(const EHRecord &other) = default; 68 | 69 | EHRecord(EHRecord &&other) noexcept = default; 70 | 71 | EHRecord &operator=(EHRecord &&other) noexcept = default; 72 | 73 | virtual ~EHRecord() = default; 74 | 75 | size_t content_length() const noexcept; 76 | 77 | size_t header_length() const noexcept; 78 | 79 | uint32_t id() const noexcept; 80 | 81 | buffer_t record_start() const noexcept; 82 | 83 | buffer_t record_end() const noexcept; 84 | 85 | size_t record_length() const noexcept; 86 | 87 | size_t record_offset() const noexcept; 88 | 89 | bool is_cie() const noexcept; 90 | 91 | bool is_terminator() const noexcept; 92 | 93 | bool valid() const noexcept; 94 | 95 | private: 96 | buffer_t m_data; 97 | size_t m_offset; 98 | }; 99 | 100 | class CIE : public EHRecord { 101 | public: 102 | 103 | CIE(); 104 | 105 | CIE(buffer_t buffer, size_t offset); 106 | 107 | CIE(const CIE &other) = default; 108 | 109 | CIE &operator=(const CIE &other) = default; 110 | 111 | CIE(CIE &&other) noexcept = default; 112 | 113 | CIE &operator=(CIE &&other) noexcept = default; 114 | 115 | ~CIE() override = default; 116 | 117 | friend bool operator==(const CIE &a, const CIE &b) 118 | { 119 | return a.record_offset() == b.record_offset(); 120 | } 121 | 122 | friend bool operator<(const CIE &a, const CIE &b) 123 | { 124 | return a.record_offset() < b.record_offset(); 125 | } 126 | 127 | uint8_t version() const noexcept; 128 | 129 | czstring augmentation_str() const; 130 | 131 | uint64_t code_align_factor() const; 132 | 133 | int64_t data_align_factor() const; 134 | 135 | uint64_t ret_address_reg() const; 136 | 137 | uint64_t augmentation_len() const; 138 | 139 | buffer_t augmentation_data() const; 140 | 141 | bool augmentation_exists() const; 142 | 143 | buffer_t instructions() const; 144 | }; 145 | 146 | class CIEAugmentation { 147 | public: 148 | 149 | CIEAugmentation(); 150 | 151 | CIEAugmentation(czstring augm_str, buffer_t augm_data); 152 | 153 | DwarfPointerEncoding code_enc() const noexcept; 154 | 155 | DwarfPointerEncoding lsda_enc() const noexcept; 156 | 157 | DwarfPointerEncoding personality_enc() const noexcept; 158 | 159 | buffer_t personality() const noexcept; 160 | 161 | bool has_code_encoding() const noexcept; 162 | 163 | bool has_lsda_encoding() const noexcept; 164 | 165 | bool has_personality_encoding() const noexcept; 166 | 167 | bool valid() const noexcept; 168 | 169 | private: 170 | DwarfPointerEncodingTy m_code_enc; 171 | DwarfPointerEncodingTy m_lsda_enc; 172 | DwarfPointerEncodingTy m_personality_enc; 173 | buffer_t m_personality_data; 174 | bool m_valid; 175 | }; 176 | 177 | class LSDA { 178 | public: 179 | 180 | LSDA(); 181 | 182 | explicit LSDA(buffer_t data); 183 | 184 | DwarfPointerEncoding landing_pad_start_enc() const; 185 | 186 | bool has_landing_pad_start_address() const noexcept; 187 | 188 | buffer_t landing_pad_start_address() const; 189 | 190 | DwarfPointerEncoding type_table_enc() const; 191 | 192 | bool has_type_table() const noexcept; 193 | 194 | buffer_t header_start() const noexcept; 195 | 196 | buffer_t header_end() const; 197 | 198 | uoffset_t type_table_offset() const; 199 | 200 | DwarfPointerEncoding call_site_encoding() const; 201 | 202 | buffer_t call_site_tbl_start() const; 203 | 204 | buffer_t call_site_tbl_end() const; 205 | 206 | bool valid() const noexcept; 207 | 208 | private: 209 | buffer_t m_data; 210 | }; 211 | 212 | /// @class LSDACallSiteEntry 213 | /// @brief LSDA "table-based" entry 214 | class LSDACallSiteEntry { 215 | public: 216 | 217 | LSDACallSiteEntry(); 218 | 219 | LSDACallSiteEntry(buffer_t data, DwarfPointerEncoding callsite_enc); 220 | 221 | buffer_t start() const noexcept; 222 | 223 | buffer_t next() const noexcept; 224 | 225 | buffer_t range() const noexcept; 226 | 227 | buffer_t landing_pad() const noexcept; 228 | 229 | bool landing_pad_exists() const noexcept; 230 | 231 | uoffset_t action_table_offset() const noexcept; 232 | 233 | DwarfPointerEncoding encoding() const noexcept; 234 | 235 | bool valid() const noexcept; 236 | 237 | bool is_terminal() const noexcept; 238 | 239 | private: 240 | buffer_t m_data; 241 | DwarfPointerEncodingTy m_callsite_encoding; 242 | }; 243 | 244 | class FDE : public EHRecord { 245 | public: 246 | 247 | FDE(); 248 | 249 | FDE(buffer_t buffer, size_t offset); 250 | 251 | FDE(const FDE &other) = default; 252 | 253 | FDE &operator=(const FDE &other) = default; 254 | 255 | FDE(FDE &&other) noexcept = default; 256 | 257 | FDE &operator=(FDE &&other) noexcept = default; 258 | 259 | ~FDE() override = default; 260 | 261 | friend inline bool operator==(const FDE &a, const FDE &b) 262 | { 263 | return a.record_offset() == b.record_offset(); 264 | } 265 | 266 | friend inline bool operator<(const FDE &a, const FDE &b) 267 | { 268 | return a.record_offset() < b.record_offset(); 269 | } 270 | 271 | CIE get_cie() const noexcept; 272 | 273 | buffer_t location() const; 274 | 275 | buffer_t range(const CIEAugmentation &augm) const; 276 | 277 | // assumes that augmentation exists in parent CIE 278 | uint64_t augmentation_len(const CIEAugmentation &augm) const; 279 | 280 | // assumes that augmentation exists in parent CIE 281 | buffer_t augmentation_data(const CIEAugmentation &augm) const; 282 | 283 | buffer_t instructions(const CIEAugmentation &augm) const; 284 | }; 285 | 286 | class EhFrame { 287 | public: 288 | 289 | using FDEVec = std::vector; 290 | using CIEMap = std::map; 291 | 292 | EhFrame(); 293 | 294 | EhFrame(const EhFrame &other) = default; 295 | 296 | EhFrame &operator=(const EhFrame &other) = default; 297 | 298 | EhFrame(EhFrame &&other) noexcept = default; 299 | 300 | EhFrame &operator=(EhFrame &&other) noexcept = default; 301 | 302 | ~EhFrame() = default; 303 | 304 | void parse(buffer_t buffer, addr_t address, size_t size); 305 | 306 | const CIEMap &map() const noexcept; 307 | 308 | buffer_t data() const noexcept; 309 | 310 | addr_t virtual_address() const noexcept; 311 | 312 | size_t size() const noexcept; 313 | 314 | bool valid() const noexcept; 315 | 316 | CIE get_cie(uoffset_t offset) const; 317 | 318 | FDE get_fde(uoffset_t offset) const; 319 | 320 | private: 321 | buffer_t m_data; 322 | addr_t m_virtual_address; 323 | size_t m_size; 324 | CIEMap m_cie_map; 325 | }; 326 | 327 | class DwarfPointerReader { 328 | public: 329 | 330 | DwarfPointerReader(); 331 | 332 | uint64_t 333 | read(buffer_t buffer, DwarfPointerEncoding encoding, unsigned *len) const; 334 | 335 | bool usable_encoding(DwarfPointerEncoding encoding) const noexcept; 336 | 337 | void set_endianess(Endianness e) noexcept; 338 | 339 | void set_cfi_base(uint64_t section_base, buffer_t buffer_base) noexcept; 340 | 341 | void set_text_base(uint64_t text_base) noexcept; 342 | 343 | void set_function_base(uint64_t function_base) noexcept; 344 | 345 | void set_data_base(uint64_t data_base) noexcept; 346 | 347 | void set_address_size(uint8_t size) noexcept; 348 | 349 | void set_offset_size(uint8_t size) noexcept; 350 | 351 | uint8_t read_one_byte(buffer_t buffer) const; 352 | 353 | uint16_t read_two_bytes(buffer_t buffer) const; 354 | 355 | uint64_t read_four_bytes(buffer_t buffer) const; 356 | 357 | uint64_t read_eight_bytes(buffer_t buffer) const; 358 | 359 | uint64_t read_address(buffer_t buffer) const; 360 | 361 | private: 362 | 363 | using AddressReader = decltype(&DwarfPointerReader::read_eight_bytes); 364 | 365 | // Read an offset from BUFFER and return it as an unsigned 64 bit 366 | // integer. DWARF2/3 define offsets as either 4 or 8 bytes, 367 | // generally depending on the amount of DWARF2/3 info present. 368 | // This function pointer gets set by SetOffsetSize. 369 | AddressReader m_offset_reader; 370 | 371 | // Read an address from BUFFER and return it as an unsigned 64 bit 372 | // integer. DWARF2/3 allow addresses to be any size from 0-255 373 | // bytes currently. Internally we support 4 and 8 byte addresses, 374 | // and will CHECK on anything else. 375 | // This function pointer gets set by SetAddressSize. 376 | AddressReader m_address_reader; 377 | 378 | uint8_t m_address_size; 379 | uint8_t m_offset_size; 380 | Endianness m_endianess; 381 | buffer_t m_buffer_base; 382 | addr_t m_section_base, m_text_base, m_func_base, m_data_base; 383 | bool m_has_section_base, m_has_text_base, m_has_func_base, m_has_data_base; 384 | }; 385 | 386 | class EhFrameFormatException : public std::runtime_error { 387 | public: 388 | 389 | explicit EhFrameFormatException(const std::string &what_arg); 390 | 391 | explicit EhFrameFormatException(const char *what_arg); 392 | 393 | ~EhFrameFormatException() override = default; 394 | }; 395 | 396 | } // dwarf 397 | } // bcov 398 | -------------------------------------------------------------------------------- /src/dwarf/LEB128.hpp: -------------------------------------------------------------------------------- 1 | //===- llvm/Support/LEB128.h - [SU]LEB128 utility functions -----*- C++ -*-===// 2 | // 3 | // The LLVM Compiler Infrastructure 4 | // 5 | // This file is distributed under the University of Illinois Open Source 6 | // License. See LICENSE.TXT for details. 7 | // 8 | //===----------------------------------------------------------------------===// 9 | // 10 | // This file declares some utility functions for encoding SLEB128 and 11 | // ULEB128 values. 12 | // 13 | //===----------------------------------------------------------------------===// 14 | 15 | #pragma once 16 | 17 | #include "core/Common.hpp" 18 | 19 | namespace bcov { 20 | namespace dwarf { 21 | 22 | /// Utility function to encode a SLEB128 value to a buffer. Returns 23 | /// the length in bytes of the encoded value. 24 | inline unsigned encodeSLEB128(int64_t Value, uint8_t *p, unsigned PadTo = 0) { 25 | uint8_t *orig_p = p; 26 | unsigned Count = 0; 27 | bool More; 28 | do { 29 | uint8_t Byte = Value & 0x7f; 30 | // NOTE: this assumes that this signed shift is an arithmetic right shift. 31 | Value >>= 7; 32 | More = !((((Value == 0 ) && ((Byte & 0x40) == 0)) || 33 | ((Value == -1) && ((Byte & 0x40) != 0)))); 34 | Count++; 35 | if (More || Count < PadTo) 36 | Byte |= 0x80; // Mark this byte to show that more bytes will follow. 37 | *p++ = Byte; 38 | } while (More); 39 | 40 | // Pad with 0x80 and emit a terminating byte at the end. 41 | if (Count < PadTo) { 42 | uint8_t PadValue = Value < 0 ? 0x7f : 0x00; 43 | for (; Count < PadTo - 1; ++Count) 44 | *p++ = (PadValue | 0x80); 45 | *p++ = PadValue; 46 | } 47 | return (unsigned)(p - orig_p); 48 | } 49 | 50 | /// Utility function to encode a ULEB128 value to a buffer. Returns 51 | /// the length in bytes of the encoded value. 52 | inline unsigned encodeULEB128(uint64_t Value, uint8_t *p, 53 | unsigned PadTo = 0) { 54 | uint8_t *orig_p = p; 55 | unsigned Count = 0; 56 | do { 57 | uint8_t Byte = Value & 0x7f; 58 | Value >>= 7; 59 | Count++; 60 | if (Value != 0 || Count < PadTo) 61 | Byte |= 0x80; // Mark this byte to show that more bytes will follow. 62 | *p++ = Byte; 63 | } while (Value != 0); 64 | 65 | // Pad with 0x80 and emit a null byte at the end. 66 | if (Count < PadTo) { 67 | for (; Count < PadTo - 1; ++Count) 68 | *p++ = '\x80'; 69 | *p++ = '\x00'; 70 | } 71 | 72 | return (unsigned)(p - orig_p); 73 | } 74 | 75 | /// Utility function to decode a ULEB128 value. 76 | inline uint64_t decodeULEB128(const uint8_t *p, unsigned *n = nullptr, 77 | const uint8_t *end = nullptr, 78 | const char **error = nullptr) { 79 | const uint8_t *orig_p = p; 80 | uint64_t Value = 0; 81 | unsigned Shift = 0; 82 | if (error) 83 | *error = nullptr; 84 | do { 85 | if (end && p == end) { 86 | if (error) 87 | *error = "malformed uleb128, extends past end"; 88 | if (n) 89 | *n = (unsigned)(p - orig_p); 90 | return 0; 91 | } 92 | uint64_t Slice = *p & 0x7f; 93 | if (Shift >= 64 || Slice << Shift >> Shift != Slice) { 94 | if (error) 95 | *error = "uleb128 too big for uint64"; 96 | if (n) 97 | *n = (unsigned)(p - orig_p); 98 | return 0; 99 | } 100 | Value += uint64_t(*p & 0x7f) << Shift; 101 | Shift += 7; 102 | } while (*p++ >= 128); 103 | if (n) 104 | *n = (unsigned)(p - orig_p); 105 | return Value; 106 | } 107 | 108 | /// Utility function to decode a SLEB128 value. 109 | inline int64_t decodeSLEB128(const uint8_t *p, unsigned *n = nullptr, 110 | const uint8_t *end = nullptr, 111 | const char **error = nullptr) { 112 | const uint8_t *orig_p = p; 113 | int64_t Value = 0; 114 | unsigned Shift = 0; 115 | uint8_t Byte; 116 | do { 117 | if (end && p == end) { 118 | if (error) 119 | *error = "malformed sleb128, extends past end"; 120 | if (n) 121 | *n = (unsigned)(p - orig_p); 122 | return 0; 123 | } 124 | Byte = *p++; 125 | Value |= (int64_t(Byte & 0x7f) << Shift); 126 | Shift += 7; 127 | } while (Byte >= 128); 128 | // Sign extend negative numbers. 129 | if (Byte & 0x40) 130 | Value |= (-1ULL) << Shift; 131 | if (n) 132 | *n = (unsigned)(p - orig_p); 133 | return Value; 134 | } 135 | 136 | /// Utility function to get the size of the ULEB128-encoded value. 137 | inline unsigned getULEB128Size(uint64_t Value) { 138 | unsigned Size = 0; 139 | do { 140 | Value >>= 7; 141 | Size += sizeof(int8_t); 142 | } while (Value); 143 | return Size; 144 | } 145 | 146 | /// Utility function to get the size of the SLEB128-encoded value. 147 | inline unsigned getSLEB128Size(int64_t Value) { 148 | unsigned Size = 0; 149 | int Sign = Value >> (8 * sizeof(Value) - 1); 150 | bool IsMore; 151 | 152 | do { 153 | unsigned Byte = Value & 0x7f; 154 | Value >>= 7; 155 | IsMore = Value != Sign || ((Byte ^ Sign) & 0x40) != 0; 156 | Size += sizeof(int8_t); 157 | } while (IsMore); 158 | return Size; 159 | } 160 | 161 | } // dwarf 162 | } // bcov 163 | -------------------------------------------------------------------------------- /src/dwarf/PrettyPrint.cpp: -------------------------------------------------------------------------------- 1 | /* **************************************************************************** 2 | * Copyright (c) 2018 University of Kaiserslautern. All rights reserved. 3 | * 4 | * This file is distributed under MIT license. See LICENSE.txt for details. 5 | * 6 | * ****************************************************************************/ 7 | /** 8 | * \brief 9 | */ 10 | 11 | #include "PrettyPrint.hpp" 12 | #include "easylogging/easylogging++.h" 13 | #include 14 | 15 | #define LSDA_MARGIN " " 16 | #define CIE_MARGIN " " 17 | 18 | namespace bcov { 19 | namespace dwarf { 20 | 21 | std::ostream & 22 | write_cie(std::ostream &out, const CIE &cie) 23 | { 24 | using namespace std; 25 | out << hex << setfill('0') 26 | << setw(8) << cie.record_offset() << " " 27 | << setw(16) << cie.content_length() << " " 28 | << setw(8) << cie.id() << " CIE\n"; 29 | 30 | out << CIE_MARGIN << "Version: " 31 | << setfill(' ') << setw(16) << (unsigned) cie.version() << "\n" 32 | << CIE_MARGIN << "Augmentation: " 33 | << cie.augmentation_str() << "\n" << dec 34 | << CIE_MARGIN << "Code alignment factor: " 35 | << cie.code_align_factor() << "\n" 36 | << CIE_MARGIN << "Data alignment factor: " 37 | << cie.data_align_factor() << "\n" 38 | << CIE_MARGIN << "Return address column: " 39 | << cie.ret_address_reg() << "\n" 40 | << CIE_MARGIN << "Augmentation data: "; 41 | out << hex << setfill('0'); 42 | for (auto pp = cie.augmentation_data(); 43 | pp < cie.augmentation_data() + cie.augmentation_len(); ++pp) { 44 | out << hex << setw(2) << (unsigned) *pp << " "; 45 | } 46 | out << "\n\n"; 47 | return out; 48 | } 49 | 50 | std::ostream & 51 | write_fde(std::ostream &out, const FDE &fde, const DwarfPointerReader &reader, 52 | const CIEAugmentation &augm) 53 | { 54 | using namespace std; 55 | addr_t pc; 56 | addr_t range; 57 | unsigned len; 58 | if (augm.has_code_encoding()) { 59 | pc = reader.read(fde.location(), augm.code_enc(), &len); 60 | range = reader.read(fde.range(augm), absolute(augm.code_enc()), &len); 61 | } else { 62 | pc = reader.read_address(fde.location()); 63 | range = reader.read_address(fde.range(augm)); 64 | } 65 | 66 | out << hex << setfill('0') 67 | << setw(8) << fde.record_offset() << " " 68 | << setw(16) << fde.content_length() << " " 69 | << setw(8) << fde.id() << " FDE cie=" 70 | << setw(8) << fde.get_cie().record_offset() << " " 71 | << setw(16) << pc << ".." << setw(16) << (pc + range) << "\n"; 72 | 73 | return out; 74 | } 75 | 76 | std::ostream & 77 | write_lsda(std::ostream &out, const LSDA &lsda, const DwarfPointerReader &reader) 78 | { 79 | out << LSDA_MARGIN << std::boolalpha 80 | << "LPStart: " << lsda.has_landing_pad_start_address() 81 | << ",TTable: " << lsda.has_type_table() << "\n"; 82 | auto pp = lsda.call_site_tbl_start(); 83 | const auto encoding = lsda.call_site_encoding(); 84 | out << std::hex; 85 | while (pp < lsda.call_site_tbl_end()) { 86 | unsigned len; 87 | LSDACallSiteEntry entry(pp, encoding); 88 | out << LSDA_MARGIN << "---------------------------\n"; 89 | out << LSDA_MARGIN << "St Offset: " 90 | << reader.read(entry.start(), absolute(encoding), &len) << "\n"; 91 | out << LSDA_MARGIN << "Length : " 92 | << reader.read(entry.range(), absolute(encoding), &len) << " \n"; 93 | out << LSDA_MARGIN << "LP Offset: " 94 | << reader.read(entry.landing_pad(), absolute(encoding), &len) << " \n"; 95 | pp = entry.next(); 96 | } 97 | 98 | return out; 99 | } 100 | 101 | sstring 102 | to_string(const CIE &cie) 103 | { 104 | std::stringstream stream; 105 | write_cie(stream, cie); 106 | return stream.str(); 107 | } 108 | 109 | sstring 110 | to_string(const FDE &fde, const DwarfPointerReader &reader, 111 | const CIEAugmentation &augm) 112 | { 113 | std::stringstream stream; 114 | write_fde(stream, fde, reader, augm); 115 | return stream.str(); 116 | } 117 | 118 | sstring 119 | to_string(const LSDA &lsda, const DwarfPointerReader &reader) 120 | { 121 | std::stringstream stream; 122 | write_lsda(stream, lsda, reader); 123 | return stream.str(); 124 | } 125 | 126 | } // dwarf 127 | } // bcov 128 | -------------------------------------------------------------------------------- /src/dwarf/PrettyPrint.hpp: -------------------------------------------------------------------------------- 1 | /* **************************************************************************** 2 | * Copyright (c) 2018 University of Kaiserslautern. All rights reserved. 3 | * 4 | * This file is distributed under MIT license. See LICENSE.txt for details. 5 | * 6 | * ****************************************************************************/ 7 | /** 8 | * \brief 9 | */ 10 | 11 | #pragma once 12 | 13 | #include "core/Common.hpp" 14 | #include "EhFrame.hpp" 15 | 16 | namespace bcov { 17 | namespace dwarf { 18 | 19 | std::ostream & 20 | write_cie(std::ostream &out, const CIE &cie); 21 | 22 | std::ostream & 23 | write_fde(std::ostream &out, const FDE &fde, const DwarfPointerReader &reader, 24 | const CIEAugmentation &augm); 25 | 26 | std::ostream & 27 | write_lsda(std::ostream &out, const LSDA &lsda, const DwarfPointerReader &reader); 28 | 29 | sstring to_string(const CIE &cie); 30 | 31 | sstring to_string(const LSDA &lsda, const DwarfPointerReader &reader); 32 | 33 | sstring to_string(const FDE &fde, const DwarfPointerReader &reader, 34 | const CIEAugmentation &augm); 35 | 36 | } // dwarf 37 | } // bcov 38 | -------------------------------------------------------------------------------- /src/elf/ElfExtender.hpp: -------------------------------------------------------------------------------- 1 | /* **************************************************************************** 2 | * Copyright (c) 2018 University of Kaiserslautern. All rights reserved. 3 | * 4 | * This file is distributed under MIT license. See LICENSE.txt for details. 5 | * 6 | * ****************************************************************************/ 7 | /** 8 | * \brief 9 | */ 10 | 11 | #pragma once 12 | 13 | #include "libelfin/elf/elf++.hh" 14 | #include "elf/Util.hpp" 15 | #include "core/Common.hpp" 16 | 17 | namespace bcov { 18 | 19 | class ElfExtender { 20 | public: 21 | 22 | ElfExtender(); 23 | 24 | ElfExtender(size_t code_seg_size, size_t data_seg_size); 25 | 26 | virtual ~ElfExtender() = default; 27 | 28 | void code_segment_size(size_t size); 29 | 30 | void data_segment_size(size_t size); 31 | 32 | bool extend(sstring_view input_file, sstring_view output_file); 33 | 34 | private: 35 | struct Impl; 36 | size_t m_code_seg_size; 37 | size_t m_data_seg_size; 38 | }; 39 | 40 | class ElfExtenderException : public std::logic_error { 41 | public: 42 | 43 | explicit ElfExtenderException(const std::string &what_arg); 44 | 45 | explicit ElfExtenderException(const char *what_arg); 46 | 47 | ~ElfExtenderException() override = default; 48 | }; 49 | 50 | } // bcov 51 | -------------------------------------------------------------------------------- /src/elf/ElfModule.hpp: -------------------------------------------------------------------------------- 1 | /* **************************************************************************** 2 | * Copyright (c) 2018 University of Kaiserslautern. All rights reserved. 3 | * 4 | * This file is distributed under MIT license. See LICENSE.txt for details. 5 | * 6 | * ****************************************************************************/ 7 | /** 8 | * \brief 9 | */ 10 | 11 | #pragma once 12 | 13 | #include "libelfin/elf/elf++.hh" 14 | #include "util/Demangler.hpp" 15 | #include "dwarf/EhFrame.hpp" 16 | #include "flax/Flax.hpp" 17 | #include "core/Function.hpp" 18 | #include "core/Region.hpp" 19 | 20 | BCOV_FORWARD(CallSite) 21 | BCOV_FORWARD(Demangler) 22 | BCOV_FORWARD(BcovConfig) 23 | 24 | #define DOT_CALLGRAPH_DUMP_COUNT 100 25 | 26 | namespace bcov { 27 | 28 | class ElfFunction : public FunctionBase { 29 | friend class ElfModuleBuilder; 30 | 31 | public: 32 | using LandingPads = std::vector; 33 | using CallSites = std::vector; 34 | 35 | ElfFunction(); 36 | 37 | ElfFunction(sstring_view name, addr_t addr, const uint8_t *data, size_t size); 38 | 39 | ElfFunction(sstring_view name, addr_t got_address); 40 | 41 | ~ElfFunction() override = default; 42 | 43 | //=============================================== 44 | 45 | addr_t got_offset() const noexcept; 46 | 47 | addr_t export_address() const noexcept; 48 | 49 | void add_call_site(CallSiteKind kind, addr_t src, addr_t target = 0); 50 | 51 | span call_sites() const noexcept; 52 | 53 | uoffset_t eh_frame_offset() const noexcept; 54 | 55 | /// @brief return offsets of landing pads into the function 56 | const LandingPads &landing_pads() const noexcept; 57 | 58 | bool has_landing_pads() const noexcept; 59 | 60 | unsigned padding() const noexcept; 61 | 62 | private: 63 | addr_t m_got_addr; 64 | addr_t m_export_addr; 65 | uint8_t m_padding; 66 | uint32_t m_eh_frame_offset; 67 | CallSites m_call_sites; 68 | LandingPads m_landing_pads; 69 | }; 70 | 71 | static inline bool operator==(const ElfFunction &a, const ElfFunction &b) 72 | { 73 | return a.address() == b.address(); 74 | } 75 | 76 | static inline bool operator!=(const ElfFunction &a, const ElfFunction &b) 77 | { 78 | return !(a == b); 79 | } 80 | 81 | template<> 82 | struct identify { 83 | size_t operator()(const ElfFunction &f) const noexcept 84 | { 85 | return f.idx(); 86 | } 87 | }; 88 | 89 | class ElfCallGraph { 90 | friend class ElfModuleBuilder; 91 | 92 | public: 93 | using Graph = OrderedGraph; 94 | 95 | const Graph &forward() const noexcept; 96 | 97 | const Graph &backward() const noexcept; 98 | 99 | private: 100 | Graph::VertexStore m_store; 101 | Graph m_forward; 102 | Graph m_backward; 103 | }; 104 | 105 | class ElfModule { 106 | friend class ElfModuleBuilder; 107 | 108 | public: 109 | 110 | ElfModule(); 111 | 112 | ElfModule(const ElfModule &other) = default; 113 | 114 | ElfModule &operator=(const ElfModule &other) = default; 115 | 116 | ElfModule(ElfModule &&other) noexcept = default; 117 | 118 | ElfModule &operator=(ElfModule &&other) noexcept = default; 119 | 120 | virtual ~ElfModule() = default; 121 | 122 | //=============================================== 123 | 124 | span probed_functions() const noexcept; 125 | 126 | span static_functions() const noexcept; 127 | 128 | span dynamic_functions() const noexcept; 129 | 130 | IFunction get_instrumented_function(sstring_view func_name) const; 131 | 132 | IFunction get_instrumented_function(addr_t func_address) const; 133 | 134 | ElfFunction *get_static_function_at(addr_t func_addr) const noexcept; 135 | 136 | ElfFunction *get_dynamic_function_at(addr_t func_addr) const noexcept; 137 | 138 | ElfFunction * 139 | get_dynamic_function_by_got(addr_t got_offset) const noexcept; 140 | 141 | ElfFunction *get_function_at(addr_t func_addr) const noexcept; 142 | 143 | ElfFunction *get_function_by_name(sstring_view func_name) const noexcept; 144 | 145 | bool exists(sstring_view func_name) const; 146 | 147 | const sstring &name() const noexcept; 148 | 149 | void name(sstring_view name) const noexcept; 150 | 151 | buffer_t get_buffer(addr_t address) const noexcept; 152 | 153 | bool is_inside_got_region(addr_t address) const noexcept; 154 | 155 | uint64_t read_address(addr_t address) const noexcept; 156 | 157 | const elf::elf &binary() const noexcept; 158 | 159 | Demangler *demangler() const noexcept; 160 | 161 | const dwarf::EhFrame &eh_frame() noexcept; 162 | 163 | const MemoryRegion &code_region() const noexcept; 164 | 165 | const MemoryRegion &data_region() const noexcept; 166 | 167 | bool is_position_independent_code() const noexcept; 168 | 169 | protected: 170 | 171 | void add(IFunction function); 172 | 173 | void binary(elf::elf binary) noexcept; 174 | 175 | bool init_got_region() noexcept; 176 | 177 | void finalize() noexcept; 178 | 179 | private: 180 | struct Impl; 181 | std::shared_ptr m_impl; 182 | }; 183 | 184 | class ElfModuleBuilder { 185 | public: 186 | 187 | ElfModuleBuilder() = default; 188 | 189 | ElfModuleBuilder(const ElfModuleBuilder &other) = default; 190 | 191 | ElfModuleBuilder &operator=(const ElfModuleBuilder &other) = default; 192 | 193 | ElfModuleBuilder(ElfModuleBuilder &&other) noexcept = default; 194 | 195 | ElfModuleBuilder &operator=(ElfModuleBuilder &&other) noexcept = default; 196 | 197 | virtual ~ElfModuleBuilder() = default; 198 | 199 | static ElfModule build(sstring_view file, const BcovConfig &config); 200 | 201 | static ElfModule build(sstring_view file); 202 | 203 | protected: 204 | static void do_initial_analyses(ElfModule &module); 205 | 206 | private: 207 | struct Impl; 208 | }; 209 | 210 | class ElfModuleBuilderException : public std::runtime_error { 211 | public: 212 | explicit ElfModuleBuilderException(const std::string &what_arg); 213 | 214 | explicit ElfModuleBuilderException(const char *what_arg); 215 | 216 | ~ElfModuleBuilderException() override = default; 217 | }; 218 | 219 | namespace dot { 220 | 221 | void 222 | write_call_graph(std::ostream &out, sstring_view graph_name, const ElfFunction &root, 223 | const ElfCallGraph::Graph &graph, 224 | unsigned max_node_count = DOT_CALLGRAPH_DUMP_COUNT); 225 | 226 | void 227 | write_call_graph(sstring_view file_name, sstring_view graph_name, 228 | const ElfFunction &root, const ElfCallGraph::Graph &graph, 229 | unsigned max_node_count = DOT_CALLGRAPH_DUMP_COUNT); 230 | 231 | } 232 | } // bcov 233 | -------------------------------------------------------------------------------- /src/elf/ElfParser.cpp: -------------------------------------------------------------------------------- 1 | /* **************************************************************************** 2 | * Copyright (c) 2018 University of Kaiserslautern. All rights reserved. 3 | * 4 | * This file is distributed under MIT license. See LICENSE.txt for details. 5 | * 6 | * ****************************************************************************/ 7 | /** 8 | * \brief 9 | */ 10 | 11 | #include "ElfParser.hpp" 12 | #include "easylogging/easylogging++.h" 13 | #include 14 | 15 | namespace elf { 16 | enum class machine : ElfTypes::Half { 17 | EM_SPARC = 2, 18 | EM_MIPS = 8, 19 | EM_ARM = 40, 20 | EM_X86_64 = 62 21 | }; 22 | } 23 | 24 | namespace bcov { 25 | 26 | static inline bool 27 | is_executable(const elf::elf &binary) 28 | { 29 | return binary.get_hdr().type == elf::et::exec; 30 | } 31 | 32 | static inline bool 33 | is_shared_obj(const elf::elf &binary) 34 | { 35 | return binary.get_hdr().type == elf::et::dyn; 36 | } 37 | 38 | static bool 39 | is_x64_binary(const elf::elf &binary) 40 | { 41 | return binary.get_hdr().machine == to_integral(elf::machine::EM_X86_64) and 42 | binary.get_hdr().ei_class == elf::elfclass::_64; 43 | } 44 | 45 | elf::elf 46 | ElfParser::parse(sstring_view file_name) 47 | { 48 | sstring file_name_str = to_string(file_name); 49 | 50 | int fd = open(file_name_str.c_str(), O_RDONLY); 51 | if (fd < 0) { 52 | throw std::runtime_error("could not open input elf file!"); 53 | } 54 | 55 | elf::elf binary(elf::create_mmap_loader(fd)); 56 | 57 | if (not is_x64_binary(binary)) { 58 | throw ElfLogicException("error: we support x86-64 binaries only!"); 59 | } 60 | if (not(is_executable(binary) or is_shared_obj(binary))) { 61 | throw ElfLogicException( 62 | "error: only executables and shared libraries are supported!"); 63 | } 64 | return binary; 65 | } 66 | 67 | ElfLogicException::ElfLogicException(const std::string &what_arg) : 68 | std::logic_error(what_arg) 69 | { 70 | } 71 | 72 | ElfLogicException::ElfLogicException(const char *what_arg) : 73 | std::logic_error(what_arg) 74 | { 75 | } 76 | 77 | } //bcov 78 | -------------------------------------------------------------------------------- /src/elf/ElfParser.hpp: -------------------------------------------------------------------------------- 1 | /* **************************************************************************** 2 | * Copyright (c) 2018 University of Kaiserslautern. All rights reserved. 3 | * 4 | * This file is distributed under MIT license. See LICENSE.txt for details. 5 | * 6 | * ****************************************************************************/ 7 | /** 8 | * \brief 9 | */ 10 | #pragma once 11 | 12 | #include "util/BcovConfig.hpp" 13 | #include "libelfin/elf/elf++.hh" 14 | 15 | namespace bcov { 16 | 17 | class ElfParser { 18 | public: 19 | ElfParser() = delete; 20 | 21 | ElfParser(const ElfParser &other) = delete; 22 | 23 | ElfParser &operator=(const ElfParser &other) = delete; 24 | 25 | ElfParser(ElfParser &&other) noexcept = default; 26 | 27 | ElfParser &operator=(ElfParser &&other) noexcept = default; 28 | 29 | virtual ~ElfParser() = default; 30 | 31 | static elf::elf parse(sstring_view file_name); 32 | 33 | }; 34 | 35 | class ElfLogicException : public std::logic_error { 36 | public: 37 | 38 | explicit ElfLogicException(const std::string &what_arg); 39 | 40 | explicit ElfLogicException(const char *what_arg); 41 | 42 | ~ElfLogicException() override = default; 43 | }; 44 | 45 | } 46 | -------------------------------------------------------------------------------- /src/elf/ElfPatchManager.hpp: -------------------------------------------------------------------------------- 1 | /* **************************************************************************** 2 | * Copyright (c) 2018 University of Kaiserslautern. All rights reserved. 3 | * 4 | * This file is distributed under MIT license. See LICENSE.txt for details. 5 | * 6 | * ****************************************************************************/ 7 | /** 8 | * \brief 9 | */ 10 | 11 | 12 | #pragma once 13 | 14 | #include "core/Common.hpp" 15 | #include "elf/ElfModule.hpp" 16 | #include "dump/patch.h" 17 | 18 | #define BCOV_PATCH_SEG_PAD (1U) 19 | 20 | namespace bcov { 21 | 22 | class SuperBlockStore; 23 | 24 | // minimum cost for all is a rip-relative mov instruction to update coverage data 25 | // c6 05 1e 06 38 00 01 mov byte ptr [rip + 0x38061e], 1 26 | static constexpr unsigned kSBProbeCovInstSize = 7U; 27 | static constexpr unsigned kSBDetourByteSize = 5U; 28 | 29 | enum class SBProbeKind : uint8_t { 30 | kLink, // marker for function 31 | kReturn, // cost = prev inst (varies) + 1 (ret) 32 | kLongJmp, // cost = 5 (long jmp) (direct + indirect) 33 | kLongCall, // cost = call (varies) + 5 (uncond jmp ) 34 | kJumpTab, // cost = modify jump table - 5 (uncond jmp) 35 | kShortCall, // cost = prev inst (varies) + call (varies) 36 | kShortJmp, 37 | kInnerBB, // cost = varies + 5 (uncond jmp) 38 | kLongCondJmp, // cost = 2 (short cond jmp) + 2 * 5 (uncond jmp) 39 | kShortCondJmp, 40 | kGuest, // cost = host bb needs to be moved + 5 (uncond jmp) 41 | kNoHost, // failed instrumentation as no host bb found 42 | kPlainHost, // (partially) relocated host without coverage update 43 | kDummyCount 44 | }; 45 | 46 | static constexpr auto kSBProbeKindCount = (unsigned) SBProbeKind::kDummyCount; 47 | 48 | static inline bool operator<(SBProbeKind a, SBProbeKind b) 49 | { 50 | return (uint8_t) a < (uint8_t) b; 51 | } 52 | 53 | static inline SBProbeKind min(SBProbeKind a, SBProbeKind b) 54 | { 55 | return (SBProbeKind) (std::min((uint8_t) a, (uint8_t) b)); 56 | } 57 | 58 | enum class SBProbeAttr : uint8_t { 59 | kNone = 0x0, 60 | kReplacement = 0x1, // original guest is nohost, this probe is a replacement 61 | kHostProbe = 0x2, // probe is host 62 | kLargeHost = 0x4 // probe is host with large bb or probe is guest with large host probe 63 | }; 64 | 65 | static inline SBProbeAttr operator&(SBProbeAttr a, SBProbeAttr b) 66 | { 67 | return SBProbeAttr((uint8_t) a & (uint8_t) b); 68 | } 69 | 70 | static inline SBProbeAttr operator|(SBProbeAttr a, SBProbeAttr b) 71 | { 72 | return SBProbeAttr((uint8_t) a | (uint8_t) b); 73 | } 74 | 75 | static inline bool fits_detour(size_t size) 76 | { 77 | return size >= kSBDetourByteSize; 78 | } 79 | 80 | static inline bool is_instrumentable(const BasicBlock &bb) 81 | { 82 | return kSBDetourByteSize <= bb.byte_size(); 83 | } 84 | 85 | static inline bool is_instrumentable(const MCInst &inst) 86 | { 87 | return kSBDetourByteSize <= inst.size(); 88 | } 89 | 90 | czstring to_string(SBProbeKind a); 91 | 92 | enum class PatchManagerMode : uint8_t { 93 | kNone = 0x0, 94 | kLeafNode = 0x1, 95 | kAnyNode = 0x2, 96 | kJumpTab = 0x10 97 | }; 98 | 99 | czstring to_string(PatchManagerMode mode); 100 | 101 | static inline PatchManagerMode 102 | operator|(PatchManagerMode a, PatchManagerMode b) 103 | { 104 | return (PatchManagerMode) ((uint8_t) a | (uint8_t) b); 105 | } 106 | 107 | static inline PatchManagerMode 108 | operator&(PatchManagerMode a, PatchManagerMode b) 109 | { 110 | return (PatchManagerMode) ((uint8_t) a & (uint8_t) b); 111 | } 112 | 113 | static inline PatchManagerMode 114 | get_effective(PatchManagerMode mode) 115 | { 116 | return (PatchManagerMode) ((uint8_t) mode & 0x0F); 117 | } 118 | 119 | static inline bool 120 | supports_jumptab_patch(PatchManagerMode mode) 121 | { 122 | return (mode & PatchManagerMode::kJumpTab) == PatchManagerMode::kJumpTab; 123 | } 124 | 125 | //============================================================================== 126 | 127 | class SuperBlockProbe { 128 | friend class ElfPatchManager; 129 | 130 | public: 131 | SuperBlockProbe(); 132 | 133 | ~SuperBlockProbe() = default; 134 | 135 | SBProbeKind kind() const noexcept; 136 | 137 | const IFunction *function() const noexcept; 138 | 139 | size_t probe_count() const noexcept; 140 | 141 | const SuperBlockStore *super_block_store() const noexcept; 142 | 143 | const BasicBlock *basic_block() const noexcept; 144 | 145 | const JumpTable *jump_table() const noexcept; 146 | 147 | const BasicBlock *host() const noexcept; 148 | 149 | bool valid() const noexcept; 150 | 151 | bool is_link() const noexcept; 152 | 153 | uint8_t padding() const noexcept; 154 | 155 | size_t super_block_idx() const noexcept; 156 | 157 | bool is_replaced() const noexcept; 158 | 159 | bool is_attr_set(SBProbeAttr attr) const noexcept; 160 | 161 | private: 162 | 163 | struct Link { 164 | const IFunction *m_function; 165 | const SuperBlockStore *m_sb_store; 166 | uint32_t m_probe_count = 0; 167 | }; 168 | 169 | struct Default { 170 | const BasicBlock *m_detour_bb; 171 | const BasicBlock *m_host_bb; 172 | uint8_t m_padding; 173 | SBProbeAttr m_attr; 174 | uint16_t m_sb_id; 175 | }; 176 | 177 | struct JumpTab { 178 | const BasicBlock *m_detour_bb; 179 | const JumpTable *m_jumptab; 180 | uint8_t m_padding; 181 | SBProbeAttr m_attr; 182 | uint16_t m_sb_id; 183 | }; 184 | 185 | private: 186 | SBProbeKind m_kind = SBProbeKind::kLink; 187 | union { 188 | Default m_default; 189 | JumpTab m_jumptab; 190 | Link m_link; 191 | }; 192 | }; 193 | 194 | //============================================================================== 195 | 196 | class CoverageReporterBase { 197 | public: 198 | using CoverageVec = std::vector; 199 | 200 | virtual ~CoverageReporterBase() = default; 201 | 202 | virtual void 203 | report(const IFunction &function, const CoverageVec &covered_basic_blocks) = 0; 204 | 205 | virtual void init(addr_t mem_base_address, bool position_independent_code) = 0; 206 | }; 207 | 208 | class LogCoverageReporter : public CoverageReporterBase { 209 | public: 210 | ~LogCoverageReporter() override = default; 211 | 212 | void report(const IFunction &function, 213 | const CoverageVec &covered_basic_blocks) override; 214 | 215 | void init(addr_t mem_base_addr, bool position_independent_code) override; 216 | 217 | void set_report_actual_address(); 218 | 219 | private: 220 | bool m_report_actual_address = false; 221 | addr_t m_base_addr = 0; 222 | }; 223 | 224 | class OStreamCoverageReporter : public CoverageReporterBase { 225 | public: 226 | 227 | explicit OStreamCoverageReporter(std::ostream &os); 228 | 229 | ~OStreamCoverageReporter() override = default; 230 | 231 | void report(const IFunction &function, 232 | const CoverageVec &covered_basic_blocks) override; 233 | 234 | void init(addr_t mem_base_addr, bool position_independent_code) override; 235 | 236 | void set_report_actual_address(); 237 | 238 | private: 239 | std::ostream &m_ostream; 240 | bool m_report_actual_address = false; 241 | addr_t m_base_addr = 0; 242 | }; 243 | 244 | //============================================================================== 245 | 246 | class ElfPatchManager { 247 | public: 248 | 249 | using SBProbeVec = std::vector; 250 | 251 | ElfPatchManager(); 252 | 253 | ElfPatchManager(const ElfPatchManager &other) = default; 254 | 255 | ElfPatchManager &operator=(const ElfPatchManager &other) = default; 256 | 257 | ElfPatchManager(ElfPatchManager &&other) noexcept = default; 258 | 259 | ElfPatchManager &operator=(ElfPatchManager &&other) noexcept = default; 260 | 261 | virtual ~ElfPatchManager() = default; 262 | 263 | void set_mode(PatchManagerMode mode); 264 | 265 | PatchManagerMode mode() const noexcept; 266 | 267 | void build_probes(const ElfModule &module); 268 | 269 | bool patch(sstring_view infile_path, sstring_view outfile_path); 270 | 271 | size_t patch_code_seg_size() const noexcept; 272 | 273 | size_t patch_data_seg_size() const noexcept; 274 | 275 | size_t probe_count() const noexcept; 276 | 277 | void report(sstring_view data_file_name, CoverageReporterBase *reporter); 278 | 279 | SBProbeVec &probes() const noexcept; 280 | 281 | private: 282 | struct Impl; 283 | std::shared_ptr m_impl; 284 | }; 285 | 286 | } // bcov 287 | -------------------------------------------------------------------------------- /src/elf/Util.hpp: -------------------------------------------------------------------------------- 1 | /* **************************************************************************** 2 | * Copyright (c) 2018 University of Kaiserslautern. All rights reserved. 3 | * 4 | * This file is distributed under MIT license. See LICENSE.txt for details. 5 | * 6 | * ****************************************************************************/ 7 | /** 8 | * \brief 9 | */ 10 | 11 | #pragma once 12 | 13 | #include "libelfin/elf/elf++.hh" 14 | 15 | namespace elf { 16 | 17 | static inline bool 18 | is_read(const section &sec) 19 | { 20 | return (sec.get_hdr().flags & shf::write) == shf::write; 21 | } 22 | 23 | static inline bool 24 | is_alloc(const section &sec) 25 | { 26 | return (sec.get_hdr().flags & shf::alloc) == shf::alloc; 27 | } 28 | 29 | static inline bool 30 | is_executable(const section &sec) 31 | { 32 | return (sec.get_hdr().flags & shf::execinstr) == shf::execinstr; 33 | } 34 | 35 | static inline bool 36 | is_executable(const segment &seg) 37 | { 38 | return (seg.get_hdr().flags & pf::x) == pf::x; 39 | } 40 | 41 | static inline bool 42 | is_loadable(const segment &seg) 43 | { 44 | return seg.get_hdr().type == pt::load; 45 | } 46 | 47 | static inline bool 48 | is_gnu_relro(const segment &seg) 49 | { 50 | return seg.get_hdr().type == pt::gnu_relro; 51 | } 52 | 53 | static inline bool 54 | is_writable(const segment &seg) 55 | { 56 | return (seg.get_hdr().flags & pf::w) == pf::w; 57 | } 58 | 59 | static inline bool 60 | is_readable(const segment &seg) 61 | { 62 | return (seg.get_hdr().flags & pf::r) == pf::r; 63 | } 64 | 65 | } // elf 66 | -------------------------------------------------------------------------------- /src/flax/Emulator.hpp: -------------------------------------------------------------------------------- 1 | /* **************************************************************************** 2 | * Copyright (c) 2018 University of Kaiserslautern. All rights reserved. 3 | * 4 | * This file is distributed under MIT license. See LICENSE.txt for details. 5 | * 6 | * ****************************************************************************/ 7 | /** 8 | * \brief 9 | */ 10 | 11 | #pragma once 12 | 13 | #include "x64/Arch.hpp" 14 | #include 15 | 16 | #define EMULATOR_PAGE_SIZE (0x1000ULL) 17 | 18 | namespace bcov { 19 | namespace x64 { 20 | 21 | constexpr addr_t kPageMask = ((addr_t) EMULATOR_PAGE_SIZE - 1); 22 | 23 | static inline addr_t 24 | page_aligned_lower(addr_t addr) 25 | { 26 | return (addr & ~kPageMask); 27 | } 28 | 29 | static inline uint64_t 30 | page_aligned_higher(addr_t addr) 31 | { 32 | return page_aligned_lower(addr + EMULATOR_PAGE_SIZE - 1); 33 | } 34 | 35 | class EmulatorUtils { 36 | public: 37 | 38 | static void 39 | write_context(uc_engine *emulator, 40 | const RegisterContext ®_ctx); 41 | 42 | static void 43 | read_context(uc_engine *emulator, 44 | RegisterContext ®_ctx); 45 | 46 | static void 47 | write_gpr_context(uc_engine *emulator, const RegisterContext<> ®_ctx); 48 | 49 | static void 50 | read_gpr_context(uc_engine *emulator, RegisterContext<> ®_ctx); 51 | 52 | static void 53 | write_avx_context(uc_engine *emulator, const RegisterContext<> ®_ctx); 54 | 55 | static void 56 | read_avx_context(uc_engine *emulator, RegisterContext<> ®_ctx); 57 | 58 | static void 59 | write_context(uc_engine *emulator, const RegisterContext<> ®_ctx); 60 | 61 | static void 62 | read_context(uc_engine *emulator, RegisterContext<> ®_ctx); 63 | 64 | static sstring dump_gpr_context(uc_engine *emulator); 65 | }; 66 | 67 | X64Reg get_x64_reg(uc_x86_reg uc_reg) __attribute__((const)); 68 | 69 | uc_x86_reg get_uc_reg(X64Reg reg) __attribute__((const)); 70 | 71 | } // x64 72 | 73 | namespace flax { 74 | 75 | class EmulatorContext { 76 | friend class EmulatorEngine; 77 | public: 78 | 79 | EmulatorContext() = default; 80 | 81 | EmulatorContext(const EmulatorContext &other) = delete; 82 | 83 | EmulatorContext &operator=(const EmulatorContext &other) = delete; 84 | 85 | EmulatorContext(EmulatorContext &&other) noexcept; 86 | 87 | EmulatorContext &operator=(EmulatorContext &&other) noexcept; 88 | 89 | ~EmulatorContext(); 90 | 91 | bool valid() const; 92 | 93 | uc_context *get() const; 94 | 95 | private: 96 | uc_context *m_context = nullptr; 97 | }; 98 | 99 | class EmulatorEngine { 100 | public: 101 | 102 | EmulatorEngine(); 103 | 104 | EmulatorEngine(const EmulatorEngine &other) = delete; 105 | 106 | EmulatorEngine &operator=(const EmulatorEngine &other) = delete; 107 | 108 | EmulatorEngine(EmulatorEngine &&other) noexcept; 109 | 110 | EmulatorEngine &operator=(EmulatorEngine &&other) noexcept; 111 | 112 | ~EmulatorEngine(); 113 | 114 | void make_context(EmulatorContext &context); 115 | 116 | void init(uc_arch arch, uc_mode mode); 117 | 118 | bool valid() const noexcept; 119 | 120 | uc_engine *get() const noexcept; 121 | 122 | void save_context(EmulatorContext &context); 123 | 124 | void restore_context(const EmulatorContext &context); 125 | 126 | private: 127 | uc_engine *m_emulator; 128 | }; 129 | 130 | void dummy_code_callback(uc_engine *uc, uint64_t address, uint32_t size, 131 | void *user_data); 132 | 133 | void dummy_mem_callback(uc_engine *uc, uc_mem_type type, uint64_t address, int size, 134 | int64_t value, void *user_data); 135 | 136 | czstring to_string(uc_mem_type mem_type) __attribute__((const)); 137 | 138 | } // flax 139 | } // bcov 140 | -------------------------------------------------------------------------------- /src/flax/Flax.hpp: -------------------------------------------------------------------------------- 1 | /* **************************************************************************** 2 | * Copyright (c) 2018 University of Kaiserslautern. All rights reserved. 3 | * 4 | * This file is distributed under MIT license. See LICENSE.txt for details. 5 | * 6 | * ****************************************************************************/ 7 | /** 8 | * \brief 9 | */ 10 | 11 | 12 | #pragma once 13 | 14 | #include 15 | #include "elf/ElfModule.hpp" 16 | #include "core/MCInst.hpp" 17 | #include "flax/Emulator.hpp" 18 | #include "x64/Arch.hpp" 19 | #include "core/Region.hpp" 20 | 21 | struct cs_insn; 22 | 23 | namespace bcov { 24 | namespace flax { 25 | 26 | class MemorySegment { 27 | public: 28 | MemorySegment() = default; 29 | 30 | ~MemorySegment() = default; 31 | 32 | void start(addr_t address) 33 | { m_start = address; } 34 | 35 | void end(addr_t address) 36 | { m_end = address; } 37 | 38 | void buffer(buffer_t buffer) 39 | { m_buffer = const_cast(buffer); } 40 | 41 | void perms(uint32_t perms) 42 | { m_perms = (uint8_t) perms; } 43 | 44 | void file_size(size_t size) 45 | { m_file_size = (unsigned) size; } 46 | 47 | void invalidate() 48 | { m_end = 0; } 49 | 50 | buffer_t buffer() const 51 | { return m_buffer; } 52 | 53 | buffer_t buffer(addr_t addr) const 54 | { return m_buffer + (addr - m_start); } 55 | 56 | addr_t start() const 57 | { return m_start; } 58 | 59 | addr_t end() const 60 | { return m_end; } 61 | 62 | uint8_t perms() const 63 | { return m_perms; } 64 | 65 | size_t size() const 66 | { return m_end - m_start; } 67 | 68 | size_t file_size() const 69 | { return m_file_size; } 70 | 71 | bool valid() const 72 | { return m_end != 0; } 73 | 74 | bool is_inside(addr_t addr) const 75 | { return m_start <= addr && addr < m_end; } 76 | 77 | private: 78 | addr_t m_end = 0; 79 | addr_t m_start; 80 | uint8_t *m_buffer; 81 | uint8_t m_perms; 82 | unsigned m_file_size; 83 | }; 84 | 85 | class FlaxVisitorBase; 86 | 87 | class FlaxManager { 88 | public: 89 | 90 | FlaxManager(); 91 | 92 | FlaxManager(const FlaxManager &other) = default; 93 | 94 | FlaxManager &operator=(const FlaxManager &other) = default; 95 | 96 | FlaxManager(FlaxManager &&other) noexcept = default; 97 | 98 | FlaxManager &operator=(FlaxManager &&other) noexcept = default; 99 | 100 | virtual ~FlaxManager() = default; 101 | 102 | void load_module(const elf::elf &module); 103 | 104 | void reset_data_segment(); 105 | 106 | void reset_stack_segment(); 107 | 108 | bool is_readable(addr_t address) const noexcept; 109 | 110 | bool is_executable(addr_t address) const noexcept; 111 | 112 | bool is_writable(addr_t address) const noexcept; 113 | 114 | const MemorySegment &code_segment() const noexcept; 115 | 116 | const MemorySegment &data_segment() const noexcept; 117 | 118 | const MemorySegment &relro_segment() const noexcept; 119 | 120 | const MemorySegment &stack_segment() const noexcept; 121 | 122 | void set_stack_segment(addr_t base_addr, size_t size); 123 | 124 | addr_t get_mapped(addr_t orig_addr) const; 125 | 126 | addr_t get_original(addr_t mapped_addr) const; 127 | 128 | addr_t get_stack_base() const; 129 | 130 | addr_t get_stack_size() const; 131 | 132 | EmulatorEngine &engine() const; 133 | 134 | void run(FlaxVisitorBase *visitor, addr_t start, addr_t end, size_t run_count, 135 | size_t inst_count = 0); 136 | 137 | void run_promiscuous(FlaxVisitorBase *visitor, addr_t start, addr_t end, 138 | size_t run_count, size_t inst_count); 139 | 140 | void run_instructions(FlaxVisitorBase *visitor, addr_t *addresses, 141 | size_t run_count, size_t inst_count); 142 | 143 | addr_t last_reachable_address() const noexcept; 144 | 145 | size_t get_executed_instruction_count() const noexcept; 146 | 147 | void inc_executed_instruction_count(); 148 | 149 | void stop(); 150 | 151 | FlaxVisitorBase *visitor() const noexcept; 152 | 153 | private: 154 | struct Impl; 155 | std::shared_ptr m_impl; 156 | }; 157 | 158 | class FlaxVisitorBase { 159 | public: 160 | 161 | FlaxVisitorBase() = default; 162 | 163 | virtual ~FlaxVisitorBase() = default; 164 | 165 | virtual void visit_instruction(FlaxManager *mgr, addr_t address, uint32_t size); 166 | 167 | virtual void visit_valid_mem_access(FlaxManager *mgr, uc_mem_type type, 168 | uint64_t address, int size, 169 | int64_t value); 170 | 171 | virtual bool visit_invalid_mem_access(FlaxManager *mgr, uc_mem_type type, 172 | uint64_t address, int size, 173 | int64_t value); 174 | 175 | virtual bool visit_invalid_code_access(FlaxManager *mgr, uc_mem_type type, 176 | uint64_t address, int size, 177 | int64_t value); 178 | 179 | virtual bool visit_emulation_error(FlaxManager *mgr, uc_err error); 180 | 181 | virtual void visit_start(FlaxManager *mgr); 182 | 183 | virtual void visit_finish(FlaxManager *mgr); 184 | 185 | virtual void visit_run_start(FlaxManager *mgr, unsigned run_num); 186 | 187 | virtual void visit_run_finish(FlaxManager *mgr, unsigned run_num); 188 | 189 | virtual bool is_finished() const noexcept; 190 | 191 | virtual bool is_run_finished() const noexcept; 192 | 193 | virtual addr_t get_resume_address() const noexcept; 194 | 195 | }; 196 | 197 | } // flax 198 | } // bcov 199 | -------------------------------------------------------------------------------- /src/graph/CFG.cpp: -------------------------------------------------------------------------------- 1 | /* **************************************************************************** 2 | * Copyright (c) 2018 University of Kaiserslautern. All rights reserved. 3 | * 4 | * This file is distributed under MIT license. See LICENSE.txt for details. 5 | * 6 | * ****************************************************************************/ 7 | /** 8 | * \brief 9 | */ 10 | 11 | #include "CFG.hpp" 12 | #include "graph/DirectedGraph.hpp" 13 | 14 | namespace bcov { 15 | 16 | struct CFG::Impl { 17 | 18 | Impl(); 19 | 20 | ~Impl() = default; 21 | 22 | BasicBlock m_entry; 23 | BasicBlock m_exit; 24 | CFG::VertexStore m_vertex_store; 25 | CFG::Graph m_successors; 26 | CFG::Graph m_predecessors; 27 | }; 28 | 29 | CFG::Impl::Impl() 30 | { 31 | m_entry.kind(BasicBlockKind::kEntry); 32 | m_exit.kind(BasicBlockKind::kExit); 33 | } 34 | 35 | CFG::CFG() : m_impl(std::make_shared()) 36 | { 37 | 38 | } 39 | 40 | const BasicBlock * 41 | CFG::virtual_entry() const noexcept 42 | { 43 | return &m_impl->m_entry; 44 | } 45 | 46 | const BasicBlock * 47 | CFG::virtual_exit() const noexcept 48 | { 49 | return &m_impl->m_exit; 50 | } 51 | 52 | void 53 | CFG::basic_blocks(span basic_blocks) 54 | { 55 | m_impl->m_vertex_store.init(basic_blocks); 56 | m_impl->m_successors.set_vertex_store(m_impl->m_vertex_store); 57 | m_impl->m_predecessors.set_vertex_store(m_impl->m_vertex_store); 58 | m_impl->m_entry.id(basic_blocks.size()); 59 | m_impl->m_exit.id(basic_blocks.size() + 1); 60 | m_impl->m_vertex_store.insert_vertex(&m_impl->m_entry); 61 | m_impl->m_vertex_store.insert_vertex(&m_impl->m_exit); 62 | m_impl->m_successors.resize(); 63 | m_impl->m_predecessors.resize(); 64 | } 65 | 66 | void 67 | CFG::reset() 68 | { 69 | m_impl->m_vertex_store.reset(); 70 | m_impl->m_successors.reset(); 71 | m_impl->m_predecessors.reset(); 72 | } 73 | 74 | const CFG::Edges & 75 | CFG::successors(const BasicBlock &bb) const 76 | { 77 | return m_impl->m_successors.get_edges(bb); 78 | } 79 | 80 | const CFG::Edges & 81 | CFG::predecessors(const BasicBlock &bb) const 82 | { 83 | return m_impl->m_predecessors.get_edges(bb); 84 | } 85 | 86 | void 87 | CFG::insert_edge(const BasicBlock &src, const BasicBlock &dst) 88 | { 89 | m_impl->m_successors.insert_edge(src, dst); 90 | m_impl->m_predecessors.insert_edge(dst, src); 91 | } 92 | 93 | void 94 | CFG::reset_padding_edges(const BasicBlock &bb) 95 | { 96 | auto succ = m_impl->m_successors.get_edges(bb)[0]; 97 | m_impl->m_predecessors.remove_edge(*succ, bb); 98 | reset_edges(bb); 99 | } 100 | 101 | void 102 | CFG::reset_edges(const BasicBlock &bb) 103 | { 104 | m_impl->m_successors.clear_edges(bb); 105 | m_impl->m_predecessors.clear_edges(bb); 106 | } 107 | 108 | void 109 | CFG::add_entry_block(const BasicBlock &bb) 110 | { 111 | insert_edge(m_impl->m_entry, bb); 112 | } 113 | 114 | void 115 | CFG::add_exit_block(const BasicBlock &bb) 116 | { 117 | insert_edge(bb, m_impl->m_exit); 118 | } 119 | 120 | const CFG::Graph & 121 | CFG::forward() const noexcept 122 | { 123 | return m_impl->m_successors; 124 | } 125 | 126 | const CFG::Graph & 127 | CFG::backward() const noexcept 128 | { 129 | return m_impl->m_predecessors; 130 | } 131 | 132 | size_t 133 | CFG::size() const noexcept 134 | { 135 | return m_impl->m_vertex_store.vertices().size(); 136 | } 137 | 138 | } // bcov 139 | -------------------------------------------------------------------------------- /src/graph/CFG.hpp: -------------------------------------------------------------------------------- 1 | /* **************************************************************************** 2 | * Copyright (c) 2018 University of Kaiserslautern. All rights reserved. 3 | * 4 | * This file is distributed under MIT license. See LICENSE.txt for details. 5 | * 6 | * ****************************************************************************/ 7 | /** 8 | * \brief 9 | */ 10 | 11 | 12 | #pragma once 13 | 14 | #include "core/Common.hpp" 15 | #include "core/BasicBlock.hpp" 16 | #include "graph/DirectedGraph.hpp" 17 | 18 | namespace bcov { 19 | 20 | template<> 21 | struct identify { 22 | size_t operator()(const BasicBlock &bb) const noexcept 23 | { 24 | return bb.id(); 25 | } 26 | }; 27 | 28 | using BBPtrVec = std::vector; 29 | 30 | class CFG { 31 | friend class FunctionBuilder; 32 | 33 | public: 34 | using Node = BasicBlock; 35 | using Graph = OrderedGraph>; 36 | using Edges = Graph::Edges; 37 | using VertexStore = Graph::VertexStore; 38 | using Vertices = Graph::VertexStore::Vertices; 39 | 40 | CFG(); 41 | 42 | CFG(const CFG &other) = default; 43 | 44 | CFG &operator=(const CFG &other) = default; 45 | 46 | CFG(CFG &&other) noexcept = default; 47 | 48 | CFG &operator=(CFG &&other) noexcept = default; 49 | 50 | virtual ~CFG() = default; 51 | 52 | //=============================================== 53 | 54 | const BasicBlock *virtual_entry() const noexcept; 55 | 56 | const BasicBlock *virtual_exit() const noexcept; 57 | 58 | const Edges &successors(const BasicBlock &bb) const; 59 | 60 | const Edges &predecessors(const BasicBlock &bb) const; 61 | 62 | void insert_edge(const BasicBlock &src, const BasicBlock &dst); 63 | 64 | void reset_padding_edges(const BasicBlock &bb); 65 | 66 | void reset_edges(const BasicBlock &bb); 67 | 68 | void reset(); 69 | 70 | const Graph &forward() const noexcept; 71 | 72 | const Graph &backward() const noexcept; 73 | 74 | size_t size() const noexcept; 75 | 76 | protected: 77 | void basic_blocks(span basic_blocks); 78 | 79 | /// supports defining functions with multiple entry blocks. In practice, 80 | /// there should only one entry. 81 | void add_entry_block(const BasicBlock &bb); 82 | 83 | void add_exit_block(const BasicBlock &bb); 84 | 85 | private: 86 | struct Impl; 87 | std::shared_ptr m_impl; 88 | 89 | }; 90 | 91 | } // bcov 92 | -------------------------------------------------------------------------------- /src/graph/DominatorTree.cpp: -------------------------------------------------------------------------------- 1 | /* **************************************************************************** 2 | * Copyright (c) 2018 University of Kaiserslautern. All rights reserved. 3 | * 4 | * This file is distributed under MIT license. See LICENSE.txt for details. 5 | * 6 | * ****************************************************************************/ 7 | /** 8 | * \brief Implentation of SEMI-NCA dominator tree algorithm based on 9 | * 10 | * - L. Georgiadis. "Linear-Time Algorithms for Dominators and Related Problems". 11 | * PhD thesis, Princeton University, 2005. 12 | * 13 | */ 14 | 15 | #include "DominatorTree.hpp" 16 | #include "core/Function.hpp" 17 | #include "easylogging/easylogging++.h" 18 | #include "Dot.hpp" 19 | 20 | #define DFS_INDEX_START (1) 21 | #define NODE_ID(x) Identify()(*(x)) 22 | 23 | namespace bcov { 24 | 25 | using NodePtrVec = std::vector; 26 | 27 | /// @brief depth-first search marker for dominator tree 28 | template> 29 | class DTDFSMarker : public GraphVisitorBase { 30 | using NodePtrVec = std::vector; 31 | 32 | public: 33 | DTDFSMarker(NodePtrVec &ordered_vertices, NodePtrVec &parents, 34 | std::vector &dfnums); 35 | 36 | void visit_preorder(const T &node, const T *parent) override; 37 | 38 | private: 39 | NodePtrVec &m_ordered_vertices; 40 | NodePtrVec &m_parents; 41 | std::vector &m_dfnums; 42 | unsigned m_cur_num; 43 | }; 44 | 45 | template 46 | DTDFSMarker::DTDFSMarker(NodePtrVec &ordered_vertices, 47 | NodePtrVec &parents, 48 | std::vector &dfnums) 49 | : m_ordered_vertices(ordered_vertices), m_parents(parents), m_dfnums(dfnums), 50 | m_cur_num(DFS_INDEX_START) 51 | { } 52 | 53 | template 54 | void 55 | DTDFSMarker::visit_preorder(const T &node, const T *parent) 56 | { 57 | m_parents[NODE_ID(&node)] = parent; 58 | m_dfnums[NODE_ID(&node)] = m_cur_num; 59 | m_ordered_vertices.push_back(&node); 60 | m_cur_num++; 61 | } 62 | 63 | template> 64 | struct DominatorTreeBuildHelper { 65 | using NodePtrVec = std::vector; 66 | using Graph = OrderedGraph; 67 | 68 | explicit DominatorTreeBuildHelper(NodePtrVec &idoms) 69 | : m_idoms(idoms) 70 | { } 71 | 72 | ~DominatorTreeBuildHelper() = default; 73 | 74 | void init(const Graph &forward_graph, const T *root); 75 | 76 | const NodePtrVec &ordered_vertices() const 77 | { return m_ordered_vertices; } 78 | 79 | const CFG::Node *sdom(const T *node) const noexcept 80 | { return m_sdom[NODE_ID(node)]; } 81 | 82 | void set_sdom(const T *node, const T *sdom) 83 | { m_sdom[NODE_ID(node)] = sdom; } 84 | 85 | const CFG::Node *parent(const T *node) const noexcept 86 | { return m_idoms[NODE_ID(node)]; } 87 | 88 | unsigned depth(const CFG::Node *node) const 89 | { return m_dfnums[NODE_ID(node)]; } 90 | 91 | unsigned eval(const T *pred, unsigned last_linked); 92 | 93 | bool validate_dom_tree(const Graph &backward_graph); 94 | 95 | const T *get_node_at(unsigned dfs_depth) const 96 | { return m_ordered_vertices[dfs_depth - DFS_INDEX_START]; } 97 | 98 | void build(const Graph &backward_graph); 99 | 100 | NodePtrVec &m_idoms; 101 | NodePtrVec m_ordered_vertices; 102 | NodePtrVec m_sdom; 103 | std::vector m_dfnums; 104 | }; 105 | 106 | template 107 | unsigned 108 | DominatorTreeBuildHelper::eval(const T *pred, unsigned last_linked) 109 | { 110 | if (depth(pred) < last_linked) { 111 | // forward arcs 112 | return depth(pred); 113 | } 114 | // cross and back arcs 115 | // TODO: evaluate path compression 116 | auto result = depth(sdom(pred)); 117 | do { 118 | if (depth(sdom(pred)) < result) { 119 | result = depth(sdom(pred)); 120 | } 121 | pred = parent(pred); 122 | } while (depth(pred) > last_linked); 123 | return result; 124 | } 125 | 126 | template 127 | void 128 | DominatorTreeBuildHelper::init(const Graph &forward_graph, 129 | const T *root) 130 | { 131 | auto vec_size = forward_graph.get_vertices().size(); 132 | m_idoms.resize(vec_size, nullptr); 133 | m_ordered_vertices.reserve(vec_size); 134 | m_dfnums.resize(vec_size, 0); 135 | 136 | // preorder sort nodes in m_ordered_vertices 137 | DTDFSMarker dfst_marker(m_ordered_vertices, m_idoms, m_dfnums); 138 | forward_graph.traverse_depth_first(dfst_marker, *root); 139 | m_sdom = m_idoms; 140 | } 141 | 142 | template 143 | bool 144 | DominatorTreeBuildHelper::validate_dom_tree(const Graph &backward_graph) 145 | { 146 | // Implementation of dominator tree algorithm using iterative dataflow 147 | // based on Cooper et. al. "A Simple, Fast Dominance Algorithm". TR-06-33870 148 | 149 | // Note that the published algorithm in Figure 3 has a typo in method *intersect*. 150 | // Conditions for advancing fingers must be inversed. 151 | 152 | NodePtrVec v_idoms; 153 | v_idoms.resize(backward_graph.get_vertices().size(), nullptr); 154 | auto entry_node = m_ordered_vertices.front(); 155 | v_idoms[NODE_ID(entry_node)] = entry_node; 156 | bool changed = true; 157 | while (changed) { 158 | changed = false; 159 | for (auto it = m_ordered_vertices.begin() + 1; 160 | it != m_ordered_vertices.end(); ++it) { 161 | const T *n = (*it); 162 | auto candidate = backward_graph.get_edges(*n).back(); 163 | for (const auto &p : backward_graph.get_edges(*n)) { 164 | if (v_idoms[NODE_ID(p)] == nullptr || depth(p) == 0) { 165 | // skip unvisited parents and infinite loops in postdom 166 | continue; 167 | } 168 | if (depth(candidate) == 0) { 169 | candidate = p; 170 | continue; 171 | } 172 | auto finger1 = candidate; 173 | auto finger2 = p; 174 | while (finger1 != nullptr && finger2 != nullptr && 175 | *finger1 != *finger2) { 176 | if (depth(finger1) > depth(finger2)) { 177 | finger1 = v_idoms[NODE_ID(finger1)]; 178 | } 179 | if (finger1 != nullptr && depth(finger2) > depth(finger1)) { 180 | finger2 = v_idoms[NODE_ID(finger2)]; 181 | } 182 | } 183 | candidate = (finger1 != nullptr) ? finger1 : finger2; 184 | } 185 | if (v_idoms[NODE_ID(n)] == nullptr || 186 | (*v_idoms[NODE_ID(n)]) != *candidate) { 187 | v_idoms[NODE_ID(n)] = candidate; 188 | changed = true; 189 | } 190 | } 191 | } 192 | 193 | bool result = true; 194 | for (auto it = m_ordered_vertices.begin() + 1; 195 | it != m_ordered_vertices.end(); ++it) { 196 | if (*v_idoms[NODE_ID(*it)] != *m_idoms[NODE_ID(*it)]) { 197 | DLOG(ERROR) << "domtree: invalid parent for bb @ " << NODE_ID(*it) 198 | << " found " << NODE_ID(m_idoms[NODE_ID(*it)]) 199 | << " should be " << NODE_ID(v_idoms[NODE_ID(*it)]); 200 | result = false; 201 | } 202 | } 203 | return result; 204 | } 205 | 206 | template 207 | void DominatorTreeBuildHelper::build( 208 | const DominatorTreeBuildHelper::Graph &backward_graph) 209 | { 210 | DCHECK(ordered_vertices().size() > 2); 211 | for (unsigned i = (unsigned) ordered_vertices().size() - 1; i > 1; --i) { 212 | auto n = ordered_vertices()[i]; 213 | auto semi = depth(sdom(n)); 214 | for (const auto u : backward_graph.get_edges(*n)) { 215 | unsigned v = eval(u, i + 1); 216 | LOG_IF(v == 0, INFO) << "infinite loop head detected @ " 217 | << to_hex(u->address()); 218 | if (v != 0 && v < semi) { 219 | semi = v; 220 | } 221 | } 222 | CHECK(semi > 0); 223 | set_sdom(n, get_node_at(semi)); 224 | } 225 | 226 | for (auto it = ordered_vertices().begin() + 1; 227 | it != ordered_vertices().end(); ++it) { 228 | auto idom = m_idoms[NODE_ID(*it)]; 229 | while (depth(idom) > depth(sdom(*it))) { 230 | idom = m_idoms[NODE_ID(idom)]; 231 | } 232 | m_idoms[NODE_ID(*it)] = idom; 233 | } 234 | 235 | DLOG_IF(!validate_dom_tree(backward_graph), ERROR) << "dominator tree mismatch!"; 236 | } 237 | 238 | //=============================================== 239 | 240 | struct DominatorTree::Impl { 241 | 242 | Impl(); 243 | 244 | ~Impl() = default; 245 | 246 | const CFG::Node *idom(const CFG::Node *node) const 247 | { return m_idoms[node->id()]; } 248 | 249 | const CFG::Node *m_root; 250 | CFG::Graph m_dom_tree; 251 | CFG::VertexStore m_vertex_store; 252 | std::vector m_idoms; 253 | }; 254 | 255 | DominatorTree::Impl::Impl() 256 | : m_root(nullptr), m_dom_tree(), m_idoms() 257 | { } 258 | 259 | DominatorTree::DominatorTree() 260 | : m_impl(std::make_shared()) 261 | { } 262 | 263 | const CFG::Node * 264 | DominatorTree::root() const noexcept 265 | { 266 | return m_impl->m_root; 267 | } 268 | 269 | const CFG::Node * 270 | DominatorTree::idom(const CFG::Node *bb) const noexcept 271 | { 272 | return m_impl->m_idoms[bb->id()]; 273 | } 274 | 275 | const CFG::Graph & 276 | DominatorTree::tree() const noexcept 277 | { 278 | return m_impl->m_dom_tree; 279 | } 280 | 281 | bool 282 | DominatorTree::dominates(const CFG::Node *a, const CFG::Node *b) const noexcept 283 | { 284 | if (a->is_virtual() || b->is_virtual()) { 285 | return false; 286 | } 287 | auto current = b; 288 | while (current != nullptr && !current->is_virtual()) { 289 | if (*current == *a) { 290 | return true; 291 | } 292 | current = idom(current); 293 | } 294 | return false; 295 | } 296 | 297 | bool 298 | DominatorTree::valid() const noexcept 299 | { 300 | return m_impl->m_root != nullptr; 301 | } 302 | 303 | DominatorTree 304 | DominatorTreeBuilder::build(const CFG::Node *root, const CFG::Graph &forward_graph, 305 | const CFG::Graph &backward_graph) 306 | { 307 | DominatorTree instance; 308 | DominatorTree::Impl &impl = *instance.m_impl; 309 | impl.m_root = root; 310 | DominatorTreeBuildHelper bd(impl.m_idoms); 311 | bd.init(forward_graph, root); 312 | 313 | bd.build(backward_graph); 314 | 315 | impl.m_vertex_store.init(forward_graph.get_vertices()); 316 | impl.m_dom_tree.set_vertex_store(impl.m_vertex_store); 317 | for (auto it = bd.ordered_vertices().begin() + 1; 318 | it != bd.ordered_vertices().end(); ++it) { 319 | auto node = *it; 320 | impl.m_dom_tree.insert_edge(*impl.idom(node), *node); 321 | } 322 | return instance; 323 | } 324 | 325 | } // bcov 326 | -------------------------------------------------------------------------------- /src/graph/DominatorTree.hpp: -------------------------------------------------------------------------------- 1 | /* **************************************************************************** 2 | * Copyright (c) 2018 University of Kaiserslautern. All rights reserved. 3 | * 4 | * This file is distributed under MIT license. See LICENSE.txt for details. 5 | * 6 | * ****************************************************************************/ 7 | /** 8 | * \brief 9 | */ 10 | 11 | 12 | #pragma once 13 | 14 | #include "CFG.hpp" 15 | 16 | namespace bcov { 17 | 18 | class DominatorTree { 19 | friend class DominatorTreeBuilder; 20 | 21 | public: 22 | 23 | DominatorTree(); 24 | 25 | DominatorTree(const DominatorTree &other) = default; 26 | 27 | DominatorTree &operator=(const DominatorTree &other) = default; 28 | 29 | DominatorTree(DominatorTree &&other) noexcept = default; 30 | 31 | DominatorTree &operator=(DominatorTree &&other) noexcept = default; 32 | 33 | virtual ~DominatorTree() = default; 34 | 35 | const CFG::Node *root() const noexcept; 36 | 37 | const CFG::Node *idom(const CFG::Node *bb) const noexcept; 38 | 39 | const CFG::Graph &tree() const noexcept; 40 | 41 | bool dominates(const CFG::Node *a, const CFG::Node *b) const noexcept; 42 | 43 | bool valid() const noexcept; 44 | 45 | private: 46 | struct Impl; 47 | std::shared_ptr m_impl; 48 | }; 49 | 50 | class DominatorTreeBuilder { 51 | public: 52 | 53 | DominatorTree build(const CFG::Node *root, const CFG::Graph &forward_graph, 54 | const CFG::Graph &backward_graph); 55 | 56 | }; 57 | 58 | } // bcov 59 | -------------------------------------------------------------------------------- /src/graph/Dot.hpp: -------------------------------------------------------------------------------- 1 | /* **************************************************************************** 2 | * Copyright (c) 2018 University of Kaiserslautern. All rights reserved. 3 | * 4 | * This file is distributed under MIT license. See LICENSE.txt for details. 5 | * 6 | * ****************************************************************************/ 7 | /** 8 | * \brief 9 | */ 10 | 11 | 12 | #pragma once 13 | 14 | #include "graph/CFG.hpp" 15 | #include "graph/SuperBlock.hpp" 16 | 17 | #define DOT_QUOTE_WORD(x) "\"" << x << "\"" 18 | #define DOT_ATTR_SEP ", " 19 | #define DOT_NEWLINE "\\l" 20 | 21 | namespace bcov { 22 | namespace dot { 23 | 24 | enum class ValueType { 25 | kQuoted, 26 | KPlain 27 | }; 28 | 29 | struct KeyValue { 30 | KeyValue() = default; 31 | 32 | ~KeyValue() = default; 33 | 34 | KeyValue(sstring_view key, sstring_view value, 35 | ValueType vtype = ValueType::kQuoted) 36 | : key(key.data()), value(value.data()), type(vtype) 37 | { } 38 | 39 | sstring key; 40 | sstring value; 41 | ValueType type; 42 | }; 43 | 44 | using KeyValueList = std::vector; 45 | using PropertyList = std::vector>; 46 | 47 | void 48 | write_attribute(std::ostream &out, const KeyValue &attr); 49 | 50 | void 51 | write_attribute_list(std::ostream &out, const KeyValueList &attrs); 52 | 53 | void 54 | write_property_list(std::ostream &out, const PropertyList &props); 55 | 56 | void 57 | write_edge(std::ostream &out, sstring_view src, sstring_view dst, 58 | const KeyValueList &attrs); 59 | 60 | void 61 | write_node(std::ostream &out, sstring_view node, const KeyValueList &attrs); 62 | 63 | void 64 | write_dot_header(std::ostream &out, sstring_view graph_name); 65 | 66 | void 67 | write_cfg(std::ostream &out, sstring_view graph_name, 68 | const CFG::Node *root, const CFG::Graph &graph); 69 | 70 | void 71 | write_cfg(sstring_view file_name, sstring_view graph_name, 72 | const CFG::Node *root, const CFG::Graph &graph); 73 | 74 | void 75 | write_domtree(std::ostream &out, sstring_view graph_name, 76 | const CFG::Node *root, const CFG::Graph &graph); 77 | 78 | void 79 | write_domtree(sstring_view file_name, sstring_view graph_name, 80 | const CFG::Node *root, const CFG::Graph &graph); 81 | 82 | void 83 | write_sb_graph(std::ostream &out, sstring_view graph_name, 84 | const SuperBlock *root, const SuperBlockStore::Graph &graph); 85 | 86 | void 87 | write_sb_graph(sstring_view file_name, sstring_view graph_name, 88 | const SuperBlock *root, const SuperBlockStore::Graph &graph); 89 | 90 | } // dot 91 | } // bcov 92 | -------------------------------------------------------------------------------- /src/graph/SuperBlock.hpp: -------------------------------------------------------------------------------- 1 | /* **************************************************************************** 2 | * Copyright (c) 2018 University of Kaiserslautern. All rights reserved. 3 | * 4 | * This file is distributed under MIT license. See LICENSE.txt for details. 5 | * 6 | * ****************************************************************************/ 7 | /** 8 | * \brief 9 | */ 10 | 11 | 12 | #pragma once 13 | 14 | 15 | #include "core/Common.hpp" 16 | #include "graph/CFG.hpp" 17 | 18 | BCOV_FORWARD(IFunction) 19 | 20 | namespace bcov { 21 | 22 | enum class SuperBlockKind : unsigned char { 23 | kNone = 0, 24 | kAllNode, 25 | kAnyNode 26 | }; 27 | 28 | static inline bool is_instrumentable(SuperBlockKind kind) 29 | { 30 | return kind != SuperBlockKind::kNone; 31 | } 32 | 33 | class SuperBlock { 34 | friend class SuperBlockStoreBuilder; 35 | 36 | public: 37 | using Idx = BasicBlock::Idx; 38 | 39 | SuperBlock(); 40 | 41 | ~SuperBlock() = default; 42 | 43 | const BBPtrVec &basic_blocks() const noexcept; 44 | 45 | bool exists(const BasicBlock *bb) const noexcept; 46 | 47 | SuperBlockKind kind() const noexcept; 48 | 49 | Idx idx() const noexcept; 50 | 51 | bool valid() const noexcept; 52 | 53 | bool is_virtual_root() const noexcept; 54 | 55 | private: 56 | SuperBlockKind m_kind; 57 | Idx m_idx; 58 | BBPtrVec m_basic_blocks; 59 | }; 60 | 61 | template<> 62 | struct identify { 63 | size_t operator()(const SuperBlock &sb) const noexcept 64 | { 65 | return sb.idx(); 66 | } 67 | }; 68 | 69 | /// @brief owns super-blocks and their graphs 70 | class SuperBlockStore { 71 | friend class SuperBlockStoreBuilder; 72 | 73 | public: 74 | using Node = SuperBlock; 75 | using Graph = OrderedGraph>; 76 | using Edges = Graph::Edges; 77 | using VertexStore = OrderedVertexStore; 78 | using Vertices = VertexStore::Vertices; 79 | 80 | SuperBlockStore(); 81 | 82 | ~SuperBlockStore() = default; 83 | 84 | const Node *virtual_root() const noexcept; 85 | 86 | const Graph &forward_dom_graph() const noexcept; 87 | 88 | const Graph &backward_dom_graph() const noexcept; 89 | 90 | span super_blocks() const noexcept; 91 | 92 | /// @brief return true if a is predecessor to b in the dominator graph 93 | bool dominates(const Node &a, const Node &b) const; 94 | 95 | const Edges &get_dominators(const Node &a) const; 96 | 97 | const SuperBlock *get_super_block(const BasicBlock *bb) const noexcept; 98 | 99 | bool valid() const noexcept; 100 | 101 | private: 102 | struct Impl; 103 | std::shared_ptr m_impl; 104 | }; 105 | 106 | static inline bool operator==(const SuperBlock &a, const SuperBlock &b) 107 | { 108 | return a.idx() == b.idx(); 109 | } 110 | 111 | static inline bool operator!=(const SuperBlock &a, const SuperBlock &b) 112 | { 113 | return !(a == b); 114 | } 115 | 116 | class SuperBlockStoreBuilder { 117 | public: 118 | static SuperBlockStore build(const IFunction *function); 119 | 120 | struct Impl; 121 | }; 122 | 123 | } // bcov 124 | -------------------------------------------------------------------------------- /src/main/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "core/Disassembler.hpp" 4 | #include "util/BcovConfigParser.hpp" 5 | #include "util/ProgOptions.hpp" 6 | #include "graph/Dot.hpp" 7 | #include "elf/ElfPatchManager.hpp" 8 | #include "elf/ElfModule.hpp" 9 | #include "util/Logging.hpp" 10 | 11 | 12 | namespace bcov { 13 | 14 | void 15 | dump_program_graphs_of_single_function(const IFunction &function) 16 | { 17 | auto graph_name = "func_" + to_hex(function.address()); 18 | auto cfg_path = graph_name + ".cfg.dot"; 19 | auto rev_cfg_path = graph_name + ".rev.cfg.dot"; 20 | auto pre_dom_tree_path = graph_name + ".pre.dom.dot"; 21 | auto post_dom_tree_path = graph_name + ".post.dom.dot"; 22 | auto sb_dom_path = graph_name + ".sb.dom.dot"; 23 | 24 | dot::write_cfg(cfg_path, 25 | graph_name, 26 | function.cfg().virtual_entry(), 27 | function.cfg().forward()); 28 | 29 | dot::write_cfg(rev_cfg_path, 30 | graph_name, 31 | function.cfg().virtual_exit(), 32 | function.cfg().backward()); 33 | 34 | dot::write_domtree(pre_dom_tree_path, 35 | graph_name, 36 | function.predominator().root(), 37 | function.predominator().tree()); 38 | 39 | dot::write_domtree(post_dom_tree_path, 40 | graph_name, 41 | function.postdominator().root(), 42 | function.postdominator().tree()); 43 | 44 | auto sb_store = SuperBlockStoreBuilder::build(&function); 45 | 46 | dot::write_sb_graph(sb_dom_path, 47 | graph_name, 48 | sb_store.virtual_root(), 49 | sb_store.forward_dom_graph()); 50 | 51 | std::cout << function.name() << ": program graphs dumped successfully. \n"; 52 | } 53 | 54 | } // bcov 55 | 56 | int main(int argc, const char **argv) 57 | { 58 | using namespace bcov; 59 | ProgOptions options = ProgOptions::parse(argc, argv); 60 | initialize_logging(options.log_file().data(), options.verbosity()); 61 | 62 | bcov::ElfModule module; 63 | if (options.config_file().empty() && options.selected_function().empty()) { 64 | LOG(INFO) << "bcov will probe all static functions"; 65 | module = ElfModuleBuilder::build(options.input_file()); 66 | } else { 67 | BcovConfigParser config_parser; 68 | BcovConfig config = config_parser.parse(options.config_file()); 69 | if (config.functions().empty() && !options.selected_function().empty()) { 70 | config.add_function(options.selected_function()); 71 | } 72 | module = ElfModuleBuilder::build(options.input_file(), config); 73 | } 74 | 75 | if (options.program_mode() == ProgramMode::kDump) { 76 | if (options.selected_function().empty()) { 77 | std::cerr << "please provide a function name!"; 78 | exit(EXIT_FAILURE); 79 | } 80 | 81 | auto func = module.get_instrumented_function(options.selected_function()); 82 | if (!func.is_valid()) { 83 | std::cerr << options.selected_function() 84 | << ": selected function not found! \n"; 85 | exit(EXIT_FAILURE); 86 | } 87 | 88 | dump_program_graphs_of_single_function(func); 89 | exit(EXIT_SUCCESS); 90 | } 91 | 92 | DCHECK(options.program_mode() == ProgramMode::kReport || 93 | options.program_mode() == ProgramMode::kPatch); 94 | 95 | auto mgr_mode = options.operation_params() == OperationParams::kAllNode 96 | ? PatchManagerMode::kLeafNode : PatchManagerMode::kAnyNode; 97 | 98 | ElfPatchManager patch_mgr; 99 | patch_mgr.set_mode(mgr_mode | PatchManagerMode::kJumpTab); 100 | patch_mgr.build_probes(module); 101 | 102 | if (patch_mgr.probes().empty()) { 103 | std::cout << "weird! no probes identified!"; 104 | exit(EXIT_SUCCESS); 105 | } 106 | 107 | if (options.program_mode() == ProgramMode::kReport) { 108 | if (options.output_file().empty()) { 109 | OStreamCoverageReporter coverage_reporter(std::cout); 110 | coverage_reporter.set_report_actual_address(); 111 | patch_mgr.report(options.data_file(), &coverage_reporter); 112 | } else { 113 | std::ofstream out_file; 114 | out_file.open(options.output_file().data(), std::ofstream::out); 115 | if (!out_file.good()) { 116 | std::cerr << "can not open output file: " << options.output_file(); 117 | exit(EXIT_FAILURE); 118 | } 119 | OStreamCoverageReporter coverage_reporter(out_file); 120 | coverage_reporter.set_report_actual_address(); 121 | patch_mgr.report(options.data_file(), &coverage_reporter); 122 | } 123 | exit(EXIT_SUCCESS); 124 | } 125 | 126 | try { 127 | auto success = patch_mgr.patch(options.input_file(), options.output_file()); 128 | if (!success) { 129 | std::cout << "can not patch input file!\n"; 130 | exit(0); 131 | } 132 | } catch (const std::exception &exp) { 133 | std::cerr << "fatal error: " << exp.what(); 134 | exit(1); 135 | } 136 | 137 | std::cout << "file patched successfully\n"; 138 | return 0; 139 | } 140 | 141 | -------------------------------------------------------------------------------- /src/util/BcovConfig.cpp: -------------------------------------------------------------------------------- 1 | /* **************************************************************************** 2 | * Copyright (c) 2018 University of Kaiserslautern. All rights reserved. 3 | * 4 | * This file is distributed under MIT license. See LICENSE.txt for details. 5 | * 6 | * ****************************************************************************/ 7 | /** 8 | * \brief 9 | */ 10 | 11 | #include "BcovConfig.hpp" 12 | 13 | namespace bcov { 14 | 15 | FuncConfigItem::FuncConfigItem(sstring_view func_name) 16 | : m_func_name(func_name.data()), 17 | m_jump_table_map() 18 | { 19 | } 20 | 21 | void 22 | FuncConfigItem::add_jump_table(addr_t inst_addr, std::vector &targets) 23 | { 24 | m_jump_table_map.emplace(std::make_pair(inst_addr, std::move(targets))); 25 | } 26 | 27 | const sstring & 28 | FuncConfigItem::func_name() const noexcept 29 | { 30 | return m_func_name; 31 | } 32 | 33 | bool 34 | FuncConfigItem::has_jump_tables() const noexcept 35 | { 36 | return !m_jump_table_map.empty(); 37 | } 38 | 39 | ConfJumpTabIter 40 | FuncConfigItem::begin() const noexcept 41 | { 42 | return m_jump_table_map.cbegin(); 43 | } 44 | 45 | ConfJumpTabIter 46 | FuncConfigItem::end() const noexcept 47 | { 48 | return m_jump_table_map.cend(); 49 | } 50 | 51 | const ConfJumpTabMap & 52 | FuncConfigItem::jump_tables() const noexcept 53 | { 54 | return m_jump_table_map; 55 | } 56 | 57 | bool 58 | FuncConfigItem::is_valid() const noexcept 59 | { 60 | return !m_func_name.empty(); 61 | } 62 | 63 | std::ostream &operator<<(std::ostream &os, const FuncConfigItem &item) 64 | { 65 | os << "Function: " << item.m_func_name << "\n"; 66 | for (const auto &jump_tab_pair : item.m_jump_table_map) { 67 | os << "Jump Table: " << std::hex << std::showbase << jump_tab_pair.first 68 | << " | "; 69 | for (auto target : jump_tab_pair.second) { 70 | os << std::hex << std::showbase << target << " "; 71 | } 72 | os << "\n"; 73 | } 74 | return os; 75 | } 76 | 77 | FuncConfigItem * 78 | BcovConfig::add_function(sstring_view func_name) 79 | { 80 | m_func_items.emplace_back(FuncConfigItem(func_name)); 81 | return (&m_func_items.back()); 82 | } 83 | 84 | span 85 | BcovConfig::functions() const noexcept 86 | { 87 | return m_func_items; 88 | } 89 | } // bcov 90 | -------------------------------------------------------------------------------- /src/util/BcovConfig.hpp: -------------------------------------------------------------------------------- 1 | /* **************************************************************************** 2 | * Copyright (c) 2018 University of Kaiserslautern. All rights reserved. 3 | * 4 | * This file is distributed under MIT license. See LICENSE.txt for details. 5 | * 6 | * ****************************************************************************/ 7 | /** 8 | * \brief 9 | */ 10 | #pragma once 11 | 12 | #include "core/Common.hpp" 13 | #include 14 | 15 | namespace bcov { 16 | 17 | using ConfJumpTabTargets = std::vector; 18 | using ConfJumpTabMap = std::unordered_map; 19 | using ConfJumpTabIter = ConfJumpTabMap::const_iterator; 20 | 21 | class FuncConfigItem { 22 | public: 23 | 24 | FuncConfigItem() = default; 25 | 26 | explicit FuncConfigItem(sstring_view func_name); 27 | 28 | FuncConfigItem(const FuncConfigItem &other) = default; 29 | 30 | FuncConfigItem &operator=(const FuncConfigItem &other) = default; 31 | 32 | FuncConfigItem(FuncConfigItem &&other) noexcept = default; 33 | 34 | FuncConfigItem &operator=(FuncConfigItem &&other) noexcept = default; 35 | 36 | ~FuncConfigItem() = default; 37 | 38 | void add_jump_table(addr_t inst_addr, ConfJumpTabTargets &targets); 39 | 40 | const sstring &func_name() const noexcept; 41 | 42 | bool has_jump_tables() const noexcept; 43 | 44 | ConfJumpTabIter begin() const noexcept; 45 | 46 | ConfJumpTabIter end() const noexcept; 47 | 48 | const ConfJumpTabMap &jump_tables() const noexcept; 49 | 50 | bool is_valid() const noexcept; 51 | 52 | friend std::ostream & 53 | operator<<(std::ostream &stream, const FuncConfigItem &matrix); 54 | 55 | private: 56 | sstring m_func_name; 57 | ConfJumpTabMap m_jump_table_map; 58 | }; 59 | 60 | class BcovConfig { 61 | public: 62 | 63 | BcovConfig() = default; 64 | 65 | BcovConfig(const BcovConfig &other) = default; 66 | 67 | BcovConfig &operator=(const BcovConfig &other) = default; 68 | 69 | BcovConfig(BcovConfig &&other) noexcept = default; 70 | 71 | BcovConfig &operator=(BcovConfig &&other) noexcept = default; 72 | 73 | virtual ~BcovConfig() = default; 74 | 75 | FuncConfigItem *add_function(sstring_view func_name); 76 | 77 | span functions() const noexcept; 78 | 79 | private: 80 | std::vector m_func_items; 81 | }; 82 | } 83 | 84 | -------------------------------------------------------------------------------- /src/util/BcovConfigParser.cpp: -------------------------------------------------------------------------------- 1 | /* **************************************************************************** 2 | * Copyright (c) 2018 University of Kaiserslautern. All rights reserved. 3 | * 4 | * This file is distributed under MIT license. See LICENSE.txt for details. 5 | * 6 | * ****************************************************************************/ 7 | /** 8 | * \brief 9 | */ 10 | 11 | #include "BcovConfigParser.hpp" 12 | #include 13 | 14 | 15 | namespace bcov { 16 | 17 | BcovConfigParser::BcovConfigParser() : 18 | m_func_name_regex("[._[:alnum:]]+"), 19 | m_address_regex("0x[[:xdigit:]]{2,16}") 20 | { } 21 | 22 | BcovConfig 23 | BcovConfigParser::parse(sstring_view file_name) const 24 | { 25 | BcovConfig config; 26 | if (file_name.empty()) { 27 | return config; 28 | } 29 | 30 | std::ifstream config_file(file_name.data()); 31 | int line_num = 1; 32 | FuncConfigItem *func_item = nullptr; 33 | 34 | for (std::string line; std::getline(config_file, line);) { 35 | if (line.empty()) { 36 | continue; 37 | } 38 | auto func_name = parse_function_name(line); 39 | bool sucess; 40 | if (!func_name.empty()) { 41 | func_item = config.add_function(func_name); 42 | sucess = true; 43 | } else { 44 | addr_t tab_addr; 45 | ConfJumpTabTargets targets; 46 | sucess = parse_jump_table(line, tab_addr, targets); 47 | if (sucess && func_item != nullptr) { 48 | func_item->add_jump_table(tab_addr, targets); 49 | } 50 | } 51 | if (!sucess) { 52 | throw ConfigParseException( 53 | "Could not parse configuration at line:" + std::to_string(line_num)); 54 | } 55 | line_num++; 56 | } 57 | return config; 58 | } 59 | 60 | std::string 61 | BcovConfigParser::parse_function_name(const std::string &line) const noexcept 62 | { 63 | if ((line.front()) != '[' or (line.back()) != ']') { 64 | return ""; 65 | } 66 | std::smatch m; 67 | if (!std::regex_search(line, m, m_func_name_regex)) { 68 | return ""; 69 | } 70 | return m[0]; 71 | } 72 | 73 | bool 74 | BcovConfigParser::parse_jump_table(const std::string &line, addr_t &jump_tab_addr, 75 | ConfJumpTabTargets &targets) const noexcept 76 | { 77 | auto addr_begin = std::sregex_iterator(line.begin(), line.end(), 78 | m_address_regex); 79 | auto addr_end = std::sregex_iterator(); 80 | bool status = false; 81 | try { 82 | if (std::distance(addr_begin, addr_end) == 0) { 83 | return status; 84 | } 85 | jump_tab_addr = std::stoul((*addr_begin).str(), nullptr, 16); 86 | for (std::sregex_iterator it = ++addr_begin; it != addr_end; ++it) { 87 | targets.push_back(std::stoul((*it).str(), nullptr, 16)); 88 | } 89 | if (targets.size() > 2) { 90 | status = true; 91 | } 92 | } catch (std::invalid_argument &exp) { 93 | } catch (std::out_of_range &exp) { 94 | } 95 | return status; 96 | } 97 | 98 | ConfigParseException::ConfigParseException(const std::string &what_arg) : 99 | runtime_error(what_arg) 100 | { } 101 | 102 | ConfigParseException::ConfigParseException(const char *what_arg) : 103 | runtime_error(what_arg) 104 | { 105 | } 106 | } // bcov 107 | -------------------------------------------------------------------------------- /src/util/BcovConfigParser.hpp: -------------------------------------------------------------------------------- 1 | /* **************************************************************************** 2 | * Copyright (c) 2018 University of Kaiserslautern. All rights reserved. 3 | * 4 | * This file is distributed under MIT license. See LICENSE.txt for details. 5 | * 6 | * ****************************************************************************/ 7 | /** 8 | * \brief 9 | */ 10 | #pragma once 11 | 12 | #include "core/Common.hpp" 13 | #include "BcovConfig.hpp" 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | namespace bcov { 21 | 22 | class BcovConfigParser { 23 | public: 24 | BcovConfigParser(); 25 | 26 | BcovConfigParser(const BcovConfigParser &other) = default; 27 | 28 | BcovConfigParser &operator=(const BcovConfigParser &other) = default; 29 | 30 | BcovConfigParser(BcovConfigParser &&other) noexcept = default; 31 | 32 | BcovConfigParser &operator=(BcovConfigParser &&other) noexcept = default; 33 | 34 | virtual ~BcovConfigParser() = default; 35 | 36 | BcovConfig parse(sstring_view file_name) const; 37 | 38 | protected: 39 | 40 | std::string parse_function_name(const std::string &line) const noexcept; 41 | 42 | bool parse_jump_table(const std::string &line, addr_t &jump_tab_addr, 43 | ConfJumpTabTargets &targets) const noexcept; 44 | 45 | private: 46 | std::regex m_func_name_regex; 47 | std::regex m_address_regex; 48 | }; 49 | 50 | class ConfigParseException : public std::runtime_error { 51 | public: 52 | 53 | explicit ConfigParseException(const std::string &what_arg); 54 | 55 | explicit ConfigParseException(const char *what_arg); 56 | 57 | ~ConfigParseException() override = default; 58 | }; 59 | 60 | } // bcov 61 | -------------------------------------------------------------------------------- /src/util/Demangler.cpp: -------------------------------------------------------------------------------- 1 | /* **************************************************************************** 2 | * Copyright (c) 2018 University of Kaiserslautern. All rights reserved. 3 | * 4 | * This file is distributed under MIT license. See LICENSE.txt for details. 5 | * 6 | * ****************************************************************************/ 7 | /** 8 | * \brief 9 | */ 10 | 11 | #include "Demangler.hpp" 12 | #include 13 | #include 14 | 15 | #define CPP_MANGLED_NAME_PREFIX "_Z" 16 | #define CPP_MANGLED_NAME_MAXSIZE (1024UL) 17 | 18 | namespace bcov { 19 | 20 | bool 21 | Demangler::is_unmangled_name(sstring_view name) noexcept 22 | { 23 | // to check whether a function name is mangled or not we need to 24 | // (1) check if the name is prefixed with _Z. According to 25 | // Itanium C++ ABI, all mangled names should start with _Z. 26 | // See http://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling-structure 27 | // (2) use demangler API which would return success on mangled names. 28 | // See https://itanium-cxx-abi.github.io/cxx-abi/abi.html#demangler 29 | // 30 | // here we do a quick check to see if the name is unmangled i.e. is not prefixed 31 | // with '_Z' 32 | return (std::strncmp(name.data(), CPP_MANGLED_NAME_PREFIX, 2)) != 0; 33 | } 34 | 35 | Demangler::Demangler() : m_status(false), m_demangled_name(nullptr) 36 | { 37 | m_demangled_name = (char *) malloc(CPP_MANGLED_NAME_MAXSIZE); 38 | } 39 | 40 | Demangler::~Demangler() 41 | { 42 | if (m_demangled_name != nullptr) { 43 | free(m_demangled_name); 44 | } 45 | } 46 | 47 | bool 48 | Demangler::is_success() const noexcept 49 | { 50 | return m_status; 51 | } 52 | 53 | const char * 54 | Demangler::demangled_name() const noexcept 55 | { 56 | return m_demangled_name; 57 | } 58 | 59 | void 60 | Demangler::demangle(sstring_view func_name) noexcept 61 | { 62 | int status; 63 | size_t size = CPP_MANGLED_NAME_MAXSIZE; 64 | m_demangled_name = abi::__cxa_demangle(func_name.data(), m_demangled_name, &size, &status); 65 | m_status = (status == 0); 66 | } 67 | } // bcov 68 | -------------------------------------------------------------------------------- /src/util/Demangler.hpp: -------------------------------------------------------------------------------- 1 | /* **************************************************************************** 2 | * Copyright (c) 2018 University of Kaiserslautern. All rights reserved. 3 | * 4 | * This file is distributed under MIT license. See LICENSE.txt for details. 5 | * 6 | * ****************************************************************************/ 7 | /** 8 | * \brief 9 | */ 10 | 11 | 12 | #pragma once 13 | 14 | #include "core/Common.hpp" 15 | 16 | namespace bcov { 17 | 18 | class Demangler { 19 | public: 20 | 21 | Demangler(); 22 | 23 | Demangler(const Demangler &other) = default; 24 | 25 | Demangler &operator=(const Demangler &other) = default; 26 | 27 | Demangler(Demangler &&other) noexcept = default; 28 | 29 | Demangler &operator=(Demangler &&other) noexcept = default; 30 | 31 | virtual ~Demangler(); 32 | 33 | bool is_success() const noexcept; 34 | 35 | static bool is_unmangled_name(sstring_view name) noexcept; 36 | 37 | const char *demangled_name() const noexcept; 38 | 39 | void demangle(sstring_view func_name) noexcept; 40 | 41 | private: 42 | bool m_status; 43 | char *m_demangled_name; 44 | 45 | }; 46 | } // bcov 47 | -------------------------------------------------------------------------------- /src/util/ElfData.hpp: -------------------------------------------------------------------------------- 1 | /* **************************************************************************** 2 | * Copyright (c) 2018 University of Kaiserslautern. All rights reserved. 3 | * 4 | * This file is distributed under MIT license. See LICENSE.txt for details. 5 | * 6 | * ****************************************************************************/ 7 | /** 8 | * \brief 9 | */ 10 | 11 | #pragma once 12 | 13 | #include "libelfin/elf/data.hh" 14 | 15 | // info needed to parse relocation symbols 16 | 17 | #define R_X86_64_GLOB_DAT 6 /* Create GOT entry */ 18 | #define R_X86_64_JUMP_SLOT 7 /* Create PLT entry */ 19 | 20 | #define ELF64_R_SYM(i)((i) >> 32U) 21 | #define ELF64_R_TYPE(i)((i) & 0xffffffffUL) 22 | 23 | // info needed to parse the dynamic segment 24 | #define DT_NULL 0 25 | #define DT_NEEDED 1 26 | #define DT_PLTRELSZ 2 27 | #define DT_PLTGOT 3 28 | #define DT_HASH 4 29 | #define DT_STRTAB 5 30 | #define DT_SYMTAB 6 31 | #define DT_RELA 7 32 | #define DT_RELASZ 8 33 | #define DT_RELAENT 9 34 | #define DT_STRSZ 10 35 | #define DT_SYMENT 11 36 | #define DT_INIT 12 37 | #define DT_FINI 13 38 | #define DT_SONAME 14 39 | #define DT_RPATH 15 40 | #define DT_SYMBOLIC 16 41 | #define DT_REL 17 42 | #define DT_RELSZ 18 43 | #define DT_RELENT 19 44 | #define DT_PLTREL 20 45 | #define DT_DEBUG 21 46 | #define DT_TEXTREL 22 47 | #define DT_JMPREL 23 48 | #define DT_ENCODING 32 49 | #define OLD_DT_LOOS 0x60000000 50 | #define DT_LOOS 0x6000000d 51 | #define DT_HIOS 0x6ffff000 52 | #define DT_VALRNGLO 0x6ffffd00 53 | #define DT_VALRNGHI 0x6ffffdff 54 | #define DT_ADDRRNGLO 0x6ffffe00 55 | #define DT_ADDRRNGHI 0x6ffffeff 56 | #define DT_VERSYM 0x6ffffff0 57 | #define DT_RELACOUNT 0x6ffffff9 58 | #define DT_RELCOUNT 0x6ffffffa 59 | #define DT_FLAGS_1 0x6ffffffb 60 | #define DT_VERDEF 0x6ffffffc 61 | #define DT_VERDEFNUM 0x6ffffffd 62 | #define DT_VERNEED 0x6ffffffe 63 | #define DT_VERNEEDNUM 0x6fffffff 64 | #define OLD_DT_HIOS 0x6fffffff 65 | #define DT_LOPROC 0x70000000 66 | #define DT_HIPROC 0x7fffffff 67 | 68 | #define GOT_SEC_NAME ".got" 69 | #define GOT_PLT_SEC_NAME ".got.plt" 70 | #define RELA_PLT_SEC_NAME ".rela.plt" 71 | #define RELA_DYN_SEC_NAME ".rela.dyn" 72 | 73 | typedef struct { 74 | elf::Elf64::Addr r_offset; 75 | uint64_t r_info; 76 | int64_t r_addend; 77 | } Elf64_Rela; 78 | 79 | typedef struct { 80 | elf::Elf64::Sxword d_tag; /* entry tag value */ 81 | union { 82 | elf::Elf64::Xword d_val; 83 | elf::Elf64::Addr d_ptr; 84 | } d_un; 85 | } Elf64_Dyn; 86 | 87 | const char *to_string(elf::Elf64::Sxword d_tag); 88 | -------------------------------------------------------------------------------- /src/util/FileUtil.hpp: -------------------------------------------------------------------------------- 1 | /* **************************************************************************** 2 | * Copyright (c) 2018 University of Kaiserslautern. All rights reserved. 3 | * 4 | * This file is distributed under MIT license. See LICENSE.txt for details. 5 | * 6 | * ****************************************************************************/ 7 | /** 8 | * \brief 9 | */ 10 | 11 | #include "core/Common.hpp" 12 | 13 | namespace bcov { 14 | 15 | static inline sstring 16 | get_base_name(sstring_view path) 17 | { 18 | sstring file_path(path.data()); 19 | return file_path.substr(file_path.find_last_of('/') + 1); 20 | } 21 | 22 | static inline sstring 23 | get_directory_name(sstring_view path) 24 | { 25 | sstring file_path(path.data()); 26 | return file_path.substr(0, file_path.find_last_of('/')); 27 | } 28 | 29 | } // bcov 30 | -------------------------------------------------------------------------------- /src/util/Logging.cpp: -------------------------------------------------------------------------------- 1 | /* **************************************************************************** 2 | * Copyright (c) 2018 University of Kaiserslautern. All rights reserved. 3 | * 4 | * This file is distributed under MIT license. See LICENSE.txt for details. 5 | * 6 | * ****************************************************************************/ 7 | 8 | 9 | #include "Logging.hpp" 10 | 11 | INITIALIZE_EASYLOGGINGPP 12 | 13 | void initialize_logging_common() 14 | { 15 | using namespace el; 16 | Loggers::reconfigureAllLoggers(ConfigurationType::Format, "%levshort | %msg"); 17 | 18 | Loggers::reconfigureAllLoggers(ConfigurationType::ToStandardOutput, "false"); 19 | 20 | Loggers::reconfigureAllLoggers(ConfigurationType::ToFile, "true"); 21 | 22 | Configurations log_conf; 23 | log_conf.setToDefault(); 24 | log_conf.set(Level::Info, ConfigurationType::PerformanceTracking, 25 | "true"); 26 | log_conf.set(Level::Info, ConfigurationType::SubsecondPrecision, "3"); 27 | log_conf.set(Level::Info, ConfigurationType::MaxLogFileSize, "2097152"); 28 | 29 | Loggers::reconfigureLogger("performance", log_conf); 30 | } 31 | 32 | void 33 | initialize_logging(const char *log_file, unsigned short verbosity_level) 34 | { 35 | using namespace el; 36 | if (log_file == nullptr) { 37 | Loggers::reconfigureAllLoggers(ConfigurationType::Filename, "bcov.log"); 38 | } else { 39 | Loggers::reconfigureAllLoggers(ConfigurationType::Filename, log_file); 40 | } 41 | initialize_logging_common(); 42 | Loggers::setVerboseLevel(verbosity_level); 43 | } 44 | 45 | void 46 | initialize_logging(int argc, const char **argv) 47 | { 48 | START_EASYLOGGINGPP(argc, argv); 49 | initialize_logging_common(); 50 | } 51 | -------------------------------------------------------------------------------- /src/util/Logging.hpp: -------------------------------------------------------------------------------- 1 | /* **************************************************************************** 2 | * Copyright (c) 2018 University of Kaiserslautern. All rights reserved. 3 | * 4 | * This file is distributed under MIT license. See LICENSE.txt for details. 5 | * 6 | * ****************************************************************************/ 7 | /** 8 | * \brief 9 | */ 10 | 11 | #pragma once 12 | 13 | #include "easylogging/easylogging++.h" 14 | 15 | void initialize_logging(const char *log_file, unsigned short verbosity_level); 16 | 17 | void initialize_logging(int argc, const char **argv); 18 | -------------------------------------------------------------------------------- /src/util/ProgOptions.cpp: -------------------------------------------------------------------------------- 1 | /* **************************************************************************** 2 | * Copyright (c) 2018 University of Kaiserslautern. All rights reserved. 3 | * 4 | * This file is distributed under MIT license. See LICENSE.txt for details. 5 | * 6 | * ****************************************************************************/ 7 | /** 8 | * \brief 9 | */ 10 | 11 | #include "ProgOptions.hpp" 12 | #include "cmdline/cmdline.hpp" 13 | #include 14 | 15 | namespace bcov { 16 | 17 | const char *ProgOptionName::kInputFile = "input"; 18 | const char *ProgOptionName::kConfigFile = "config"; 19 | const char *ProgOptionName::kDataFile = "data"; 20 | const char *ProgOptionName::kOutputFile = "output"; 21 | const char *ProgOptionName::kLogFile = "log-file"; 22 | const char *ProgOptionName::kFuncName = "function"; 23 | const char *ProgOptionName::kParameter = "parameter"; 24 | const char *ProgOptionName::kMode = "mode"; 25 | const char *ProgOptionName::kVerbosity = "verbosity"; 26 | 27 | static void 28 | usage_error_exit(const cmdline::parser &parser, const std::string &msg, 29 | ErrorCode error_code) 30 | { 31 | std::cerr << "\n" << msg << "\n" << std::endl; 32 | std::cerr << parser.usage(); 33 | std::exit((int) error_code); 34 | } 35 | 36 | static void 37 | error_exit(const std::string &msg, ErrorCode error_code) 38 | { 39 | std::cerr << msg << std::endl; 40 | std::exit((int) error_code); 41 | } 42 | 43 | static bool 44 | file_exists(const std::string &file_name) noexcept 45 | { 46 | std::ifstream file(file_name); 47 | return file.good(); 48 | } 49 | 50 | static ProgramMode 51 | parse_mode(const std::string &mode) noexcept 52 | { 53 | if (mode == "patch") { 54 | return ProgramMode::kPatch; 55 | } 56 | if (mode == "report") { 57 | return ProgramMode::kReport; 58 | } 59 | if (mode == "dump") { 60 | return ProgramMode::kDump; 61 | } 62 | return ProgramMode::kInvalid; 63 | } 64 | 65 | static OperationParams 66 | parse_dump_param(const std::string ¶m) noexcept 67 | { 68 | if (param == "cfg") { 69 | return OperationParams::kDumpCFG; 70 | } 71 | if (param == "predom") { 72 | return OperationParams::kDumpPreDom; 73 | } 74 | 75 | if (param == "postdom") { 76 | return OperationParams::kDumpPostDom; 77 | } 78 | 79 | if (param == "sbdom") { 80 | return OperationParams::kDumpSBDom; 81 | } 82 | 83 | return OperationParams::kInvalid; 84 | } 85 | 86 | static inline bool is_dump_params(OperationParams params) 87 | { 88 | return ((unsigned) params & 0x3) == 0; 89 | } 90 | 91 | static OperationParams 92 | parse_params(ProgramMode mode, const std::string ¶ms) noexcept 93 | { 94 | if (mode == ProgramMode::kPatch || mode == ProgramMode::kReport) { 95 | if (params == "all") { 96 | return OperationParams::kAllNode; 97 | } 98 | if (params == "any") { 99 | return OperationParams::kAnyNode; 100 | } 101 | return OperationParams::kInvalid; 102 | } 103 | sstring delimiter = ","; 104 | size_t pos1 = 0; 105 | size_t pos2 = 0; 106 | OperationParams res = OperationParams::kInvalid; 107 | std::string token; 108 | while ((pos2 = params.find(delimiter, pos2)) != std::string::npos) { 109 | token = params.substr(pos1, pos2 - pos1); 110 | res = res | parse_dump_param(token); 111 | pos1 = ++pos2; 112 | } 113 | 114 | token = params.substr(pos1, params.size() - pos1); 115 | res = res | parse_dump_param(token); 116 | 117 | return res; 118 | } 119 | 120 | //============================================================================== 121 | 122 | ProgOptions::ProgOptions() 123 | : m_mode(ProgramMode::kPatch), m_params(OperationParams::kAllNode), 124 | m_verbosity(0) 125 | { } 126 | 127 | ProgOptions 128 | ProgOptions::parse(int argc, const char **argv) 129 | { 130 | cmdline::parser cmd_parser; 131 | cmd_parser.add(ProgOptionName::kMode, 'm', 132 | "operation mode [patch|report|dump]", true); 133 | 134 | cmd_parser.add(ProgOptionName::kParameter, 'p', 135 | "operation mode parameter", false); 136 | 137 | cmd_parser.add(ProgOptionName::kInputFile, 'i', 138 | "input elf file", true); 139 | 140 | cmd_parser.add(ProgOptionName::kConfigFile, 'c', 141 | "configuration file", false); 142 | 143 | cmd_parser.add(ProgOptionName::kOutputFile, 'o', 144 | "patched output file", false); 145 | 146 | cmd_parser.add(ProgOptionName::kDataFile, 'd', 147 | "coverage data file", false); 148 | 149 | cmd_parser.add(ProgOptionName::kLogFile, 'l', 150 | "log file", false); 151 | 152 | cmd_parser.add(ProgOptionName::kFuncName, 'f', 153 | "selected function name", false); 154 | 155 | cmd_parser.add(ProgOptionName::kVerbosity, 'v', "verbosity level", 156 | false, 0); 157 | 158 | cmd_parser.add("help", 'h', "print help"); 159 | auto parsed_ok = cmd_parser.parse(argc, argv); 160 | if (!parsed_ok) { 161 | usage_error_exit(cmd_parser, "please check provided options!", 162 | ErrorCode::kBadUsage); 163 | } 164 | 165 | ProgOptions options; 166 | options.m_mode = parse_mode(cmd_parser.get(ProgOptionName::kMode)); 167 | if (options.program_mode() == ProgramMode::kInvalid) { 168 | usage_error_exit(cmd_parser, "invalid operation mode!", 169 | ErrorCode::kBadUsage); 170 | } 171 | options.m_input_file = cmd_parser.get(ProgOptionName::kInputFile); 172 | if (options.program_mode() == ProgramMode::kPatch) { 173 | if (cmd_parser.exist(ProgOptionName::kOutputFile)) { 174 | options.m_output_file = 175 | cmd_parser.get(ProgOptionName::kOutputFile); 176 | } else { 177 | error_exit("please specify an output file", ErrorCode::kBadUsage); 178 | } 179 | } 180 | 181 | if (options.program_mode() == ProgramMode::kReport) { 182 | if (cmd_parser.exist(ProgOptionName::kDataFile)) { 183 | options.m_data_file = 184 | cmd_parser.get(ProgOptionName::kDataFile); 185 | } else { 186 | error_exit("please specify a coverage data file", ErrorCode::kBadUsage); 187 | } 188 | } 189 | 190 | if (cmd_parser.exist(ProgOptionName::kConfigFile)) { 191 | options.m_config_file = 192 | cmd_parser.get(ProgOptionName::kConfigFile); 193 | } 194 | 195 | if (cmd_parser.exist(ProgOptionName::kOutputFile)) { 196 | options.m_output_file = 197 | cmd_parser.get(ProgOptionName::kOutputFile); 198 | } 199 | 200 | if (cmd_parser.exist(ProgOptionName::kLogFile)) { 201 | options.m_log_file = 202 | cmd_parser.get(ProgOptionName::kLogFile); 203 | } else { 204 | options.m_log_file = "bcov.log"; 205 | } 206 | 207 | if (cmd_parser.exist(ProgOptionName::kFuncName)) { 208 | options.m_function = cmd_parser.get(ProgOptionName::kFuncName); 209 | } 210 | 211 | if (cmd_parser.exist(ProgOptionName::kVerbosity)) { 212 | options.m_verbosity = cmd_parser.get(ProgOptionName::kVerbosity); 213 | } 214 | 215 | if (cmd_parser.exist(ProgOptionName::kParameter)) { 216 | options.m_params = 217 | parse_params(options.program_mode(), 218 | cmd_parser.get(ProgOptionName::kParameter)); 219 | 220 | if (options.operation_params() == OperationParams::kInvalid) { 221 | usage_error_exit(cmd_parser, "invalid parameters!", 222 | ErrorCode::kBadUsage); 223 | } 224 | } 225 | 226 | return options; 227 | } 228 | 229 | sstring_view 230 | ProgOptions::input_file() const noexcept 231 | { 232 | return m_input_file; 233 | } 234 | 235 | sstring_view 236 | ProgOptions::output_file() const noexcept 237 | { 238 | return m_output_file; 239 | } 240 | 241 | sstring_view 242 | ProgOptions::log_file() const noexcept 243 | { 244 | return m_log_file; 245 | } 246 | 247 | sstring_view 248 | ProgOptions::data_file() const noexcept 249 | { 250 | return m_data_file; 251 | } 252 | 253 | sstring_view 254 | ProgOptions::config_file() const noexcept 255 | { 256 | return m_config_file; 257 | } 258 | 259 | sstring_view 260 | ProgOptions::selected_function() const noexcept 261 | { 262 | return m_function; 263 | } 264 | 265 | int 266 | ProgOptions::verbosity() const noexcept 267 | { 268 | return m_verbosity; 269 | } 270 | 271 | ProgramMode 272 | ProgOptions::program_mode() const noexcept 273 | { 274 | return m_mode; 275 | } 276 | 277 | OperationParams 278 | ProgOptions::operation_params() const noexcept 279 | { 280 | return m_params; 281 | } 282 | 283 | czstring 284 | to_string(ProgramMode mode) 285 | { 286 | switch (mode) { 287 | case ProgramMode::kPatch: return "patch"; 288 | case ProgramMode::kReport: return "report"; 289 | case ProgramMode::kDump: return "dump"; 290 | default: return "invalid"; 291 | } 292 | } 293 | 294 | std::string 295 | to_string(OperationParams params) 296 | { 297 | if (params == OperationParams::kInvalid) { 298 | return "invalid"; 299 | } 300 | if ((params & OperationParams::kAllNode) == OperationParams::kAllNode) { 301 | return "all-node"; 302 | } 303 | if ((params & OperationParams::kAnyNode) == OperationParams::kAnyNode) { 304 | return "any-node"; 305 | } 306 | 307 | sstring res; 308 | if ((params & OperationParams::kDumpCFG) == OperationParams::kDumpCFG) { 309 | res += "cfg"; 310 | } 311 | if ((params & OperationParams::kDumpPreDom) == OperationParams::kDumpPreDom) { 312 | res += res.empty() ? "predom" : "|predom"; 313 | } 314 | if ((params & OperationParams::kDumpPostDom) == OperationParams::kDumpPostDom) { 315 | res += res.empty() ? "postdom" : "|postdom"; 316 | } 317 | 318 | if ((params & OperationParams::kDumpSBDom) == OperationParams::kDumpSBDom) { 319 | res += res.empty() ? "sbdom" : "|sbdom"; 320 | } 321 | 322 | return res.empty() ? "invalid" : res; 323 | } 324 | } // bcov 325 | -------------------------------------------------------------------------------- /src/util/ProgOptions.hpp: -------------------------------------------------------------------------------- 1 | /* **************************************************************************** 2 | * Copyright (c) 2018 University of Kaiserslautern. All rights reserved. 3 | * 4 | * This file is distributed under MIT license. See LICENSE.txt for details. 5 | * 6 | * ****************************************************************************/ 7 | /** 8 | * \brief 9 | */ 10 | 11 | #pragma once 12 | 13 | #include "core/Common.hpp" 14 | 15 | namespace bcov { 16 | 17 | struct ProgOptionName { 18 | static const char *kInputFile; 19 | static const char *kConfigFile; 20 | static const char *kDataFile; 21 | static const char *kOutputFile; 22 | static const char *kLogFile; 23 | static const char *kFuncName; 24 | static const char *kParameter; 25 | static const char *kMode; 26 | static const char *kVerbosity; 27 | }; 28 | 29 | enum class ErrorCode : int { 30 | kOk = 0, 31 | kGeneric = -1, 32 | kInputFile = -3, 33 | kConfigFile = -4, 34 | kBadUsage = -5 35 | }; 36 | 37 | enum class ProgramMode : uint8_t { 38 | kInvalid, 39 | kPatch, 40 | kReport, 41 | kDump 42 | }; 43 | 44 | enum class OperationParams : uint32_t { 45 | kInvalid = 0x00, 46 | kAllNode = 0x01, 47 | kAnyNode = 0x02, 48 | kDumpCFG = 0x04, 49 | kDumpPreDom = 0x08, 50 | kDumpPostDom = 0x10, 51 | kDumpSBDom = 0x20 52 | }; 53 | 54 | static inline OperationParams operator&(OperationParams a, OperationParams b) 55 | { 56 | return (OperationParams)((unsigned) a & (unsigned) b); 57 | } 58 | 59 | static inline OperationParams operator|(OperationParams a, OperationParams b) 60 | { 61 | return (OperationParams)((unsigned) a | (unsigned) b); 62 | } 63 | 64 | czstring to_string(ProgramMode mode); 65 | 66 | std::string to_string(OperationParams params); 67 | 68 | class ProgOptions { 69 | public: 70 | 71 | ProgOptions(); 72 | 73 | ProgOptions(const ProgOptions &other) = default; 74 | 75 | ProgOptions &operator=(const ProgOptions &other) = default; 76 | 77 | ProgOptions(ProgOptions &&other) noexcept = default; 78 | 79 | ProgOptions &operator=(ProgOptions &&other) noexcept = default; 80 | 81 | virtual ~ProgOptions() = default; 82 | 83 | static ProgOptions parse(int argc, const char **argv); 84 | 85 | sstring_view input_file() const noexcept; 86 | 87 | sstring_view output_file() const noexcept; 88 | 89 | sstring_view log_file() const noexcept; 90 | 91 | sstring_view data_file() const noexcept; 92 | 93 | sstring_view config_file() const noexcept; 94 | 95 | sstring_view selected_function() const noexcept; 96 | 97 | ProgramMode program_mode() const noexcept; 98 | 99 | OperationParams operation_params() const noexcept; 100 | 101 | int verbosity() const noexcept; 102 | 103 | private: 104 | ProgramMode m_mode; 105 | OperationParams m_params; 106 | sstring m_config_file; 107 | sstring m_output_file; 108 | sstring m_data_file; 109 | sstring m_input_file; 110 | sstring m_function; 111 | sstring m_log_file; 112 | int m_verbosity; 113 | }; 114 | 115 | } // bcov 116 | -------------------------------------------------------------------------------- /src/x64/Asm.hpp: -------------------------------------------------------------------------------- 1 | /* **************************************************************************** 2 | * Copyright (c) 2018 University of Kaiserslautern. All rights reserved. 3 | * 4 | * This file is distributed under MIT license. See LICENSE.txt for details. 5 | * 6 | * ****************************************************************************/ 7 | /** 8 | * \brief 9 | */ 10 | 11 | 12 | #pragma once 13 | 14 | #include "core/CSInstWrapper.hpp" 15 | #include "core/Disassembler.hpp" 16 | #include "core/MCInst.hpp" 17 | 18 | namespace bcov { 19 | namespace x64 { 20 | 21 | constexpr unsigned kInstRIPRelMoveSize = 7U; 22 | 23 | constexpr unsigned kInstJMPRel32Size = 5U; 24 | 25 | constexpr unsigned kInstJccRel32Size = 6U; 26 | 27 | constexpr unsigned kInstJMPRel8Size = 2U; 28 | 29 | // jmp QWORD PTR [rsp] 30 | static constexpr uint32_t kJmpRSPMemDisp0Inst = 0x002424FFU; 31 | 32 | static constexpr uint32_t kJmpRSPMemDisp0InstSize = 3U; 33 | 34 | // jmp QWORD PTR [rsp + disp8] 35 | static constexpr uint32_t kJmpRSPMemDisp8Inst = 0x002464FFU; 36 | 37 | static constexpr uint32_t kJmpRSPMemDisp8InstSize = 4U; 38 | 39 | // jmp QWORD PTR [rsp + disp32] 40 | static constexpr uint32_t kJmpRSPMemDisp32Inst = 0x0024A4FFU; 41 | 42 | static constexpr uint32_t kJmpRSPMemDisp32InstSize = 7U; 43 | 44 | // sub QWORD PTR [rsp],imm8 45 | static constexpr unsigned kRSPAdjustByteSize = 5U; 46 | 47 | static constexpr unsigned kQWORD = 8U; 48 | 49 | static constexpr unsigned kDWORD = 4U; 50 | 51 | static constexpr unsigned kWORD = 2U; 52 | 53 | static constexpr uint8_t kCondShortJmpCXZ = 0xE3U; 54 | 55 | } // x64 56 | 57 | class X64Asm { 58 | public: 59 | static void jmp_rel_32(uint8_t *buf, addr_t src, addr_t dst); 60 | 61 | static void call_rel_32(uint8_t *buf, addr_t src, addr_t dst); 62 | 63 | static void jmp_rel_8(uint8_t *buf, addr_t src, addr_t dst); 64 | 65 | // sub QWORD PTR [rsp],imm8 66 | static void sub_rsp_mem_imm8(uint8_t *buf, int8_t off); 67 | 68 | // call QWORD PTR [rsp + disp32] 69 | static void jmp_rsp_mem_disp32(uint8_t *buf, int32_t off); 70 | 71 | // mov [rip+disp32], imm8 72 | static void 73 | mov_rip_mem_imm8(uint8_t *buf, addr_t inst_addr, addr_t mem_addr, uint8_t imm8); 74 | 75 | static void fill_nop(uint8_t *buf, size_t size); 76 | 77 | static void fill_int3(uint8_t *buf, size_t size); 78 | 79 | }; 80 | 81 | //@brief: rewrites a given instruction to a different address. Assumes a small code model 82 | class X64InstRewriter { 83 | public: 84 | 85 | X64InstRewriter() = default; 86 | 87 | ~X64InstRewriter() = default; 88 | 89 | static void 90 | rewrite_call(const MCInst &inst, buffer_t src_buf, addr_t dst_addr, 91 | uint8_t **dst_buf_p); 92 | 93 | static void 94 | rewrite_call_to_jmp(uint8_t *inst_p); 95 | 96 | static void 97 | rewrite_uncond_jmp(const MCInst &inst, buffer_t src_buf, addr_t dst_addr, 98 | uint8_t **dst_buf_p); 99 | 100 | static void 101 | rewrite_cond_jmp(const MCInst &inst, buffer_t src_buf, addr_t dst_addr, 102 | uint8_t **dst_buf_p); 103 | 104 | static void 105 | rewrite_pc_rel_inst(const MCInst &inst, buffer_t src_buf, addr_t dst_addr, 106 | uint8_t **dst_buf_p); 107 | 108 | static void 109 | rewrite(const MCInst &inst, buffer_t src_buf, addr_t dst_addr, 110 | uint8_t **dst_buf_p); 111 | }; 112 | 113 | } // bcov 114 | -------------------------------------------------------------------------------- /src/x64/Inst.cpp: -------------------------------------------------------------------------------- 1 | /* **************************************************************************** 2 | * Copyright (c) 2018 University of Kaiserslautern. All rights reserved. 3 | * 4 | * This file is distributed under MIT license. See LICENSE.txt for details. 5 | * 6 | * ****************************************************************************/ 7 | /** 8 | * \brief 9 | */ 10 | 11 | #include "Inst.hpp" 12 | #include "easylogging/easylogging++.h" 13 | #include 14 | 15 | namespace bcov { 16 | namespace x64 { 17 | 18 | x86_reg 19 | get_canonical(x86_reg reg) 20 | { 21 | switch (reg) { 22 | case X86_REG_AL: 23 | case X86_REG_AX: 24 | case X86_REG_EAX:return X86_REG_RAX; 25 | 26 | case X86_REG_BL: 27 | case X86_REG_BX: 28 | case X86_REG_EBX:return X86_REG_RBX; 29 | 30 | case X86_REG_CL: 31 | case X86_REG_CX: 32 | case X86_REG_ECX:return X86_REG_RCX; 33 | 34 | case X86_REG_DL: 35 | case X86_REG_DX: 36 | case X86_REG_EDX:return X86_REG_RDX; 37 | 38 | case X86_REG_SIL: 39 | case X86_REG_SI: 40 | case X86_REG_ESI:return X86_REG_RSI; 41 | 42 | case X86_REG_DIL: 43 | case X86_REG_DI: 44 | case X86_REG_EDI:return X86_REG_RDI; 45 | 46 | case X86_REG_BPL: 47 | case X86_REG_BP: 48 | case X86_REG_EBP:return X86_REG_RBP; 49 | 50 | case X86_REG_SPL: 51 | case X86_REG_SP: 52 | case X86_REG_ESP:return X86_REG_RSP; 53 | 54 | case X86_REG_R8B: 55 | case X86_REG_R8W: 56 | case X86_REG_R8D:return X86_REG_R8; 57 | 58 | case X86_REG_R9B: 59 | case X86_REG_R9W: 60 | case X86_REG_R9D:return X86_REG_R9; 61 | 62 | case X86_REG_R10B: 63 | case X86_REG_R10W: 64 | case X86_REG_R10D:return X86_REG_R10; 65 | 66 | case X86_REG_R11B: 67 | case X86_REG_R11W: 68 | case X86_REG_R11D:return X86_REG_R11; 69 | 70 | case X86_REG_R12B: 71 | case X86_REG_R12W: 72 | case X86_REG_R12D:return X86_REG_R12; 73 | 74 | case X86_REG_R13B: 75 | case X86_REG_R13W: 76 | case X86_REG_R13D:return X86_REG_R13; 77 | 78 | case X86_REG_R14B: 79 | case X86_REG_R14W: 80 | case X86_REG_R14D:return X86_REG_R14; 81 | 82 | case X86_REG_R15B: 83 | case X86_REG_R15W: 84 | case X86_REG_R15D:return X86_REG_R15; 85 | 86 | default:return reg; 87 | } 88 | } 89 | 90 | bool 91 | is_branch(const cs_insn *inst) noexcept 92 | { 93 | for (uint8_t i = 0; i < inst->detail->groups_count; ++i) { 94 | if (inst->detail->groups[i] == CS_GRP_JUMP || 95 | inst->detail->groups[i] == CS_GRP_CALL || 96 | inst->detail->groups[i] == CS_GRP_RET || 97 | inst->detail->groups[i] == CS_GRP_INT || 98 | inst->detail->groups[i] == CS_GRP_IRET || 99 | inst->detail->groups[i] == CS_GRP_BRANCH_RELATIVE) { 100 | return true; 101 | } 102 | } 103 | return is_trap(inst) || is_loop(inst); 104 | } 105 | 106 | bool 107 | is_call(const cs_insn *inst) noexcept 108 | { 109 | for (uint8_t i = 0; i < inst->detail->groups_count; ++i) { 110 | if (inst->detail->groups[i] == CS_GRP_CALL) { 111 | return true; 112 | } 113 | } 114 | return false; 115 | } 116 | 117 | bool 118 | is_jump(const cs_insn *inst) noexcept 119 | { 120 | for (uint8_t i = 0; i < inst->detail->groups_count; ++i) { 121 | if (inst->detail->groups[i] == CS_GRP_JUMP) { 122 | return true; 123 | } 124 | } 125 | return false; 126 | } 127 | 128 | uint8_t 129 | opnd_count(const cs_insn *inst) noexcept 130 | { 131 | return inst->detail->x86.op_count; 132 | } 133 | 134 | bool 135 | is_opnd_read(const cs_insn *inst, Opnd opnd) noexcept 136 | { 137 | const cs_x86_op &opnd_ref = inst->detail->x86.operands[to_integral(opnd)]; 138 | return (opnd_ref.access & CS_AC_READ) == CS_AC_READ; 139 | } 140 | 141 | bool 142 | is_opnd_write(const cs_insn *inst, Opnd opnd) noexcept 143 | { 144 | const cs_x86_op &opnd_ref = inst->detail->x86.operands[to_integral(opnd)]; 145 | return (opnd_ref.access & CS_AC_WRITE) == CS_AC_WRITE; 146 | } 147 | 148 | bool 149 | is_opnd_immediate(const cs_insn *inst, Opnd opnd) noexcept 150 | { 151 | const cs_x86_op &opnd_ref = inst->detail->x86.operands[to_integral(opnd)]; 152 | return opnd_ref.type == X86_OP_IMM; 153 | } 154 | 155 | bool 156 | is_opnd_rip_immediate(const cs_insn *inst, Opnd opnd) noexcept 157 | { 158 | const cs_x86_op &opnd_ref = inst->detail->x86.operands[to_integral(opnd)]; 159 | return opnd_ref.type == X86_OP_MEM && opnd_ref.mem.base == X86_REG_RIP && 160 | opnd_ref.mem.index == X86_REG_INVALID; 161 | } 162 | 163 | bool 164 | is_opnd_reg(const cs_insn *inst, Opnd opnd) noexcept 165 | { 166 | const cs_x86_op &opnd_ref = inst->detail->x86.operands[to_integral(opnd)]; 167 | return opnd_ref.type == X86_OP_REG; 168 | } 169 | 170 | bool 171 | is_opnd_mem(const cs_insn *inst, Opnd opnd) noexcept 172 | { 173 | const cs_x86_op &opnd_ref = inst->detail->x86.operands[to_integral(opnd)]; 174 | return opnd_ref.type == X86_OP_MEM; 175 | } 176 | 177 | bool 178 | has_one_const_opnd(const cs_insn *inst) noexcept 179 | { 180 | return inst->detail->x86.op_count == 1 && is_opnd_immediate(inst, Opnd::One); 181 | } 182 | 183 | bool 184 | has_rip_rel_const_opnd(const cs_insn *inst) noexcept 185 | { 186 | return inst->detail->x86.op_count == 1 && is_opnd_rip_immediate(inst, Opnd::One); 187 | } 188 | 189 | addr_t 190 | get_direct_branch_target(const cs_insn *inst) noexcept 191 | { 192 | // precondition: inst is a branch instruction 193 | return has_one_const_opnd(inst) ? (addr_t) inst->detail->x86.operands[0].imm : 0; 194 | } 195 | 196 | addr_t 197 | get_rip_rel_branch_target(const cs_insn *inst) noexcept 198 | { 199 | // precondition: inst is a branch instruction 200 | return has_rip_rel_const_opnd(inst) ? inst->address + inst->size + 201 | inst->detail->x86.operands[0].mem.disp : 0; 202 | } 203 | 204 | bool 205 | is_branch_relative(const cs_insn *inst) noexcept 206 | { 207 | for (uint8_t i = 0; i < inst->detail->groups_count; ++i) { 208 | if (inst->detail->groups[i] == CS_GRP_BRANCH_RELATIVE) { 209 | return true; 210 | } 211 | } 212 | return false; 213 | } 214 | 215 | bool 216 | is_rip_relative(const cs_insn *inst) noexcept 217 | { 218 | for (int i = 0; i < inst->detail->x86.op_count; ++i) { 219 | if (inst->detail->x86.operands[i].type != X86_OP_MEM) { 220 | continue; 221 | } 222 | if (inst->detail->x86.operands[i].mem.base == X86_REG_RIP) { 223 | return true; 224 | } 225 | } 226 | return false; 227 | } 228 | 229 | bool 230 | is_loop(const cs_insn *inst) noexcept 231 | { 232 | return inst->id == X86_INS_LOOP || inst->id == X86_INS_LOOPE || 233 | inst->id == X86_INS_LOOPNE; 234 | } 235 | 236 | bool 237 | is_trap(const cs_insn *inst) noexcept 238 | { 239 | return inst->id == X86_INS_UD2 || inst->id == X86_INS_UD0 || 240 | inst->id == X86_INS_UD1 || inst->id == X86_INS_HLT; 241 | } 242 | 243 | bool 244 | is_relative(const cs_insn *inst) noexcept 245 | { 246 | // XXX: assuming that relative instructions 247 | // in x64 can either be (1) relative branch or (2) rip-relative 248 | return is_branch_relative(inst) || is_rip_relative(inst); 249 | } 250 | 251 | bool 252 | is_conditional(const cs_insn *inst) noexcept 253 | { 254 | for (int i = 0; i < inst->detail->regs_read_count; ++i) { 255 | if (inst->detail->regs_read[i] == X86_REG_EFLAGS) { 256 | return true; 257 | } 258 | } 259 | return false; 260 | } 261 | 262 | bool 263 | is_return(const cs_insn *inst) noexcept 264 | { 265 | for (uint8_t i = 0; i < inst->detail->groups_count; ++i) { 266 | if (inst->detail->groups[i] == CS_GRP_RET) { 267 | return true; 268 | } 269 | } 270 | return false; 271 | } 272 | 273 | bool 274 | is_const_xor(const cs_insn *inst) noexcept 275 | { 276 | if (inst->id != X86_INS_XOR) { 277 | return false; 278 | } 279 | auto &operands = inst->detail->x86.operands; 280 | return operands[0].reg == operands[1].reg; 281 | } 282 | 283 | namespace abi { 284 | bool 285 | sysv_is_call_arg_reg(const cs_insn *inst, Opnd opnd) noexcept 286 | { 287 | const cs_x86_op &opnd_ref = inst->detail->x86.operands[to_integral(opnd)]; 288 | if (opnd_ref.type != X86_OP_REG) { 289 | return false; 290 | } 291 | auto reg = get_canonical(opnd_ref.reg); 292 | return reg == X86_REG_RDI || reg == X86_REG_RSI || reg == X86_REG_RDX || 293 | reg == X86_REG_RCX || reg == X86_REG_R8 || reg == X86_REG_R9; 294 | } 295 | 296 | bool 297 | sysv_is_scratch_reg(const cs_insn *inst, Opnd opnd) noexcept 298 | { 299 | const cs_x86_op &opnd_ref = inst->detail->x86.operands[to_integral(opnd)]; 300 | if (opnd_ref.type != X86_OP_REG) { 301 | return false; 302 | } 303 | auto reg = get_canonical(opnd_ref.reg); 304 | return reg == X86_REG_RBX || reg == X86_REG_R10 || reg == X86_REG_R11 || 305 | reg == X86_REG_R12 || reg == X86_REG_R13 || reg == X86_REG_R14 || 306 | reg == X86_REG_R15; 307 | } 308 | 309 | bool 310 | sysv_is_call_result_reg(const cs_insn *inst, Opnd opnd) noexcept 311 | { 312 | const cs_x86_op &opnd_ref = inst->detail->x86.operands[to_integral(opnd)]; 313 | if (opnd_ref.type != X86_OP_REG) { 314 | return false; 315 | } 316 | auto reg = get_canonical(opnd_ref.reg); 317 | return reg == X86_REG_RAX; 318 | } 319 | 320 | } // abi 321 | } // x64 322 | } // bcov 323 | -------------------------------------------------------------------------------- /src/x64/Inst.hpp: -------------------------------------------------------------------------------- 1 | /* **************************************************************************** 2 | * Copyright (c) 2018 University of Kaiserslautern. All rights reserved. 3 | * 4 | * This file is distributed under MIT license. See LICENSE.txt for details. 5 | * 6 | * ****************************************************************************/ 7 | /** 8 | * \brief 9 | */ 10 | 11 | 12 | #pragma once 13 | 14 | #include "core/Common.hpp" 15 | 16 | struct cs_insn; 17 | 18 | namespace bcov { 19 | namespace x64 { 20 | 21 | constexpr unsigned kMaxInstSize = 15; 22 | 23 | enum class Opnd : uint8_t { 24 | One = 0, 25 | Two, 26 | Three, 27 | Four, 28 | Five, 29 | Six, 30 | Seven, 31 | Eight, 32 | END 33 | }; 34 | 35 | bool is_jump(const cs_insn *inst) noexcept __attribute__ ((const)); 36 | 37 | bool is_call(const cs_insn *inst) noexcept __attribute__ ((const)); 38 | 39 | /// @brief: call, jmp, ret or any control-transfer instruction 40 | bool is_branch(const cs_insn *inst) noexcept __attribute__ ((const)); 41 | 42 | uint8_t opnd_count(const cs_insn *inst) noexcept __attribute__ ((const)); 43 | 44 | bool is_opnd_read(const cs_insn *inst, Opnd opnd) noexcept __attribute__ ((const)); 45 | 46 | bool is_opnd_write(const cs_insn *inst, Opnd opnd) noexcept __attribute__ ((const)); 47 | 48 | bool 49 | is_opnd_immediate(const cs_insn *inst, Opnd opnd) noexcept __attribute__ ((const)); 50 | 51 | bool is_opnd_rip_immediate(const cs_insn *inst, 52 | Opnd opnd) noexcept __attribute__ ((const)); 53 | 54 | bool is_opnd_reg(const cs_insn *inst, Opnd opnd) noexcept __attribute__ ((const)); 55 | 56 | bool is_opnd_mem(const cs_insn *inst, Opnd opnd) noexcept __attribute__ ((const)); 57 | 58 | /// @brief: inst has a single immediate argument 59 | bool has_one_const_opnd(const cs_insn *inst) noexcept __attribute__ ((const)); 60 | 61 | /// @brief: inst has a single rip-based immediate argument 62 | bool has_rip_rel_const_opnd(const cs_insn *inst) noexcept __attribute__ ((const)); 63 | 64 | /// precondition: inst is jump. returns zero for indirect jumps 65 | addr_t 66 | get_direct_branch_target(const cs_insn *inst) noexcept __attribute__ ((const)); 67 | 68 | addr_t 69 | get_rip_rel_branch_target(const cs_insn *inst) noexcept __attribute__ ((const)); 70 | 71 | bool is_branch_relative(const cs_insn *inst) noexcept __attribute__ ((const)); 72 | 73 | bool is_rip_relative(const cs_insn *inst) noexcept __attribute__ ((const)); 74 | 75 | bool is_loop(const cs_insn *inst) noexcept __attribute__ ((const)); 76 | 77 | bool is_relative(const cs_insn *inst) noexcept __attribute__ ((const)); 78 | 79 | bool is_trap(const cs_insn *inst) noexcept __attribute__ ((const)); 80 | 81 | /// @brief: inst reads the flags register 82 | bool is_conditional(const cs_insn *inst) noexcept __attribute__ ((const)); 83 | 84 | bool is_return(const cs_insn *inst) noexcept __attribute__ ((const)); 85 | 86 | /// @brief: inst zeros a register by xor'ing it with itself. 87 | bool is_const_xor(const cs_insn *inst) noexcept __attribute__ ((const)); 88 | 89 | namespace abi { 90 | 91 | bool sysv_is_call_arg_reg(const cs_insn *inst, 92 | Opnd opnd) noexcept __attribute__ ((const)); 93 | 94 | bool sysv_is_scratch_reg(const cs_insn *inst, 95 | Opnd opnd) noexcept __attribute__ ((const)); 96 | 97 | bool sysv_is_call_result_reg(const cs_insn *inst, 98 | Opnd opnd) noexcept __attribute__ ((const)); 99 | 100 | } 101 | 102 | } // x64 103 | } // bcov 104 | -------------------------------------------------------------------------------- /src/x64/JumpTabAnalyzer.hpp: -------------------------------------------------------------------------------- 1 | /* **************************************************************************** 2 | * Copyright (c) 2018 University of Kaiserslautern. All rights reserved. 3 | * 4 | * This file is distributed under MIT license. See LICENSE.txt for details. 5 | * 6 | * ****************************************************************************/ 7 | /** 8 | * \brief 9 | */ 10 | 11 | 12 | #pragma once 13 | 14 | #include "core/Function.hpp" 15 | #include "flax/Flax.hpp" 16 | 17 | namespace bcov { 18 | namespace x64 { 19 | 20 | class JumpTabAnalyzer { 21 | public: 22 | 23 | static void 24 | build(const IFunction &func, const BasicBlock &pivot_bb, 25 | flax::FlaxManager *microx_mgr, JumpTable &result); 26 | }; 27 | 28 | } // x64 29 | } // bcov 30 | -------------------------------------------------------------------------------- /tools/bcov-rt/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(bcov-rt C) 2 | 3 | set(CMAKE_C_STANDARD 99) 4 | 5 | set(CMAKE_VERBOSE_MAKEFILE ON) 6 | 7 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -pedantic -pipe") 8 | 9 | set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -fno-omit-frame-pointer") 10 | 11 | set(CMAKE_C_FLAGS_DEBUG_ASAN "${CMAKE_C_FLAGS_DEBUG} -fsanitize=address") 12 | 13 | set(BCOV_DUMP_SOURCES 14 | bcov-rt.c 15 | ${CMAKE_SOURCE_DIR}/src/dump/patch.c 16 | ) 17 | 18 | set_source_files_properties(${BCOV_DUMP_SOURCES} PROPERTIES COMPILE_FLAGS "-Wno-unused-value") 19 | 20 | add_library(bcov-rt SHARED ${BCOV_DUMP_SOURCES}) 21 | 22 | install(TARGETS bcov-rt 23 | RUNTIME DESTINATION bin 24 | LIBRARY DESTINATION lib 25 | ) 26 | 27 | -------------------------------------------------------------------------------- /tools/bcov-rt/bcov-rt.c: -------------------------------------------------------------------------------- 1 | /* **************************************************************************** 2 | * Copyright (c) 2018 University of Kaiserslautern. All rights reserved. 3 | * 4 | * This file is distributed under MIT license. See LICENSE.txt for details. 5 | * 6 | * ****************************************************************************/ 7 | /** 8 | * \brief 9 | */ 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include "dump/patch.h" 21 | #include "bcov-rt.h" 22 | 23 | #define BCOV_DIR_SEP '/' 24 | #define BCOV_FILE_SUFFIX ".bcov" 25 | #define BCOV_OPTS_NAME "BCOV_OPTIONS" 26 | #define BCOV_OPTS_DIR_NAME "coverage_dir" 27 | #define BCOV_OPTS_PID_NAME "log_pid" 28 | #define BCOV_OPTS_USER_TAG_NAME "tag" 29 | #define BCOV_OPTS_USER_TAG_SIZE 32 30 | #define BCOV_OPTS_TIME_TAG_SIZE 16 31 | #define MAX_MOD_NAME_SIZE (64) 32 | #define MAX_MOD_PATH_SIZE (1024) 33 | #define MEM_PAGE_SIZE (0x1000U) 34 | #define UNUSED(x) ((void)(x)) 35 | 36 | #ifdef NDEBUG 37 | #define DEBUG_PRINT(fmt, ...) \ 38 | do { ((void)(fmt, __VA_ARGS__)); } while (0) 39 | #else 40 | #define DEBUG_PRINT(fmt, ...) \ 41 | do { fprintf(stderr, fmt, __VA_ARGS__); } while (0) 42 | #endif 43 | 44 | static bool 45 | is_data_segment(const char *permissions) 46 | { 47 | return permissions[0] == 'r' && permissions[1] == 'w'; 48 | } 49 | 50 | static bool 51 | begins_with_bcov_magic(uint64_t addr) 52 | { 53 | return bcov_has_valid_magic((const uint8_t *) addr); 54 | } 55 | 56 | static const char * 57 | get_basename(const char *mod_name) 58 | { 59 | return strrchr(mod_name, '/'); 60 | } 61 | 62 | static void 63 | parse_line(const char *line, size_t *mod_start, size_t *mod_end, char *perms, 64 | size_t *offset, char *device, int *inode, char *mod_name) 65 | { 66 | sscanf(line, "%lx-%lx %s %lx %s %d %s", mod_start, mod_end, 67 | perms, offset, device, inode, mod_name); 68 | } 69 | 70 | static void 71 | make_current_time_tag(char *out_str) 72 | { 73 | long ms; // Milliseconds 74 | time_t s; // Seconds 75 | struct timespec spec; 76 | clock_gettime(CLOCK_REALTIME, &spec); 77 | s = spec.tv_sec; 78 | ms = (spec.tv_nsec >> 19); 79 | sprintf(out_str, ".%lu.%04li", s, ms); 80 | } 81 | 82 | static bool 83 | copy_user_option(const char *opts_str, char *output, unsigned len) 84 | { 85 | char *c = output; 86 | const char *n = opts_str; 87 | while (*n != '\0' && *n != ',' && 88 | (c < output + len)) { 89 | *c = *n; 90 | ++n; 91 | ++c; 92 | } 93 | *c = 0; 94 | return *n == 0 || *n == ','; 95 | } 96 | 97 | static bool 98 | parse_options(char *output_path, char *user_tag, bool *log_pid) 99 | { 100 | const char *opts_str_st = getenv(BCOV_OPTS_NAME); 101 | const char *str_p; 102 | if (opts_str_st == NULL) { 103 | *output_path = '\0'; 104 | *user_tag = '\0'; 105 | *log_pid = false; 106 | return true; 107 | } 108 | *log_pid = strstr(opts_str_st, BCOV_OPTS_PID_NAME) != NULL; 109 | 110 | str_p = strstr(opts_str_st, BCOV_OPTS_DIR_NAME); 111 | if (str_p == NULL) { 112 | *output_path = '\0'; 113 | } else { 114 | str_p += sizeof(BCOV_OPTS_DIR_NAME); 115 | if (!copy_user_option(str_p, output_path, MAX_MOD_PATH_SIZE)) { 116 | return false; 117 | } 118 | } 119 | 120 | str_p = strstr(opts_str_st, BCOV_OPTS_USER_TAG_NAME); 121 | if (str_p == NULL) { 122 | *user_tag = '\0'; 123 | } else { 124 | str_p += sizeof(BCOV_OPTS_USER_TAG_NAME); 125 | if (!copy_user_option(str_p, user_tag, BCOV_OPTS_USER_TAG_SIZE)) { 126 | return false; 127 | } 128 | } 129 | return true; 130 | } 131 | 132 | static bool 133 | make_file_path_suffix(const char *module_path, const char *user_tag, char *path) 134 | { 135 | const char *basename = get_basename(module_path); 136 | size_t basename_len = strnlen(basename, MAX_MOD_NAME_SIZE); 137 | if (basename_len == 0) { 138 | DEBUG_PRINT("module name too long: %s\n", module_path); 139 | return false; 140 | } 141 | 142 | char *c = path; 143 | while (*c != '\0') ++c; 144 | if (path != c) { 145 | *(c++) = BCOV_DIR_SEP; 146 | } 147 | strcpy(c, basename + 1); 148 | c += basename_len - 1; 149 | make_current_time_tag(c); 150 | c += BCOV_OPTS_TIME_TAG_SIZE; 151 | if (*user_tag) { 152 | *(c++) = '.'; 153 | strncpy(c, user_tag, BCOV_OPTS_USER_TAG_SIZE); 154 | c += strnlen(user_tag, BCOV_OPTS_USER_TAG_SIZE); 155 | } 156 | strcpy(c, BCOV_FILE_SUFFIX); 157 | return true; 158 | } 159 | 160 | static void 161 | dump_bcov_data_segments() 162 | { 163 | static FILE *maps_file; 164 | static FILE *dump_file; 165 | static size_t line_len = 0; 166 | static size_t mod_start, mod_end, offset; 167 | static size_t data_size = 0; 168 | static size_t base_address = 0; 169 | static char line[MAX_MOD_PATH_SIZE]; 170 | static char permissions[5]; 171 | static char device[8]; 172 | static char parsed_module_path[MAX_MOD_PATH_SIZE]; 173 | static char output_path[MAX_MOD_PATH_SIZE]; 174 | static char user_tag[BCOV_OPTS_USER_TAG_SIZE]; 175 | static int inode; 176 | static bool log_pid; 177 | 178 | maps_file = fopen("/proc/self/maps", "r"); 179 | if (maps_file == NULL) { 180 | fprintf(stderr, "failed to open process maps!\n"); 181 | return; 182 | } 183 | 184 | char *line_c = &line[0]; 185 | 186 | if (parse_options(output_path, user_tag, &log_pid) != true) { 187 | DEBUG_PRINT("unknown %s in parsing options \n", "error"); 188 | return; 189 | } 190 | 191 | while ((getline(&(line_c), &line_len, maps_file)) != -1) { 192 | parse_line(line_c, &mod_start, &mod_end, permissions, &offset, device, 193 | &inode, parsed_module_path); 194 | 195 | if (offset == 0 && inode != 0) { 196 | base_address = mod_start; 197 | } 198 | 199 | if (!is_data_segment(permissions) || !begins_with_bcov_magic(mod_start)) { 200 | continue; 201 | } 202 | 203 | if (!make_file_path_suffix(parsed_module_path, user_tag, output_path)) { 204 | continue; 205 | } 206 | 207 | uint8_t *hdr_buf = (uint8_t *) mod_start; 208 | bcov_write_base_address(hdr_buf, base_address); 209 | if (log_pid) { 210 | bcov_write_process_id(hdr_buf); 211 | } 212 | data_size = bcov_read_probe_count(hdr_buf); 213 | if (data_size > (mod_end - mod_start) || data_size == 0) { 214 | DEBUG_PRINT("%s: invalid size expected <= %lx, found %lx! \n", 215 | output_path, mod_end - mod_start, data_size); 216 | continue; 217 | } 218 | data_size += BCOV_DATA_HDR_SIZE; 219 | dump_file = fopen(output_path, "wb"); 220 | if (dump_file == NULL) { 221 | DEBUG_PRINT("%s: failed to open dump file!\n", output_path); 222 | return; 223 | } 224 | if (fwrite((const char *) hdr_buf, 1, data_size, dump_file) != data_size) { 225 | DEBUG_PRINT("%s: %lx-%lx, file write error! \n", 226 | output_path, mod_start, mod_end); 227 | } else { 228 | DEBUG_PRINT("%s: %lx-%lx, %lu page(s) written successfully. \n", 229 | output_path, mod_start, mod_end, 230 | (data_size / MEM_PAGE_SIZE)); 231 | } 232 | fclose(dump_file); 233 | } 234 | 235 | fclose(maps_file); 236 | } 237 | 238 | static void 239 | user_signal_handler(int signum, siginfo_t *info, void *ptr) 240 | { 241 | UNUSED(info); 242 | UNUSED(ptr); 243 | DEBUG_PRINT("received bcov dump request %d. ", signum); 244 | dump_bcov_data_segments(); 245 | if (signum == SIGINT) { 246 | signal(SIGINT, SIG_DFL); 247 | } 248 | } 249 | 250 | void 251 | bcov_init(void) 252 | { 253 | static struct sigaction act; 254 | memset(&act, 0, sizeof(act)); 255 | act.sa_sigaction = user_signal_handler; 256 | act.sa_flags = SA_SIGINFO; 257 | #ifdef ONLINE_COVERAGE 258 | sigaction(SIGUSR1, &act, NULL); 259 | #endif 260 | } 261 | 262 | void 263 | bcov_fini(void) 264 | { 265 | dump_bcov_data_segments(); 266 | } 267 | -------------------------------------------------------------------------------- /tools/bcov-rt/bcov-rt.h: -------------------------------------------------------------------------------- 1 | /* **************************************************************************** 2 | * Copyright (c) 2018 University of Kaiserslautern. All rights reserved. 3 | * 4 | * This file is distributed under MIT license. See LICENSE.txt for details. 5 | * 6 | * ****************************************************************************/ 7 | /** 8 | * \brief 9 | */ 10 | 11 | #pragma once 12 | 13 | 14 | static void bcov_init(void) __attribute__((constructor (0x1000))); 15 | 16 | static void bcov_fini(void) __attribute__((destructor (0x1000))); 17 | --------------------------------------------------------------------------------