├── CMakeLists.txt ├── LICENSE ├── README ├── cmake ├── kmodule_sources │ ├── check_kernel_config │ │ └── module.c │ └── check_module_build │ │ └── module.c ├── modules │ ├── FindKbuild.cmake │ ├── cmake_useful.cmake │ ├── kbuild_system.cmake │ ├── kbuild_system_files │ │ ├── Kbuild.in │ │ └── Kbuild_object.in │ ├── kmodule.cmake │ ├── kmodule_files │ │ ├── CMakeLists.txt │ │ └── Kbuild.in │ └── path_prefixes.cmake └── other_sources │ ├── dwfl_report_elf_check.c │ ├── libdw_check.c │ └── libelf_check.c ├── cmake_uninstall.cmake.in ├── common ├── gen-insn-attr-x86.awk ├── inat.c ├── inat.h ├── inat_types.h ├── insn.c ├── insn.h ├── util.c ├── util.h └── x86-opcode-map.txt ├── config.h.in ├── core ├── CMakeLists.txt ├── insn_analysis.c ├── insn_analysis.h ├── racehound_main.c ├── stackdepot.c ├── stackdepot.h ├── tests │ ├── CMakeLists.txt │ ├── basics │ │ ├── CMakeLists.txt │ │ ├── minus.sh │ │ ├── minus_infinite.sh │ │ ├── plus.sh │ │ ├── plus_infinite.sh │ │ ├── test.sh.in │ │ ├── test_active.sh.in │ │ └── test_modules.sh.in │ ├── common_target │ │ ├── CMakeLists.txt │ │ ├── Kbuild.in │ │ ├── cfake.c │ │ ├── cfake.h │ │ ├── lines.list │ │ └── module.makefile.in │ └── simple │ │ ├── CMakeLists.txt │ │ ├── lines.list │ │ └── module.c └── thunks.S ├── examples ├── check_races.py └── events.py ├── kernel_patches ├── Readme.txt └── kernel_4.1-4.3 │ ├── 0001-kprobes-x86-boost-Fix-checking-if-there-is-enough-ro.patch │ ├── 0002-kprobes-x86-Use-16-bytes-for-each-instruction-slot-a.patch │ └── Readme.txt ├── lines2insns ├── CMakeLists.txt ├── config_lines2insns.h.in └── main.cpp └── ma_lines ├── CMakeLists.txt ├── Readme.txt ├── common_includes.h ├── ma_lines.cpp └── tests ├── CMakeLists.txt ├── Kbuild.in └── test.sh.in /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8.10) 2 | 3 | enable_language(C) 4 | enable_language(CXX) 5 | 6 | ####################################################################### 7 | # Prohibit a common type of an in-source build. 8 | # Note that building in a subdirectory in the source tree is still allowed 9 | # as it can be convenient. 10 | string (COMPARE EQUAL "${CMAKE_SOURCE_DIR}" "${CMAKE_BINARY_DIR}" in_source) 11 | if (in_source) 12 | message (FATAL_ERROR 13 | "It is not allowed to build the project in its top source directory." 14 | ) 15 | endif () 16 | 17 | ####################################################################### 18 | # Names and versions 19 | set(RH_PACKAGE_NAME "RaceHound") 20 | 21 | set(RH_VERSION_MAJOR 1) 22 | set(RH_VERSION_MINOR 1) 23 | 24 | set(RH_PACKAGE_VERSION "${RH_VERSION_MAJOR}.${RH_VERSION_MINOR}") 25 | ####################################################################### 26 | 27 | if (CMAKE_SIZEOF_VOID_P EQUAL 8) 28 | set (RH_64_BIT "yes") 29 | else () 30 | set (RH_64_BIT "no") 31 | endif() 32 | ####################################################################### 33 | 34 | set(COMMON_SOURCE_DIR "${CMAKE_SOURCE_DIR}/common") 35 | set(COMMON_BINARY_DIR "${CMAKE_BINARY_DIR}/common") 36 | 37 | file(MAKE_DIRECTORY "${COMMON_BINARY_DIR}") 38 | 39 | # This is needed for CMakeLists.txt from other directories could also refer 40 | # to inat-tables.c via this target. 41 | add_custom_target(insn_decoder_headers 42 | DEPENDS "${COMMON_BINARY_DIR}/inat-tables.c" 43 | ) 44 | 45 | add_custom_command(OUTPUT "${COMMON_BINARY_DIR}/inat-tables.c" 46 | COMMAND LC_ALL=C awk -f "${COMMON_SOURCE_DIR}/gen-insn-attr-x86.awk" 47 | "${COMMON_SOURCE_DIR}/x86-opcode-map.txt" > 48 | "${COMMON_BINARY_DIR}/inat-tables.c" 49 | DEPENDS "${COMMON_SOURCE_DIR}/x86-opcode-map.txt" 50 | ) 51 | ####################################################################### 52 | 53 | set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/modules) 54 | find_package(Kbuild) 55 | 56 | include(cmake_useful) 57 | include(kbuild_system) 58 | include(kmodule) 59 | 60 | include(path_prefixes) 61 | 62 | ####################################################################### 63 | # Initialize test-related stuff 64 | rh_test_init() 65 | 66 | ####################################################################### 67 | #rh_install_library(library_name) 68 | function(rh_install_library library_name) 69 | install(TARGETS ${library_name} LIBRARY 70 | DESTINATION ${RH_INSTALL_PREFIX_LIB}) 71 | endfunction(rh_install_library library_name) 72 | #rh_install_headers(install_subdir header_file [..]) 73 | function(rh_install_headers install_subdir) 74 | install(FILES ${header_file} ${ARGN} 75 | DESTINATION ${RH_INSTALL_PREFIX_INCLUDE}/${install_subdir}) 76 | endfunction(rh_install_headers install_subdir) 77 | #rh_install_kmodule(kmodule_name) 78 | function(rh_install_kmodule kmodule_name) 79 | install(FILES "${CMAKE_CURRENT_BINARY_DIR}/${kmodule_name}.ko" 80 | DESTINATION "${RH_INSTALL_PREFIX_KMODULE}") 81 | endfunction(rh_install_kmodule kmodule_name) 82 | #rh_install_symvers(kmodule_name) 83 | function(rh_install_symvers kmodule_name) 84 | install(FILES "${CMAKE_CURRENT_BINARY_DIR}/Module.symvers" 85 | DESTINATION "${RH_INSTALL_PREFIX_KSYMVERS}" 86 | RENAME "${kmodule_name}.symvers") 87 | endfunction(rh_install_symvers kmodule_name) 88 | 89 | ####################################################################### 90 | configure_file( 91 | "${CMAKE_CURRENT_SOURCE_DIR}/cmake_uninstall.cmake.in" 92 | "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" 93 | IMMEDIATE @ONLY 94 | ) 95 | 96 | add_custom_target (uninstall_files 97 | "${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" 98 | ) 99 | 100 | add_custom_target (uninstall) 101 | 102 | # During uninstall process, the files should be removed first, then 103 | # the directories. 104 | # 'uninstall_dirs' target is defined in cmake/modules/path_prefixes.cmake. 105 | add_dependencies (uninstall_dirs uninstall_files) 106 | add_dependencies (uninstall uninstall_dirs) 107 | 108 | ####################################################################### 109 | # Include configuration file (config.h) 110 | include_directories("${CMAKE_BINARY_DIR}") 111 | kbuild_include_directories("${CMAKE_BINARY_DIR}") 112 | ####################################################################### 113 | # Make "Release" the default build type 114 | if (NOT CMAKE_BUILD_TYPE) 115 | set (CMAKE_BUILD_TYPE "Release" CACHE STRING 116 | "Choose the type of build, options are: Debug Release RelWithDebInfo." 117 | FORCE) 118 | endif () 119 | message (STATUS "Build type is \"${CMAKE_BUILD_TYPE}\"") 120 | 121 | if (CMAKE_BUILD_TYPE STREQUAL "Debug") 122 | kbuild_add_definitions( 123 | "-g -DRH_DEBUG" 124 | ) 125 | elseif(CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo") 126 | kbuild_add_definitions( 127 | "-g" 128 | ) 129 | elseif(CMAKE_BUILD_TYPE STREQUAL "Release") 130 | else(CMAKE_BUILD_TYPE STREQUAL "Debug") 131 | message(FATAL_ERROR "Unknown type of build: ${CMAKE_BUILD_TYPE}.") 132 | endif() 133 | 134 | # Flags to compiler when build user-space programs 135 | set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -g -O0 -Wall -Wextra") 136 | set(CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO} -g -Wall -Wextra") 137 | set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -Wall") 138 | 139 | ####################################################################### 140 | # Perform basic checks 141 | 142 | # Check if we use kernel version 3.10 or newer 143 | check_kernel_version(3 10 0) 144 | 145 | # Check if kernel modules can be built on this system 146 | check_module_build() 147 | 148 | # Check kernel configuration 149 | check_kernel_config() 150 | ####################################################################### 151 | 152 | # The kernel-space part of the system 153 | add_subdirectory(core) 154 | ####################################################################### 155 | 156 | include(CheckCXXCompilerFlag) 157 | 158 | set(CPP11_COMPILER_FLAG "") 159 | CHECK_CXX_COMPILER_FLAG("-std=c++11" COMPILER_SUPPORTS_CXX11) 160 | CHECK_CXX_COMPILER_FLAG("-std=c++0x" COMPILER_SUPPORTS_CXX0X) 161 | 162 | if(COMPILER_SUPPORTS_CXX11) 163 | set(CPP11_COMPILER_FLAG "-std=c++11") 164 | elseif(COMPILER_SUPPORTS_CXX0X) 165 | set(CPP11_COMPILER_FLAG "-std=c++0x") 166 | else() 167 | message(STATUS 168 | "The compiler ${CMAKE_CXX_COMPILER} has no C++11 support.") 169 | endif() 170 | ####################################################################### 171 | 172 | option(BUILD_MA_LINES_PLUGIN 173 | "Build \"ma_lines\" GCC plugin." 174 | ON 175 | ) 176 | 177 | if (BUILD_MA_LINES_PLUGIN) 178 | set(do_build_ma_lines TRUE) 179 | 180 | # Build the GCC plugin only if GCC is used as the compiler. 181 | if (NOT CMAKE_COMPILER_IS_GNUCC OR 182 | CMAKE_C_COMPILER_VERSION VERSION_LESS 4.9) 183 | message(WARNING 184 | "\"ma_lines\" plugin requires GCC 4.9 or newer, skipping.") 185 | set(do_build_ma_lines FALSE) 186 | else () 187 | # Find the directory with the header files for GCC plugins 188 | execute_process ( 189 | COMMAND ${CMAKE_C_COMPILER} -print-file-name=plugin 190 | OUTPUT_VARIABLE plugin_dir 191 | OUTPUT_STRIP_TRAILING_WHITESPACE 192 | ) 193 | if (NOT plugin_dir OR 194 | plugin_dir STREQUAL "plugin") 195 | message(WARNING 196 | "GCC plugin directory is missing on this system. " 197 | "Please check if the packages GCC needs to support plugins are installed.") 198 | set(do_build_ma_lines FALSE) 199 | else () 200 | # Check if include/gcc-plugin.h exists there. 201 | find_file(gcc_plugin_h_file 202 | "gcc-plugin.h" 203 | PATHS "${plugin_dir}/include" 204 | NO_DEFAULT_PATH) 205 | if (NOT gcc_plugin_h_file) 206 | message(WARNING 207 | " header is missing on this system. " 208 | "Please check if the packages GCC needs to support plugins are installed.") 209 | set(do_build_ma_lines FALSE) 210 | endif() 211 | endif () 212 | endif() 213 | if (do_build_ma_lines) 214 | # This GCC plugin can be used when compiling the sources of 215 | # the kernel and the modules. It outputs the source lines 216 | # (file:line or so) where memory accesses happen, hence 217 | # "ma_" in the name. 218 | add_subdirectory(ma_lines) 219 | else() 220 | message (STATUS 221 | "Not building \"ma_lines\": it requires GCC 4.9 or newer with plugin support.") 222 | endif() 223 | endif () # BUILD_MA_LINES_PLUGIN 224 | ####################################################################### 225 | 226 | option(BUILD_LINES2INSNS 227 | "Build \"lines2insns\" tool" 228 | ON 229 | ) 230 | 231 | if (BUILD_LINES2INSNS) 232 | # Check if the development files for libelf and libdw are available. 233 | check_libelf_devel() 234 | check_libdw_devel() 235 | 236 | if (CPP11_COMPILER_FLAG) 237 | # This tool takes the list of source lines (e.g., collected 238 | # by "ma_lines" plugin) as well as the binary file with 239 | # debug info and outputs the locations of instructions 240 | # corresponding to those lines, filtered as necessary. These 241 | # data can than be fed to the kernel part of RaceHound to 242 | # track the appropriate memory accesses. 243 | add_subdirectory(lines2insns) 244 | else () 245 | message (WARNING 246 | "Not building \"lines2insns\": it requires a compiler that supports C++11.") 247 | endif () 248 | endif() 249 | ####################################################################### 250 | 251 | configure_file("${CMAKE_SOURCE_DIR}/config.h.in" 252 | "${CMAKE_BINARY_DIR}/config.h") 253 | message(STATUS "Creating config.h - done") 254 | ####################################################################### 255 | 256 | message(STATUS 257 | "Configured ${RH_PACKAGE_NAME} version ${RH_PACKAGE_VERSION}") 258 | ####################################################################### 259 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | # RaceHound 2 | -------------- 3 | RaceHound can be used to detect data races in the Linux kernel on x86. 4 | 5 | Kernel 3.10 or newer is required, 4.1 or newer is recommended. 6 | Kprobes, kallsyms and debugfs support should be enabled in the kernel. 7 | 8 | The main source code repository of RaceHound, releases and issue tracker 9 | are available at https://github.com/euspectre/racehound. 10 | 11 | # How it works 12 | --------------- 13 | 14 | The ideas implemented here are similar to how DataCollider tool operates 15 | on MS Windows. 16 | 17 | The kernel part of RaceHound (racehound.ko module) monitors the instructions 18 | in the kernel code in runtime. It operates as follows: 19 | 20 | 1. Place software breakpoints (Kprobes, actually) at the locations specified 21 | by the user. 22 | 23 | 2. When a software breakpoint hits, check if the corresponding instruction 24 | is about to access memory. Determine the address and the size of that memory 25 | area. 26 | 27 | 3. Save the contents of that area (or at least a part of it). 28 | 29 | 4. Place a hardware breakpoint on that memory area to detect writes (if the 30 | instruction reads from it) or both reads and writes (if the instruction 31 | writes to it). 32 | 33 | 5. Make a delay. The length of the delay is configurable. 34 | If some code tried to access that memory area during the delay, the hardware 35 | breakpoint might have detected it. 36 | 37 | 6. Disarm the hardware breakpoint. 38 | 39 | 7. Just in case, check if the contents of that memory area have changed 40 | during the delay. Might help detect races that were not caught above. 41 | 42 | 8. Let the execution continue as usual. 43 | 44 | The found races are reported to the system log and to racehound/events file 45 | in debugfs. 46 | 47 | 48 | # Build prerequisites 49 | -------------- 50 | 51 | * cmake 2.8.10 or newer. 52 | 53 | * Everything that is needed to build kernel modules (kernel development 54 | files, etc.). 55 | 56 | * GCC C compiler and C++ compiler with C++11 support (GCC 4.9 or newer is 57 | preferable). 58 | 59 | * (only if you want to build "ma_lines" plugin) GCC development files needed 60 | to build plugins. 61 | 62 | * elfutils, libelf, libdw and their development files (for example, install 63 | elfutils-devel and elfutils-libelf-devel if you use Fedora, RHEL or the like). 64 | 65 | # Build 66 | -------------- 67 | 68 | $ mkdir racehound.build 69 | $ cd racehound.build 70 | $ cmake path_to_racehound_sources 71 | $ make 72 | 73 | To install RaceHound, you can now execute "make install" as root. 74 | 75 | Note that RaceHound is installed for the current kernel only. If you update 76 | or otherwise change the kernel, please rebuild RaceHound and install it 77 | again. 78 | 79 | # Self-tests (optional) 80 | -------------- 81 | 82 | To build the tests provided with RaceHound, run "make build_tests" in the 83 | directory where RaceHound was built. Then you can run "ctest" as root there 84 | to run all the tests. 85 | 86 | The tests check the basic functionality of RaceHound. 87 | 88 | # Usage 89 | -------------- 90 | 91 | It is assumed here that debugfs is available and mounted to /sys/kernel/debug. 92 | 93 | ## Scenario 1: monitoring a set of instructions 94 | 95 | 1. The user can tell the kernel part of RaceHound to monitor the particular 96 | instructions in the kernel code. The locations of these instructions should 97 | be written to /sys/kernel/debug/racehound/breakpoints in the following 98 | format: 99 | 100 | [:]{init|core}+0xoffset[,delay=] 101 | 102 | If is not specified, the kernel or a built-in module is 103 | assumed. 104 | 105 | "init" and "core" are the areas containing the code of the kernel or a 106 | module in memory. See core_layout/module_core, etc., in struct module. Dealing 107 | with the ELF sections in the kernel has its difficulties, same for the 108 | addresses and sizes of the functions, so RaceHound uses "init" and "core" 109 | areas instead. 110 | 111 | "delay" can be used to set the length of the delay for the given monitored 112 | instruction when a software breakpoint hits. If it is not specified, "delay" 113 | parameter of "racehound" kernel module will be used (or "delay_in_atomic" 114 | in atomic context, if set). The value is in milliseconds. 115 | 116 | lines2insns tool from this project can help prepare the location strings in 117 | the correct format. 118 | 119 | Suppose you would like to monitor the memory accesses corresponding to the 120 | lines 82 and 84 in something.c source file of test_module.ko. The module 121 | should be built with debug info. Then you can do the following. 122 | 123 | $ echo "something.c:82" | lines2insns test_module.ko 124 | test_module:core+0x22f 125 | 126 | $ echo "something.c:84" | lines2insns test_module.ko 127 | test_module:core+0x251 128 | 129 | If you are interested, say, only in writes at something.c:84, you can 130 | specify this as well: 131 | 132 | $ echo "something.c:84:write" | lines2insns test_module.ko 133 | test_module:core+0x251 134 | 135 | If you know the ELF sections and the offsets there for the instructions of 136 | interest, lines2insns can convert that to the proper format too: 137 | $ echo "test_module:.text+0x251" | lines2insns --section-to-area test_module.ko 138 | test_common_target:core+0x251 139 | 140 | $ echo "test_module:.exit.text+0x1" | lines2insns --section-to-area test_module.ko 141 | test_common_target:core+0x335 142 | 143 | $ echo "test_module:.init.text+0xdd" | lines2insns --section-to-area test_module.ko 144 | test_common_target:init+0xdd 145 | 146 | See lines2insns --help for more details. 147 | 148 | So, suppose you would like to monitor the instructions at the following 149 | locations in test_module: 150 | test_module:core+0x22f 151 | test_module:core+0x251 152 | ...as well as one location in the kernel proper or in a built-in module: 153 | core+0x77654 154 | 155 | 2. Load racehound.ko if it is not loaded yet. 156 | 157 | insmod /usr/local/lib/modules/$(uname -r)/misc/racehound.ko [delay=...] 158 | 159 | You can optionally specify how long to delay execution of the instructions 160 | to check for the conflicting memory accesses. "delay" parameter of 161 | "racehound" kernel module can be used for that. The value is in milliseconds. 162 | The default is about 5000/HZ (5 jiffies). 163 | 164 | If you would like to use a different delay for atomic context, please 165 | specify it in "delay_in_atomic" parameter. 166 | 167 | 3. Instruct RaceHound to monitor the given instructions (as root). 168 | 169 | echo "test_module:core+0x22f" > /sys/kernel/debug/racehound/breakpoints 170 | echo "test_module:core+0x251" > /sys/kernel/debug/racehound/breakpoints 171 | echo "core+0x77654,delay=50" > /sys/kernel/debug/racehound/breakpoints 172 | 173 | Note that the execution of the instruction at "core+0x77654" will be delayed 174 | by 50 milliseconds ("delay=50") no matter which global settings for delays 175 | RaceHound has. 176 | 177 | Reading /sys/kernel/debug/racehound/breakpoints will show the list of the 178 | instructions to be monitored. 179 | 180 | Note that it is no longer required to load RaceHound before the analyzed 181 | module(s). 182 | 183 | As soon as RaceHound receives the list of instructions to monitor, it will 184 | check if the corresponding components of the kernel are loaded. If they are, 185 | the monitoring will start immediately. If a module is not loaded yet, 186 | RaceHound will wait for it to load and will process it after that. 187 | 188 | Different kernel components can be monitored simultaneously. 189 | 190 | To stop monitoring an instruction, you can write the same string as before 191 | to /sys/kernel/debug/racehound/breakpoints but with '-' prepended. Example: 192 | 193 | echo "-core+0x77654" > /sys/kernel/debug/racehound/breakpoints 194 | 195 | 4. Make the analyzed kernel code work. 196 | 197 | 5. If RaceHound detects a race, it will output something like this to the 198 | system log (see dmesg): 199 | 200 | [rh] Detected a data race on the memory block at ffffffffa09b936c 201 | between the instruction at test_module:core+0x22f (comm: "sh") 202 | and the instruction right before my_func+0x18/0x20 [test_module] (comm: "sh"). 203 | 204 | or 205 | 206 | [rh] Detected a data race on the memory block at ffffffffa09b936c 207 | that is about to be accessed by the instruction at 208 | test_module:core+0x251 (comm: "sh"): 209 | the memory block was modified during the delay. 210 | 211 | If the race is detected only because the memory area has been modified 212 | during the delay, RaceHound, obviously, has no idea which code has done that 213 | modification. On the other hand, the hardware breakpoints report the both 214 | involved parties. 215 | 216 | 6. If "test_module" is built with debug info, you can use addr2line or a 217 | similar tool to find the source lines for the conflicting access 218 | (my_func+0x18/0x20 in the example above). 219 | 220 | 7. Unload "racehound" module when it is no longer needed. If the analyzed 221 | kernel components are still loaded then, RaceHound will automatically 222 | "detach" from them first. 223 | 224 | Notes and tips 225 | 226 | * If the kernel is built with CONFIG_PREEMPT=y, it may help make the system 227 | more responsive when using RaceHound. Especially, - if you use the delays in 228 | seconds or longer. 229 | 230 | * There are only 4 hardware breakpoints available on an x86 CPU, so monitoring 231 | too many instructions that execute often may be pointless. The exact values 232 | of "too many" and "often" may vary, of course. 233 | 234 | * If the "hot code paths" are constantly monitored, the performance overhead 235 | may become significant as well. 236 | 237 | * If you suspect two instructions to race against each other, it is usually 238 | better to monitor only one of them. The reports about the races might be 239 | less useful otherwise: the accesses from RaceHound itself may be listed 240 | there. 241 | 242 | * If you try to write a location to monitor to 243 | /sys/kernel/debug/racehound/breakpoints and it fails, check the system log 244 | (dmesg), it may provide more info. 245 | 246 | On x86-64, adding a location to monitor may also fail if the kernel does not 247 | have the following fix: 248 | "kprobes/x86: Return correct length in __copy_instruction()" 249 | (commit c80e5c0c23ce2282476fdc64c4b5e3d3a40723fd in the mainline, included 250 | into kernel 4.1). 251 | ------------------ 252 | 253 | ## Scenario 2: sweeping through the code 254 | 255 | The idea is as follows. Suppose we have a list of the instructions from the 256 | given portion of the code, say, part of a module or of the kernel. The list 257 | may be quite long and monitoring all these instructions at the same time can 258 | be a bad idea (performance overhead, etc.). 259 | 260 | Note that "ma_lines" plugin for GCC 4.9+ can be used to get the list of the 261 | locations in the source code where memory accesses may happen (except the 262 | code written in assembly). lines2insns will translate it to the list of 263 | locations in the binary code. See ma_lines/Readme.txt. 264 | 265 | The kernel part of RaceHound provides info about which software breakpoints 266 | have been hit and which races have been found. It is available via 267 | /sys/kernel/debug/racehound/events file. Poll/select can be used for that 268 | file to wait till the events are available there. The events can then be 269 | read, one per line. 270 | 271 | The format for the "BP hit" events is the same as used for 272 | racehound/breakpoints file, see above. 273 | 274 | For the found races, the corresponding event lines are prefixed with 275 | "[race]". 276 | 277 | An example that demonstrates the usage of this API is also provided: 278 | https://github.com/euspectre/racehound/blob/master/examples/events.py 279 | 280 | A user-space application may use the information about the events to 281 | automatically start and stop monitoring the instructions from the list 282 | according to some policy. This should allow to keep overhead at the 283 | acceptable level. 284 | 285 | An example of such application is provided here: 286 | https://github.com/euspectre/racehound/blob/master/examples/check_races.py 287 | 288 | Note that both events.py and check_races.py need Python 3.4 or newer. 289 | -------------------------------------------------------------------------------- /cmake/kmodule_sources/check_kernel_config/module.c: -------------------------------------------------------------------------------- 1 | /* Here we check if some basic features of the kernel are enabled 2 | * in its configuration. Note that only the features required for 3 | * our system should be checked here. */ 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | MODULE_LICENSE("GPL"); 10 | 11 | #if !defined(CONFIG_X86_32) && !defined(CONFIG_X86_64) 12 | #error Unknown architecture: neither CONFIG_X86_32 nor CONFIG_X86_64 \ 13 | is set in the kernel config file. 14 | #endif 15 | 16 | #if !defined(CONFIG_MODULES) 17 | #error Kernel modules are not supported by the kernel \ 18 | (CONFIG_MODULES is not set in the kernel config file). 19 | #endif 20 | 21 | #if !defined(CONFIG_MODULE_UNLOAD) 22 | #error Unloading of kernel modules is not supported by the kernel \ 23 | (CONFIG_MODULE_UNLOAD is not set in the kernel config file). 24 | #endif 25 | 26 | #if !defined(CONFIG_SYSFS) 27 | #error Sysfs is not supported by the kernel \ 28 | (CONFIG_SYSFS is not set in the kernel config file). 29 | #endif 30 | 31 | #if !defined(CONFIG_DEBUG_FS) 32 | #error Debugfs is not supported by the kernel \ 33 | (CONFIG_DEBUG_FS is not set in the kernel config file). 34 | #endif 35 | 36 | #if !defined(CONFIG_KALLSYMS) 37 | #error Loading of kernel symbols into the kernel image is not supported \ 38 | by the kernel \ 39 | (CONFIG_KALLSYMS is not set in the kernel config file). 40 | #endif 41 | 42 | /* The rest of the code does not really matter as long as it is correct 43 | * from the compiler's point of view. */ 44 | static int __init 45 | my_init(void) 46 | { 47 | return 0; 48 | } 49 | 50 | static void __exit 51 | my_exit(void) 52 | { 53 | } 54 | 55 | module_init(my_init); 56 | module_exit(my_exit); 57 | -------------------------------------------------------------------------------- /cmake/kmodule_sources/check_module_build/module.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | MODULE_LICENSE("GPL"); 6 | 7 | /* 8 | * This bare skeleton of a kernel module is used to check if the system has 9 | * everything necessary to build at least such simple kernel modules. 10 | */ 11 | static int __init 12 | my_init(void) 13 | { 14 | printk(KERN_DEBUG "Initializing test module\n"); 15 | return 0; 16 | } 17 | 18 | static void __exit 19 | my_exit(void) 20 | { 21 | printk(KERN_DEBUG "Cleaning up test module\n"); 22 | } 23 | 24 | module_init(my_init); 25 | module_exit(my_exit); 26 | -------------------------------------------------------------------------------- /cmake/modules/FindKbuild.cmake: -------------------------------------------------------------------------------- 1 | # The following variables are set here: 2 | # 3 | # KBUILD_VERSION_STRING - `uname -r` (${CMAKE_SYSTEM_VERSION}) 4 | # KBUILD_BUILD_DIR - directory, where the modules are built 5 | # (often /lib/modules/${KBUILD_VERSION_STRING}/build) 6 | # KBUILD_INCLUDE_DIR - not used 7 | # KBUILD_FOUND - TRUE if everything is correct, FALSE otherwise 8 | 9 | if (NOT KBUILD_VERSION_STRING) 10 | set(KBUILD_VERSION_STRING ${CMAKE_SYSTEM_VERSION} CACHE STRING 11 | "Kernel version for which KEDR is built." 12 | ) 13 | endif (NOT KBUILD_VERSION_STRING) 14 | 15 | if (NOT KBUILD_BUILD_DIR) 16 | set(KBUILD_BUILD_DIR "/lib/modules/${KBUILD_VERSION_STRING}/build") 17 | endif (NOT KBUILD_BUILD_DIR) 18 | 19 | set(KBUILD_INCLUDE_DIRS "NOT USED") 20 | 21 | # Note: only KBUILD_BUILD_DIR variable is really used in the project. 22 | # Other variables defined only for FindModule architecture of CMake. 23 | 24 | # Handle the QUIETLY and REQUIRED arguments and set KBUILD_FOUND to TRUE if 25 | # all listed variables are TRUE 26 | 27 | include(FindPackageHandleStandardArgs) 28 | find_package_handle_standard_args(Kbuild DEFAULT_MSG KBUILD_BUILD_DIR) 29 | -------------------------------------------------------------------------------- /cmake/modules/cmake_useful.cmake: -------------------------------------------------------------------------------- 1 | #Create rule for obtain one file by copying another one 2 | function(rule_copy_file target_file source_file) 3 | add_custom_command(OUTPUT ${target_file} 4 | COMMAND cp -p ${source_file} ${target_file} 5 | DEPENDS ${source_file} 6 | ) 7 | endfunction(rule_copy_file target_file source_file) 8 | 9 | #Create rule for obtain file in binary tree by copiing it from source tree 10 | function(rule_copy_source rel_source_file) 11 | rule_copy_file(${CMAKE_CURRENT_BINARY_DIR}/${rel_source_file} ${CMAKE_CURRENT_SOURCE_DIR}/${rel_source_file}) 12 | endfunction(rule_copy_source rel_source_file) 13 | 14 | # to_abs_path(output_var path [...]) 15 | # 16 | # Convert relative path of file to absolute path: 17 | # use path in source tree, if file already exist there. 18 | # otherwise use path in binary tree. 19 | # If initial path already absolute, return it. 20 | function(to_abs_path output_var) 21 | set(result) 22 | foreach(path ${ARGN}) 23 | string(REGEX MATCH "^/" _is_abs_path ${path}) 24 | if(_is_abs_path) 25 | list(APPEND result ${path}) 26 | else(_is_abs_path) 27 | file(GLOB to_abs_path_file 28 | "${CMAKE_CURRENT_SOURCE_DIR}/${path}" 29 | ) 30 | if(NOT to_abs_path_file) 31 | set (to_abs_path_file "${CMAKE_CURRENT_BINARY_DIR}/${path}") 32 | endif(NOT to_abs_path_file) 33 | list(APPEND result ${to_abs_path_file}) 34 | endif(_is_abs_path) 35 | endforeach(path ${ARGN}) 36 | set("${output_var}" ${result} PARENT_SCOPE) 37 | endfunction(to_abs_path output_var path) 38 | 39 | #is_path_inside_dir(output_var dir path) 40 | # 41 | # Set output_var to true if path is absolute path inside given directory. 42 | # (!) path should be absolute. 43 | macro(is_path_inside_dir output_var dir path) 44 | file(RELATIVE_PATH _rel_path ${dir} ${path}) 45 | string(REGEX MATCH "^\\.\\." _is_not_inside_dir ${_rel_path}) 46 | if(_is_not_inside_dir) 47 | set(${output_var} "FALSE") 48 | else(_is_not_inside_dir) 49 | set(${output_var} "TRUE") 50 | endif(_is_not_inside_dir) 51 | endmacro(is_path_inside_dir output_var dir path) 52 | ######################################################################## 53 | 54 | function(check_libelf_devel) 55 | # libelf and its development files are required for trace processing 56 | # as well as for the tests 57 | set(checking_message "Checking for libelf development files") 58 | message(STATUS "${checking_message}") 59 | 60 | if(libelf_headers) 61 | set(checking_message "${checking_message} [cached] - done") 62 | else(libelf_headers) 63 | try_compile(libelf_compile_result # Result variable 64 | "${CMAKE_BINARY_DIR}/check_libelf_devel/libelf_check" # Binary dir 65 | "${CMAKE_SOURCE_DIR}/cmake/other_sources/libelf_check.c" # Source file 66 | CMAKE_FLAGS "-DLINK_LIBRARIES:string=elf" 67 | OUTPUT_VARIABLE libelf_compile_out) 68 | if(NOT libelf_compile_result) 69 | message(STATUS "${checking_message} - not found") 70 | message(FATAL_ERROR 71 | "Unable to find the development files for libelf library.") 72 | endif(NOT libelf_compile_result) 73 | 74 | set(libelf_headers "FOUND" CACHE INTERNAL "Whether libelf headers are installed") 75 | set(checking_message "${checking_message} - done") 76 | endif(libelf_headers) 77 | 78 | message(STATUS "${checking_message}") 79 | endfunction(check_libelf_devel) 80 | 81 | function(check_libdw_devel) 82 | # libdw and its development files are required for trace processing. 83 | set(checking_message "Checking for libdw development files") 84 | message(STATUS "${checking_message}") 85 | 86 | if(libdw_headers) 87 | set(checking_message "${checking_message} [cached] - done") 88 | else(libdw_headers) 89 | try_compile(libdw_compile_result # Result variable 90 | "${CMAKE_BINARY_DIR}/check_libdw_devel/libdw_check" # Binary dir 91 | "${CMAKE_SOURCE_DIR}/cmake/other_sources/libdw_check.c" # Source file 92 | CMAKE_FLAGS "-DLINK_LIBRARIES:string=elf;dw" 93 | OUTPUT_VARIABLE libdw_compile_out) 94 | if(NOT libdw_compile_result) 95 | message(STATUS "${checking_message} - not found") 96 | message(FATAL_ERROR 97 | "Unable to find the development files for libdw library.") 98 | endif(NOT libdw_compile_result) 99 | 100 | set(libdw_headers "FOUND" CACHE INTERNAL "Whether libdw headers are installed") 101 | set(checking_message "${checking_message} - done") 102 | endif(libdw_headers) 103 | 104 | message(STATUS "${checking_message}") 105 | endfunction(check_libdw_devel) 106 | 107 | # dwfl_report_elf() may have different parameters depending on the version 108 | # of libdw/libdwfl. See commit 904aec2c2f62b729a536c2259274fdd440b0d923 109 | # "Add parameter add_p_vaddr to dwfl_report_elf" in elfutils repository 110 | # for example. 111 | # This function sets RH_DWFL_REPORT_ELF_ADD_P_VADDR if dwfl_report_elf() 112 | # has add_p_vaddr parameter. 113 | function(check_dwfl_report_elf) 114 | set(checking_message "Checking the signature of dwfl_report_elf()") 115 | message(STATUS "${checking_message}") 116 | 117 | if(DEFINED dwfl_report_elf_add_p_vaddr_checked) 118 | message(STATUS "${checking_message} [cached] - done") 119 | else() 120 | 121 | try_compile(dwfl_report_elf_result # Result variable 122 | "${CMAKE_BINARY_DIR}/check_dwfl_report_elf/main" # Binary dir 123 | "${CMAKE_SOURCE_DIR}/cmake/other_sources/dwfl_report_elf_check.c" # Source file 124 | CMAKE_FLAGS "-DLINK_LIBRARIES:string=elf;dw" 125 | OUTPUT_VARIABLE compile_out) 126 | 127 | if (dwfl_report_elf_result) 128 | set(RH_DWFL_REPORT_ELF_ADD_P_VADDR "1" 129 | CACHE INTERNAL 130 | "dwfl_report_elf() has add_p_vaddr parameter.") 131 | endif() 132 | 133 | set(dwfl_report_elf_add_p_vaddr_checked "YES" CACHE INTERNAL 134 | "dwfl_report_elf() was checked.") 135 | 136 | message(STATUS "${checking_message} - done") 137 | endif() 138 | endfunction(check_dwfl_report_elf) 139 | 140 | ######################################################################## 141 | # Test-related macros 142 | ######################################################################## 143 | 144 | # When we are building KEDR for another system (cross-build), testing is 145 | # disabled. This is because the tests need the build tree. 146 | # In the future, the tests could be prepared that need only the installed 147 | # components of KEDR. It could be a separate test suite. 148 | 149 | # This macro enables testing support and performs other initialization tasks. 150 | # It should be used in the top-level CMakeLists.txt file before 151 | # add_subdirectory () calls. 152 | macro (rh_test_init) 153 | enable_testing () 154 | add_custom_target (check 155 | COMMAND ${CMAKE_CTEST_COMMAND} 156 | ) 157 | add_custom_target (build_tests) 158 | add_dependencies (check build_tests) 159 | endmacro (rh_test_init) 160 | 161 | # Use this macro to specify an additional target to be built before the tests 162 | # are executed. 163 | function (rh_test_add_target target_name) 164 | set_target_properties (${target_name} 165 | PROPERTIES EXCLUDE_FROM_ALL true 166 | ) 167 | add_dependencies (build_tests ${target_name}) 168 | endfunction (rh_test_add_target target_name) 169 | 170 | # This function adds a test script (a Bash script, actually) to the set of 171 | # tests for the package. The script may reside in current source or binary 172 | # directory (the source directory is searched first). 173 | function (rh_test_add_script test_name script_file) 174 | to_abs_path (TEST_SCRIPT_FILE ${script_file}) 175 | 176 | add_test (${test_name} 177 | /bin/bash ${TEST_SCRIPT_FILE} ${ARGN} 178 | ) 179 | endfunction (rh_test_add_script) 180 | 181 | function (rh_test_add test_name app_file) 182 | to_abs_path (TEST_APP_FILE ${app_file}) 183 | 184 | add_test (${test_name} ${TEST_APP_FILE} ${ARGN}) 185 | endfunction (rh_test_add) 186 | 187 | # Use this macro instead of add_subdirectory() for the subtrees related to 188 | # testing of the package. 189 | function (rh_test_add_subdirectory subdir) 190 | add_subdirectory(${subdir}) 191 | endfunction (rh_test_add_subdirectory subdir) 192 | ######################################################################## 193 | -------------------------------------------------------------------------------- /cmake/modules/kbuild_system.cmake: -------------------------------------------------------------------------------- 1 | include(cmake_useful) 2 | 3 | # This environment variable can be used in Makefiles 4 | set (ENV{KERNELDIR} "${KBUILD_BUILD_DIR}") 5 | 6 | # Location of this CMake module 7 | set(kbuild_this_module_dir "${CMAKE_SOURCE_DIR}/cmake/modules") 8 | 9 | # Symvers files to be processed for building the kernel module 10 | set(kbuild_symbol_files) 11 | 12 | # Names of the targets the built kernel module should depend on. 13 | # Usually, these are the targets used for building some other kernel 14 | # modules which symvers files are used via kbuild_use_symbols(). 15 | set(kbuild_dependencies_modules) 16 | 17 | # #include directories for building kernel modules 18 | set(kbuild_include_dirs) 19 | 20 | # Additional compiler flags for the module 21 | set(kbuild_cflags) 22 | 23 | # Helper for the building kernel module. 24 | # 25 | # Test whether given source file is inside source tree. If it is so, 26 | # create rule for copy file to same relative location in binary tree. 27 | # Output variable will contain file's absolute path in binary tree. 28 | 29 | function(copy_source_to_binary_tree source new_source_var) 30 | is_path_inside_dir(is_in_source ${CMAKE_SOURCE_DIR} "${source}") 31 | is_path_inside_dir(is_in_binary ${CMAKE_BINARY_DIR} "${source}") 32 | if(is_in_source AND NOT is_in_binary) 33 | #special process c-sources in source tree 34 | file(RELATIVE_PATH source_rel "${CMAKE_SOURCE_DIR}" "${source}") 35 | set(new_source "${CMAKE_BINARY_DIR}/${source_rel}") 36 | #add rule for create duplicate.. 37 | rule_copy_file("${new_source}" "${source}") 38 | else(is_in_source AND NOT is_in_binary) 39 | set(new_source "${source}") 40 | endif(is_in_source AND NOT is_in_binary) 41 | set(${new_source_var} "${new_source}" PARENT_SCOPE) 42 | endfunction(copy_source_to_binary_tree source new_source_var) 43 | 44 | # Extract components of the file. 45 | # 46 | # file_components(filepath [ RESULT_VAR]) 47 | # 48 | # For each , set RESULT_VAR to corresponded component of filepath. 49 | # 50 | # may be: 51 | # - DIR: everything until last '/' (including it) if it exists. 52 | # - NOTDIR: everything except directory(without '/'). 53 | # - SUFFIX: last suffix(including '.'), if it exists. 54 | # - BASENAME: everything without suffix (without '.'). 55 | 56 | 57 | function(file_components filepath) 58 | set(state "None") 59 | foreach(arg ${ARGN}) 60 | if(arg STREQUAL "DIR") 61 | set(state "DIR") 62 | elseif(arg STREQUAL "NOTDIR") 63 | set(state "NOTDIR") 64 | elseif(arg STREQUAL "SUFFIX") 65 | set(state "SUFFIX") 66 | elseif(arg STREQUAL "BASENAME") 67 | set(state "BASENAME") 68 | elseif(state STREQUAL "DIR") 69 | STRING(REGEX REPLACE "[^/]+$" "" dir ${filepath}) 70 | set(${arg} "${dir}" PARENT_SCOPE) 71 | elseif(state STREQUAL "NOTDIR") 72 | STRING(REGEX REPLACE ".*/" "" notdir ${filepath}) 73 | set(${arg} "${notdir}" PARENT_SCOPE) 74 | elseif(state STREQUAL "SUFFIX") 75 | STRING(REGEX MATCH "\\.[^/\\.]+$" suffix ${filepath}) 76 | set(${arg} "${suffix}" PARENT_SCOPE) 77 | elseif(state STREQUAL "BASENAME") 78 | STRING(REGEX REPLACE "\\.[^/\\.]+$" "" basename ${filepath}) 79 | set(${arg} "${basename}" PARENT_SCOPE) 80 | else(arg STREQUAL "DIR") 81 | message(FATAL "Unexpected argument: ${arg}") 82 | endif(arg STREQUAL "DIR") 83 | endforeach(arg ${ARGN}) 84 | endfunction(file_components filepath) 85 | 86 | # kbuild_add_module(name [sources ...]) 87 | # 88 | # Build a kernel module from sources_files, similar to add_executable(). 89 | # 90 | # The source files are divided into two categories: 91 | # - object sources; 92 | # - other sources. 93 | # 94 | # Object sources are the sources that can be used when building the kernel 95 | # module externally. 96 | # The following types of object sources are currently supported: 97 | # .c: a file with the code in C language; 98 | # .S: a file with the code in assembly language; 99 | # .o_shipped: shipped file in binary format, 100 | # does not require additional preprocessing. 101 | # 102 | # Other sources are treated only as the prerequisites in the build process. 103 | # 104 | # Only one call to kbuild_add_module() or kbuild_add_objects() 105 | # is allowed in the CMakeLists.txt. 106 | # 107 | # In case when 'sources' are omitted, the module will be built from 108 | # "${name}.c" source file. 109 | 110 | function(kbuild_add_module name) 111 | set(symvers_file ${CMAKE_CURRENT_BINARY_DIR}/Module.symvers) 112 | # Global target 113 | add_custom_target(${name} ALL 114 | DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${name}.ko ${symvers_file}) 115 | if(kbuild_dependencies_modules) 116 | add_dependencies(${name} ${kbuild_dependencies_modules}) 117 | endif(kbuild_dependencies_modules) 118 | # Sources 119 | if(ARGN) 120 | set(sources ${ARGN}) 121 | else(ARGN) 122 | set(sources "${CMAKE_CURRENT_BINARY_DIR}/${name}.c") 123 | endif(ARGN) 124 | # Sources with absolute paths 125 | to_abs_path(sources_abs ${sources}) 126 | # The list of files the building of the module depends on 127 | set(depend_files) 128 | # Sources of "c" type, but without extension 129 | # (for clean files, 130 | # for out-of-source builds do not create files in source tree) 131 | set(c_sources_noext_abs) 132 | # The sources with the code in assembly 133 | set(asm_sources_noext_abs) 134 | # Sources of "o_shipped" type, but without extension 135 | set(shipped_sources_noext_abs) 136 | # Sort the sources and move them into binary tree if needed 137 | foreach(source_abs ${sources_abs}) 138 | file_components(${source_abs} SUFFIX ext) 139 | if(ext STREQUAL ".c" OR ext STREQUAL ".S" OR ext STREQUAL ".o_shipped") 140 | # Real sources 141 | # Move source into binary tree, if needed 142 | copy_source_to_binary_tree("${source_abs}" source_abs) 143 | if(ext STREQUAL ".c") 144 | # c-source 145 | file_components("${source_abs}" BASENAME c_source_noext_abs) 146 | list(APPEND c_sources_noext_abs ${c_source_noext_abs}) 147 | elseif(ext STREQUAL ".S") 148 | # asm source 149 | file_components("${source_abs}" BASENAME asm_source_noext_abs) 150 | list(APPEND asm_sources_noext_abs ${asm_source_noext_abs}) 151 | elseif(ext STREQUAL ".o_shipped") 152 | # shipped-source 153 | file_components("${source_abs}" BASENAME shipped_source_noext_abs) 154 | list(APPEND shipped_sources_noext_abs ${shipped_source_noext_abs}) 155 | endif(ext STREQUAL ".c") 156 | endif(ext STREQUAL ".c" OR ext STREQUAL ".S" OR ext STREQUAL ".o_shipped") 157 | # In any case, add file to depend list 158 | list(APPEND depend_files ${source_abs}) 159 | endforeach(source_abs ${sources_abs}) 160 | # Form the relative paths to the sources 161 | #(for $(module)-y :=) 162 | set(c_sources_noext_rel) 163 | foreach(c_source_noext_abs ${c_sources_noext_abs}) 164 | file(RELATIVE_PATH c_source_noext_rel 165 | ${CMAKE_CURRENT_BINARY_DIR} ${c_source_noext_abs}) 166 | list(APPEND c_sources_noext_rel ${c_source_noext_rel}) 167 | endforeach(c_source_noext_abs ${c_sources_noext_abs}) 168 | 169 | set(asm_sources_noext_rel) 170 | foreach(asm_source_noext_abs ${asm_sources_noext_abs}) 171 | file(RELATIVE_PATH asm_source_noext_rel 172 | ${CMAKE_CURRENT_BINARY_DIR} ${asm_source_noext_abs}) 173 | list(APPEND asm_sources_noext_rel ${asm_source_noext_rel}) 174 | endforeach(asm_source_noext_abs ${asm_sources_noext_abs}) 175 | 176 | set(shipped_sources_noext_rel) 177 | foreach(shipped_source_noext_abs ${shipped_sources_noext_abs}) 178 | file(RELATIVE_PATH shipped_source_noext_rel 179 | ${CMAKE_CURRENT_BINARY_DIR} ${shipped_source_noext_abs}) 180 | list(APPEND shipped_sources_noext_rel ${shipped_source_noext_rel}) 181 | endforeach(shipped_source_noext_abs ${shipped_sources_noext_abs}) 182 | 183 | # Join all sources to determine type of the build (simple or not) 184 | set(obj_sources_noext_rel 185 | ${c_sources_noext_rel} 186 | ${asm_sources_noext_rel} 187 | ${shipped_sources_noext_rel}) 188 | 189 | if(NOT obj_sources_noext_rel) 190 | message(FATAL_ERROR "The list of object files for module \"${name}\" is empty.") 191 | endif(NOT obj_sources_noext_rel) 192 | # Detect if the build is simple, i.e the source object name is the same as 193 | # the module name 194 | if(obj_sources_noext_rel STREQUAL ${name}) 195 | set(is_build_simple "TRUE") 196 | else(obj_sources_noext_rel STREQUAL ${name}) 197 | # Detect if some of source object names are the same as the module name 198 | # and there are the source object files except these. 199 | # This situation is incorrect for kbuild system. 200 | list(FIND obj_sources_noext_rel ${name} is_objects_contain_name) 201 | if(is_objects_contain_name GREATER -1) 202 | message(FATAL_ERROR "Module should be built " 203 | "either from a single object with same name, " 204 | "or from the objects with names different from the name of the module") 205 | endif(is_objects_contain_name GREATER -1) 206 | set(is_build_simple "FALSE") 207 | endif(obj_sources_noext_rel STREQUAL ${name}) 208 | 209 | # Create kbuild file 210 | set(kbuild_file "${CMAKE_CURRENT_BINARY_DIR}/Kbuild") 211 | FILE(WRITE "${kbuild_file}") 212 | 213 | # Build kbuild file - compiler flags 214 | if(kbuild_cflags OR kbuild_include_dirs) 215 | set(cflags_string) 216 | # Common flags 217 | if(kbuild_cflags) 218 | foreach(cflag ${kbuild_cflags}) 219 | set(cflags_string "${cflags_string} ${cflag}") 220 | endforeach(cflag ${kbuild_cflags}) 221 | endif(kbuild_cflags) 222 | 223 | # Include directories 224 | if(kbuild_include_dirs) 225 | foreach(dir ${kbuild_include_dirs}) 226 | set(cflags_string "${cflags_string} -I${dir}") 227 | endforeach(dir ${kmodule_include_dirs}) 228 | endif(kbuild_include_dirs) 229 | FILE(APPEND "${kbuild_file}" "ccflags-y := ${cflags_string}\n") 230 | endif(kbuild_cflags OR kbuild_include_dirs) 231 | 232 | # Build kbuild file - module string 233 | FILE(APPEND "${kbuild_file}" "obj-m := ${name}.o\n") 234 | 235 | # Build kbuild file - object sources string 236 | if(NOT is_build_simple) 237 | set(obj_src_string) 238 | foreach(obj ${c_sources_noext_rel} ${asm_sources_noext_rel} ${shipped_sources_noext_rel}) 239 | set(obj_src_string "${obj_src_string} ${obj}.o") 240 | endforeach(obj ${c_sources_noext_rel} ${asm_sources_noext_rel} ${shipped_sources_noext_rel}) 241 | FILE(APPEND "${kbuild_file}" "${name}-y := ${obj_src_string}\n") 242 | endif(NOT is_build_simple) 243 | 244 | # Additional command and dependencies if module use 245 | # symbols from other modules 246 | if(kbuild_symbol_files) 247 | set(symvers_command COMMAND cat ${kbuild_symbol_files} >> ${symvers_file}) 248 | list(APPEND ${depend_files} ${kbuild_symbol_files}) 249 | endif(kbuild_symbol_files) 250 | # Create .cmd files for 'shipped' sources - gcc does not create them 251 | # automatically for some reason 252 | if(shipped_sources_noext_abs) 253 | set(cmd_create_command) 254 | foreach(shipped_source_noext_abs ${shipped_source_noext_abs}) 255 | # Parse filename to dir and name (without extension) 256 | file_components(${shipped_source_noext_abs} DIR _dir NOTDIR _name) 257 | list(APPEND cmd_create_command 258 | COMMAND printf "cmd_%s.o := cp -p %s.o_shipped %s.o\\n" 259 | "${shipped_source_noext_abs}" 260 | "${shipped_source_noext_abs}" 261 | "${shipped_source_noext_abs}" 262 | > "${_dir}.${_name}.o.cmd") 263 | endforeach(shipped_source_noext_abs ${shipped_source_noext_abs}) 264 | endif(shipped_sources_noext_abs) 265 | 266 | # The rule to create module 267 | add_custom_command( 268 | OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${name}.ko ${symvers_file} 269 | ${cmd_create_command} 270 | ${symvers_command} 271 | COMMAND $(MAKE) -C ${KBUILD_BUILD_DIR} 272 | M=${CMAKE_CURRENT_BINARY_DIR} modules 273 | DEPENDS ${depend_files} 274 | ) 275 | # The rule to clean files 276 | _kbuild_module_clean_files(${name} 277 | C_SOURCE ${c_sources_noext_abs} 278 | ASM_SOURCE ${asm_sources_noext_abs} 279 | SHIPPED_SOURCE ${shipped_sources_noext_abs}) 280 | endfunction(kbuild_add_module name) 281 | 282 | # kbuild_include_directories(dir1 .. dirn) 283 | macro(kbuild_include_directories) 284 | list(APPEND kbuild_include_dirs ${ARGN}) 285 | endmacro(kbuild_include_directories) 286 | 287 | # kbuild_use_symbols(symvers_file1 .. symvers_filen) 288 | macro(kbuild_use_symbols) 289 | list(APPEND kbuild_symbol_files ${ARGN}) 290 | endmacro(kbuild_use_symbols) 291 | 292 | # kbuild_add_dependencies(kmodule1 .. kmoduleN) 293 | macro(kbuild_add_dependencies) 294 | list(APPEND kbuild_dependencies_modules ${ARGN}) 295 | endmacro(kbuild_add_dependencies) 296 | 297 | 298 | # kbuild_add_definitions (flag1 ... flagN) 299 | # Specify additional compiler flags for the module. 300 | macro(kbuild_add_definitions) 301 | list(APPEND kbuild_cflags ${ARGN}) 302 | endmacro(kbuild_add_definitions) 303 | 304 | # Internal functions 305 | 306 | # _kbuild_module_clean_files(module_name 307 | # [C_SOURCE c_source_noext_abs ...] 308 | # [ASM_SOURCE asm_source_noext_abs ...] 309 | # [SHIPPED_SOURCE shipped_source_noext_abs ...]) 310 | # 311 | # Tell CMake that intermediate files, created by kbuild system, 312 | # should be cleaned with 'make clean'. 313 | 314 | function(_kbuild_module_clean_files module_name) 315 | # List common files (names only) for cleaning 316 | set(common_files_names 317 | "built-in.o" 318 | ".built-in.o.cmd" 319 | "Module.markers") 320 | # List module name-depending files (extensions only) for cleaning 321 | set(name_files_ext 322 | ".o" 323 | ".mod.c" 324 | ".mod.o") 325 | # Same but for the files with names starting with a dot ('.'). 326 | set(name_files_dot_ext 327 | ".ko.cmd" 328 | ".mod.o.cmd" 329 | ".o.cmd") 330 | # List source name-depending files (extensions only) for cleaning 331 | set(source_name_files_ext 332 | ".o") 333 | # Same but for the files with names starting with a dot ('.') 334 | set(source_name_files_dot_ext 335 | ".o.cmd" 336 | ".o.d") 337 | 338 | # Now collect all sort of files into list 339 | set(files_list) 340 | 341 | foreach(name ${common_files_names}) 342 | list(APPEND files_list "${CMAKE_CURRENT_BINARY_DIR}/${name}") 343 | endforeach(name ${common_files_names}) 344 | 345 | foreach(ext ${name_files_ext}) 346 | list(APPEND files_list 347 | "${CMAKE_CURRENT_BINARY_DIR}/${module_name}${ext}") 348 | endforeach(ext ${name_files_ext}) 349 | 350 | foreach(ext ${name_files_dot_ext}) 351 | list(APPEND files_list 352 | "${CMAKE_CURRENT_BINARY_DIR}/.${module_name}${ext}") 353 | endforeach(ext ${name_files_ext}) 354 | 355 | # State variable for parse argument list 356 | set(state "None") 357 | 358 | foreach(arg ${ARGN}) 359 | if(arg STREQUAL "C_SOURCE") 360 | set(state "C") 361 | elseif(arg STREQUAL "ASM_SOURCE") 362 | set(state "ASM") 363 | elseif(arg STREQUAL "SHIPPED_SOURCE") 364 | set(state "SHIPPED") 365 | elseif(state STREQUAL "C" OR state STREQUAL "ASM" OR state STREQUAL "SHIPPED") 366 | # All the types of sources are processed in a similar way 367 | # Parse the filename and extract dir and name (without extension) 368 | file_components(${arg} DIR dir NOTDIR name) 369 | foreach(ext ${source_name_files_ext}) 370 | list(APPEND files_list "${dir}${name}${ext}") 371 | endforeach(ext ${source_name_files_ext}) 372 | foreach(ext ${source_name_files_dot_ext}) 373 | list(APPEND files_list "${dir}.${name}${ext}") 374 | endforeach(ext ${source_name_files_ext}) 375 | else(arg STREQUAL "C_SOURCE") 376 | message(FATAL "Unexpected argument: ${arg}") 377 | endif(arg STREQUAL "C_SOURCE") 378 | endforeach(arg ${ARGN}) 379 | # Tell CMake that given files should be cleaned. 380 | set_directory_properties(PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES "${files_list}") 381 | endfunction(_kbuild_module_clean_files module_name) 382 | -------------------------------------------------------------------------------- /cmake/modules/kbuild_system_files/Kbuild.in: -------------------------------------------------------------------------------- 1 | ${cflags_string} 2 | ${obj_string} 3 | ${obj_src_string} -------------------------------------------------------------------------------- /cmake/modules/kbuild_system_files/Kbuild_object.in: -------------------------------------------------------------------------------- 1 | ${cflags_string} 2 | ${objects_string} -------------------------------------------------------------------------------- /cmake/modules/kmodule.cmake: -------------------------------------------------------------------------------- 1 | set(kmodule_this_module_dir "${CMAKE_SOURCE_DIR}/cmake/modules/") 2 | set(kmodule_test_sources_dir "${CMAKE_SOURCE_DIR}/cmake/kmodule_sources") 3 | 4 | set(kmodule_function_map_file "") 5 | 6 | # kmodule_try_compile(RESULT_VAR bindir srcfile 7 | # [COMPILE_DEFINITIONS flags] 8 | # [OUTPUT_VARIABLE var] 9 | # [COPY_FILE filename]) 10 | 11 | # Similar to try_compile in the simplified form, but compile srcfile as 12 | # kernel module, instead of user space program. 13 | 14 | function(kmodule_try_compile RESULT_VAR bindir srcfile) 15 | to_abs_path(src_abs_path "${srcfile}") 16 | # State for parse argument list 17 | set(state "None") 18 | foreach(arg ${ARGN}) 19 | if(arg STREQUAL "COMPILE_DEFINITIONS") 20 | set(state "COMPILE_DEFINITIONS") 21 | elseif(arg STREQUAL "OUTPUT_VARIABLE") 22 | set(state "OUTPUT_VARIABLE") 23 | elseif(arg STREQUAL "COPY_FILE") 24 | set(state "COPY_FILE") 25 | elseif(state STREQUAL "COMPILE_DEFINITIONS") 26 | set(kmodule_cflags "${kmodule_cflags} ${arg}") 27 | elseif(state STREQUAL "OUTPUT_VARIABLE") 28 | set(output_variable "${arg}") 29 | set(state "None") 30 | elseif(state STREQUAL "COPY_FILE") 31 | set(copy_file_variable "${arg}") 32 | set(state "None") 33 | else(arg STREQUAL "COMPILE_DEFINITIONS") 34 | message(FATAL_ERROR 35 | "Unexpected parameter passed to kmodule_try_compile: '${arg}'." 36 | ) 37 | endif(arg STREQUAL "COMPILE_DEFINITIONS") 38 | endforeach(arg ${ARGN}) 39 | set(cmake_params 40 | "-DSRC_FILE:path=${src_abs_path}" 41 | "-DKERNELDIR=${KBUILD_BUILD_DIR}" 42 | ) 43 | if(DEFINED kmodule_cflags) 44 | list(APPEND cmake_params "-Dkmodule_flags=${kmodule_cflags}") 45 | endif(DEFINED kmodule_cflags) 46 | if(copy_file_variable) 47 | list(APPEND cmake_params "-DCOPY_FILE=${copy_file_variable}") 48 | endif(copy_file_variable) 49 | 50 | if(DEFINED output_variable) 51 | try_compile(result_tmp "${bindir}" 52 | "${kmodule_this_module_dir}/kmodule_files" 53 | "kmodule_try_compile_target" 54 | CMAKE_FLAGS ${cmake_params} 55 | OUTPUT_VARIABLE output_tmp) 56 | set("${output_variable}" "${output_tmp}" PARENT_SCOPE) 57 | else(DEFINED output_variable) 58 | try_compile(result_tmp "${bindir}" 59 | "${kmodule_this_module_dir}/kmodule_files" 60 | "kmodule_try_compile_target" 61 | CMAKE_FLAGS ${cmake_params}) 62 | endif(DEFINED output_variable) 63 | set("${RESULT_VAR}" "${result_tmp}" PARENT_SCOPE) 64 | endfunction(kmodule_try_compile RESULT_VAR bindir srcfile) 65 | 66 | ############################################################################ 67 | # Utility macros to check for particular features. If the particular feature 68 | # is supported, the macros will set the corresponding variable to TRUE, 69 | # otherwise - to FALSE (the name of variable is mentioned in the comments 70 | # for the macro). 71 | ############################################################################ 72 | 73 | # Check if the system has everything necessary to build at least simple 74 | # kernel modules. 75 | # The macro sets variable 'MODULE_BUILD_SUPPORTED'. 76 | macro(check_module_build) 77 | set(check_module_build_message 78 | "Checking if kernel modules can be built on this system" 79 | ) 80 | message(STATUS "${check_module_build_message}") 81 | if (DEFINED MODULE_BUILD_SUPPORTED) 82 | set(check_module_build_message 83 | "${check_module_build_message} [cached] - ${MODULE_BUILD_SUPPORTED}" 84 | ) 85 | else (DEFINED MODULE_BUILD_SUPPORTED) 86 | kmodule_try_compile(module_build_supported_impl 87 | "${CMAKE_BINARY_DIR}/check_module_build" 88 | "${kmodule_test_sources_dir}/check_module_build/module.c" 89 | ) 90 | if (module_build_supported_impl) 91 | set(MODULE_BUILD_SUPPORTED "yes" CACHE INTERNAL 92 | "Can kernel modules be built on this system?" 93 | ) 94 | else (module_build_supported_impl) 95 | set(MODULE_BUILD_SUPPORTED "no") 96 | message(FATAL_ERROR 97 | "Kernel modules cannot be built on this system" 98 | ) 99 | endif (module_build_supported_impl) 100 | 101 | set(check_module_build_message 102 | "${check_module_build_message} - ${MODULE_BUILD_SUPPORTED}" 103 | ) 104 | endif (DEFINED MODULE_BUILD_SUPPORTED) 105 | message(STATUS "${check_module_build_message}") 106 | endmacro(check_module_build) 107 | 108 | # Check if the version of the kernel is acceptable 109 | # The macro sets variable 'KERNEL_VERSION_OK'. 110 | macro(check_kernel_version kversion_major kversion_minor kversion_micro) 111 | set(check_kernel_version_string 112 | "${kversion_major}.${kversion_minor}.${kversion_micro}" 113 | ) 114 | set(check_kernel_version_message 115 | "Checking if the kernel version is ${check_kernel_version_string} or newer" 116 | ) 117 | message(STATUS "${check_kernel_version_message}") 118 | if (DEFINED KERNEL_VERSION_OK) 119 | set(check_kernel_version_message 120 | "${check_kernel_version_message} [cached] - ${KERNEL_VERSION_OK}" 121 | ) 122 | else (DEFINED KERNEL_VERSION_OK) 123 | string(REGEX MATCH "[0-9]+\\.[0-9]+\\.[0-9]+" 124 | real_kernel_version_string 125 | "${KBUILD_VERSION_STRING}" 126 | ) 127 | 128 | if (real_kernel_version_string VERSION_LESS check_kernel_version_string) 129 | set(KERNEL_VERSION_OK "no") 130 | message(FATAL_ERROR 131 | "Kernel version is ${real_kernel_version_string} but ${check_kernel_version_string} or newer is required." 132 | ) 133 | else () 134 | set(KERNEL_VERSION_OK "yes" CACHE INTERNAL 135 | "Is kernel version high enough?" 136 | ) 137 | endif () 138 | 139 | set(check_kernel_version_message 140 | "${check_kernel_version_message} - ${KERNEL_VERSION_OK}" 141 | ) 142 | endif (DEFINED KERNEL_VERSION_OK) 143 | message(STATUS "${check_kernel_version_message}") 144 | endmacro(check_kernel_version kversion_major kversion_minor kversion_micro) 145 | 146 | # Check if the required kernel parameters are set in the kernel 147 | # configuration. 148 | macro(check_kernel_config) 149 | set(check_kernel_config_message 150 | "Checking the basic configuration of the kernel" 151 | ) 152 | message(STATUS "${check_kernel_config_message}") 153 | if (DEFINED KERNEL_CONFIG_OK) 154 | message(STATUS "${check_kernel_config_message} [cached] - ok") 155 | else (DEFINED KERNEL_CONFIG_OK) 156 | kmodule_try_compile(kernel_config_impl 157 | "${CMAKE_BINARY_DIR}/check_kernel_config" 158 | "${kmodule_test_sources_dir}/check_kernel_config/module.c" 159 | ) 160 | if (kernel_config_impl) 161 | set(KERNEL_CONFIG_OK "yes" CACHE INTERNAL 162 | "Are the necessary basic kernel configuration parameters set?" 163 | ) 164 | message(STATUS "${check_kernel_config_message} - ok") 165 | else (kernel_config_impl) 166 | message(FATAL_ERROR 167 | "Some of the required configuration parameters of the kernel " 168 | "are not set. Please check the configuration file for the " 169 | "kernel.\n" 170 | "The following parameters should be set:\n" 171 | "\tCONFIG_X86_32 or CONFIG_X86_64 (system architecture)\n" 172 | "\tCONFIG_MODULES (loadable module support)\n" 173 | "\tCONFIG_MODULE_UNLOAD (module unloading support)\n" 174 | "\tCONFIG_SYSFS (sysfs support)\n" 175 | "\tCONFIG_DEBUG_FS (debugfs support)\n" 176 | "\tCONFIG_KALLSYMS (loading of kernel symbols in the kernel image)\n" 177 | ) 178 | endif (kernel_config_impl) 179 | endif (DEFINED KERNEL_CONFIG_OK) # TODO 180 | endmacro(check_kernel_config) 181 | ############################################################################ 182 | -------------------------------------------------------------------------------- /cmake/modules/kmodule_files/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Needed for all CMakeLists files, otherwise cmake will warn 2 | cmake_minimum_required (VERSION 2.6) 3 | 4 | if (NOT KERNELDIR) 5 | set(KERNELDIR /lib/modules/${KBUILD_VERSION_STRING}/build) 6 | endif (NOT KERNELDIR) 7 | 8 | set(PWD $\(shell pwd\)) 9 | 10 | project(kmodule_try_compile) 11 | # Create rule for produce file from another file via copiing 12 | function(copy_file FILENAME SOURCE) 13 | add_custom_command(OUTPUT "${FILENAME}" 14 | COMMAND cp -p "${SOURCE}" "${FILENAME}" 15 | DEPENDS "${SOURCE}") 16 | endfunction(copy_file FILENAME SOURCE) 17 | 18 | if(NOT DEFINED SRC_FILE) 19 | message(FATAL_ERROR "'SRC_FILE' should be defined, but it is not.") 20 | endif(NOT DEFINED SRC_FILE) 21 | 22 | configure_file(Kbuild.in Kbuild) 23 | # 'make all' should produce try_compile.ko file 24 | add_custom_target(kmodule_compile ALL DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/try_compile.ko") 25 | if(COPY_FILE) 26 | add_custom_target(target_copy_file ALL DEPENDS "${COPY_FILE}") 27 | copy_file("${COPY_FILE}" "${CMAKE_CURRENT_BINARY_DIR}/try_compile.ko") 28 | endif(COPY_FILE) 29 | 30 | # Rule to produce try_compile.ko from try_compile.c 31 | add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/try_compile.ko" 32 | COMMAND $(MAKE) -C ${KERNELDIR} M=${PWD} modules 33 | DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/try_compile.c") 34 | 35 | # Kbuild system cannot work with out-of-source build, 36 | # so copy source file into build dir and rename it to try_compile.c. 37 | copy_file("${CMAKE_CURRENT_BINARY_DIR}/try_compile.c" "${SRC_FILE}") 38 | -------------------------------------------------------------------------------- /cmake/modules/kmodule_files/Kbuild.in: -------------------------------------------------------------------------------- 1 | ccflags-y := @kmodule_flags@ 2 | 3 | obj-m := try_compile.o 4 | -------------------------------------------------------------------------------- /cmake/modules/path_prefixes.cmake: -------------------------------------------------------------------------------- 1 | # Declare variables for path prefixes for different types of files. 2 | # 3 | # Declare path prefixes for install variant, and for tests. 4 | # Variables for install prefixes are named RH_INSTALL_PREFIX_..., 5 | # variables for test prefixes are named RH_TEST_PREFIX. 6 | 7 | set (RH_ALL_PATH_SUFFIXES EXEC READONLY GLOBAL_CONF LIB INCLUDE 8 | TEMP_SESSION TEMP STATE CACHE VAR DOC 9 | KMODULE KSYMVERS KINCLUDE EXAMPLES TEMPLATES) 10 | 11 | # See conventions about paths of installed files 12 | # Determine type of installation 13 | string(REGEX MATCH "(^/opt|^/usr|^/$)" IS_GLOBAL_INSTALL ${CMAKE_INSTALL_PREFIX}) 14 | if(IS_GLOBAL_INSTALL) 15 | set(RH_INSTALL_TYPE "global") 16 | set(RH_INSTALL_PREFIX_VAR "/var/opt/${RH_PACKAGE_NAME}") 17 | if(CMAKE_MATCH_1 STREQUAL "/opt") 18 | message("Global installation into /opt") 19 | set(RH_INSTALL_GLOBAL_IS_OPT "opt") 20 | else(CMAKE_MATCH_1 STREQUAL "/opt") 21 | message("Global installation") 22 | endif(CMAKE_MATCH_1 STREQUAL "/opt") 23 | else(IS_GLOBAL_INSTALL) 24 | message("Local installation") 25 | set(RH_INSTALL_TYPE "local") 26 | set(RH_INSTALL_PREFIX_VAR "${CMAKE_INSTALL_PREFIX}/var") 27 | endif(IS_GLOBAL_INSTALL) 28 | 29 | # Set prefixes 30 | # 1 31 | set(RH_INSTALL_PREFIX_EXEC 32 | "${CMAKE_INSTALL_PREFIX}/bin") 33 | set(RH_INSTALL_PREFIX_EXEC_AUX 34 | "${CMAKE_INSTALL_PREFIX}/lib/${RH_PACKAGE_NAME}") 35 | # 2 36 | set(RH_INSTALL_PREFIX_READONLY 37 | "${CMAKE_INSTALL_PREFIX}/share/${RH_PACKAGE_NAME}") 38 | set(RH_INSTALL_PREFIX_MANPAGE 39 | "${CMAKE_INSTALL_PREFIX}/share/man") 40 | # 3 41 | if(RH_INSTALL_TYPE STREQUAL "global") 42 | set(RH_INSTALL_PREFIX_GLOBAL_CONF 43 | "/etc/${RH_PACKAGE_NAME}") 44 | else(RH_INSTALL_TYPE STREQUAL "global") 45 | set(RH_INSTALL_PREFIX_GLOBAL_CONF 46 | "${CMAKE_INSTALL_PREFIX}/etc/${RH_PACKAGE_NAME}") 47 | endif(RH_INSTALL_TYPE STREQUAL "global") 48 | # 4 49 | set(RH_INSTALL_PREFIX_LIB 50 | "${CMAKE_INSTALL_PREFIX}/lib") 51 | set(RH_INSTALL_PREFIX_LIB_AUX 52 | "${CMAKE_INSTALL_PREFIX}/lib/${RH_PACKAGE_NAME}") 53 | # 5 54 | set(RH_INSTALL_PREFIX_INCLUDE 55 | "${CMAKE_INSTALL_PREFIX}/include/${RH_PACKAGE_NAME}") 56 | # 6 57 | set(RH_INSTALL_PREFIX_TEMP_SESSION 58 | "/tmp/${RH_PACKAGE_NAME}") 59 | # 7 60 | if(RH_INSTALL_TYPE STREQUAL "global") 61 | set(RH_INSTALL_PREFIX_TEMP 62 | "/var/tmp/${RH_PACKAGE_NAME}") 63 | else(RH_INSTALL_TYPE STREQUAL "global") 64 | set(RH_INSTALL_PREFIX_TEMP 65 | "${CMAKE_INSTALL_PREFIX}/var/tmp/${RH_PACKAGE_NAME}") 66 | endif(RH_INSTALL_TYPE STREQUAL "global") 67 | # 8 68 | if(RH_INSTALL_TYPE STREQUAL "global") 69 | if(RH_INSTALL_GLOBAL_IS_OPT) 70 | set(RH_INSTALL_PREFIX_STATE 71 | "/var/opt/${RH_PACKAGE_NAME}/lib/${RH_PACKAGE_NAME}") 72 | else(RH_INSTALL_GLOBAL_IS_OPT) 73 | set(RH_INSTALL_PREFIX_STATE 74 | "/var/lib/${RH_PACKAGE_NAME}") 75 | endif(RH_INSTALL_GLOBAL_IS_OPT) 76 | else(RH_INSTALL_TYPE STREQUAL "global") 77 | set(RH_INSTALL_PREFIX_STATE 78 | "${CMAKE_INSTALL_PREFIX}/var/lib/${RH_PACKAGE_NAME}") 79 | endif(RH_INSTALL_TYPE STREQUAL "global") 80 | # 9 81 | if(RH_INSTALL_TYPE STREQUAL "global") 82 | if(RH_INSTALL_GLOBAL_IS_OPT) 83 | set(RH_INSTALL_PREFIX_CACHE 84 | "/var/opt/${RH_PACKAGE_NAME}/cache/${RH_PACKAGE_NAME}") 85 | else(RH_INSTALL_GLOBAL_IS_OPT) 86 | set(RH_INSTALL_PREFIX_CACHE 87 | "/var/cache/${RH_PACKAGE_NAME}") 88 | endif(RH_INSTALL_GLOBAL_IS_OPT) 89 | else(RH_INSTALL_TYPE STREQUAL "global") 90 | set(RH_INSTALL_PREFIX_CACHE 91 | "${CMAKE_INSTALL_PREFIX}/var/cache/${RH_PACKAGE_NAME}") 92 | endif(RH_INSTALL_TYPE STREQUAL "global") 93 | # 10 94 | if(RH_INSTALL_TYPE STREQUAL "global") 95 | if(RH_INSTALL_GLOBAL_IS_OPT) 96 | set(RH_INSTALL_PREFIX_VAR 97 | "/var/opt/${RH_PACKAGE_NAME}") 98 | else(RH_INSTALL_GLOBAL_IS_OPT) 99 | set(RH_INSTALL_PREFIX_VAR 100 | "/var/opt/${RH_PACKAGE_NAME}") 101 | # Another variant 102 | # set(RH_INSTALL_PREFIX_VAR 103 | # "/var/${RH_PACKAGE_NAME}") 104 | endif(RH_INSTALL_GLOBAL_IS_OPT) 105 | else(RH_INSTALL_TYPE STREQUAL "global") 106 | set(RH_INSTALL_PREFIX_VAR 107 | "${CMAKE_INSTALL_PREFIX}/var/${RH_PACKAGE_NAME}") 108 | endif(RH_INSTALL_TYPE STREQUAL "global") 109 | # 11 110 | set(RH_INSTALL_PREFIX_DOC 111 | "${CMAKE_INSTALL_PREFIX}/share/doc/${RH_PACKAGE_NAME}") 112 | 113 | # Set derivative prefixes 114 | 115 | # additional, 1 116 | set(RH_INSTALL_PREFIX_KMODULE "${RH_INSTALL_PREFIX_LIB}/modules/${KBUILD_VERSION_STRING}/misc") 117 | # Another variant 118 | #"${RH_INSTALL_PREFIX_LIB}/modules/${KBUILD_VERSION_STRING}/extra") 119 | # additional, 2 120 | set(RH_INSTALL_PREFIX_KSYMVERS "${CMAKE_INSTALL_PREFIX}/lib/modules/${KBUILD_VERSION_STRING}/symvers") 121 | # additional, 3 122 | set(RH_INSTALL_PREFIX_KINCLUDE 123 | "${RH_INSTALL_PREFIX_INCLUDE}") 124 | # additional, 4 125 | set(RH_INSTALL_PREFIX_EXAMPLES 126 | "${RH_INSTALL_PREFIX_READONLY}/examples") 127 | # additional, 5 128 | set(RH_INSTALL_PREFIX_TEMPLATES 129 | "${RH_INSTALL_PREFIX_READONLY}/templates") 130 | 131 | # Default directory for configuration files 132 | set(RH_DEFAULT_CONFIG_DIR "${RH_INSTALL_PREFIX_VAR}/configs") 133 | 134 | ######################################################################## 135 | # Path prefixes for tests 136 | 137 | set(RH_TEST_COMMON_PREFIX "/var/tmp/${RH_PACKAGE_NAME}/test") 138 | 139 | foreach(var_suffix ${RH_ALL_PATH_SUFFIXES}) 140 | set(RH_TEST_PREFIX_${var_suffix} "${RH_TEST_COMMON_PREFIX}${RH_INSTALL_PREFIX_${var_suffix}}") 141 | endforeach(var_suffix ${RH_ALL_PATH_SUFFIXES}) 142 | #rewrite some prefixes 143 | #Root of include tree in building package 144 | set(RH_TEST_PREFIX_INCLUDE "${CMAKE_BINARY_DIR}/include") 145 | 146 | set(RH_TEST_PREFIX_TEMPLATES "${CMAKE_SOURCE_DIR}/templates") 147 | 148 | 149 | # rh_load_install_prefixes() 150 | # 151 | # Set common prefixes variables equal to ones in install mode. 152 | # 153 | # Called before configure files, which use prefixes. 154 | macro(rh_load_install_prefixes) 155 | foreach(var_suffix ${RH_ALL_PATH_SUFFIXES}) 156 | set(RH_PREFIX_${var_suffix} ${RH_INSTALL_PREFIX_${var_suffix}}) 157 | endforeach(var_suffix ${RH_ALL_PATH_SUFFIXES}) 158 | endmacro(rh_load_install_prefixes) 159 | 160 | # rh_load_test_prefixes() 161 | # 162 | # Set common prefixes variables equal to ones in test mode. 163 | # 164 | # Called before configure files, which use prefixes. 165 | macro(rh_load_test_prefixes) 166 | foreach(var_suffix ${RH_ALL_PATH_SUFFIXES}) 167 | set(RH_PREFIX_${var_suffix} ${RH_TEST_PREFIX_${var_suffix}}) 168 | endforeach(var_suffix ${RH_ALL_PATH_SUFFIXES}) 169 | endmacro(rh_load_test_prefixes) 170 | 171 | ######################################################################## 172 | # [NB] All the "prefix" directories ending with ${RH_PACKAGE_NAME} 173 | # should be removed when uninstalling the package. 174 | add_custom_target (uninstall_dirs 175 | COMMAND rm -rf "${RH_INSTALL_PREFIX_EXEC_AUX}" 176 | COMMAND rm -rf "${RH_INSTALL_PREFIX_READONLY}" 177 | COMMAND rm -rf "${RH_INSTALL_PREFIX_GLOBAL_CONF}" 178 | COMMAND rm -rf "${RH_INSTALL_PREFIX_LIB_AUX}" 179 | COMMAND rm -rf "${RH_INSTALL_PREFIX_INCLUDE}" 180 | COMMAND rm -rf "${RH_INSTALL_PREFIX_TEMP_SESSION}" 181 | COMMAND rm -rf "${RH_INSTALL_PREFIX_TEMP}" 182 | COMMAND rm -rf "${RH_INSTALL_PREFIX_STATE}" 183 | COMMAND rm -rf "${RH_INSTALL_PREFIX_CACHE}" 184 | COMMAND rm -rf "${RH_INSTALL_PREFIX_VAR}" 185 | COMMAND rm -rf "${RH_INSTALL_PREFIX_DOC}" 186 | COMMAND rm -rf "${RH_TEST_COMMON_PREFIX}" 187 | ) 188 | ######################################################################## 189 | -------------------------------------------------------------------------------- /cmake/other_sources/dwfl_report_elf_check.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | static Dwfl_Callbacks dwfl_callbacks; 6 | 7 | static int 8 | find_debuginfo(Dwfl_Module *mod __attribute__ ((unused)), 9 | void **userdata __attribute__ ((unused)), 10 | const char *modname __attribute__ ((unused)), 11 | GElf_Addr base __attribute__ ((unused)), 12 | const char *file_name __attribute__ ((unused)), 13 | const char *debuglink_file __attribute__ ((unused)), 14 | GElf_Word debuglink_crc __attribute__ ((unused)), 15 | char **debuginfo_file_name __attribute__ ((unused))) 16 | { 17 | return -1; 18 | } 19 | 20 | static int 21 | find_elf(Dwfl_Module *mod __attribute__ ((unused)), 22 | void **userdata __attribute__ ((unused)), 23 | const char *modname __attribute__ ((unused)), 24 | Dwarf_Addr base __attribute__ ((unused)), 25 | char **file_name __attribute__ ((unused)), 26 | Elf **elfp __attribute__ ((unused))) 27 | { 28 | return -1; 29 | } 30 | 31 | int 32 | main() 33 | { 34 | if(elf_version(EV_CURRENT) == EV_NONE) 35 | return 1; 36 | 37 | dwfl_callbacks.section_address = dwfl_offline_section_address; 38 | dwfl_callbacks.find_debuginfo = find_debuginfo; 39 | dwfl_callbacks.find_elf = find_elf; 40 | 41 | Dwfl *dwfl = dwfl_begin(&dwfl_callbacks); 42 | if (dwfl == NULL) 43 | return 1; 44 | 45 | if (NULL == dwfl_report_elf(dwfl, "blah", "foo", 0, 0, 0)); 46 | return 1; 47 | 48 | dwfl_end(dwfl); 49 | return 0; 50 | } -------------------------------------------------------------------------------- /cmake/other_sources/libdw_check.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | static Dwfl_Callbacks dwfl_callbacks; 6 | 7 | static int 8 | find_debuginfo(Dwfl_Module *mod __attribute__ ((unused)), 9 | void **userdata __attribute__ ((unused)), 10 | const char *modname __attribute__ ((unused)), 11 | GElf_Addr base __attribute__ ((unused)), 12 | const char *file_name __attribute__ ((unused)), 13 | const char *debuglink_file __attribute__ ((unused)), 14 | GElf_Word debuglink_crc __attribute__ ((unused)), 15 | char **debuginfo_file_name __attribute__ ((unused))) 16 | { 17 | return -1; 18 | } 19 | 20 | static int 21 | find_elf(Dwfl_Module *mod __attribute__ ((unused)), 22 | void **userdata __attribute__ ((unused)), 23 | const char *modname __attribute__ ((unused)), 24 | Dwarf_Addr base __attribute__ ((unused)), 25 | char **file_name __attribute__ ((unused)), 26 | Elf **elfp __attribute__ ((unused))) 27 | { 28 | return -1; 29 | } 30 | 31 | int 32 | main() 33 | { 34 | if(elf_version(EV_CURRENT) == EV_NONE) 35 | return 1; 36 | 37 | dwfl_callbacks.section_address = dwfl_offline_section_address; 38 | dwfl_callbacks.find_debuginfo = find_debuginfo; 39 | dwfl_callbacks.find_elf = find_elf; 40 | 41 | Dwfl *dwfl = dwfl_begin(&dwfl_callbacks); 42 | if (dwfl == NULL) 43 | return 1; 44 | 45 | dwfl_end(dwfl); 46 | return 0; 47 | } -------------------------------------------------------------------------------- /cmake/other_sources/libelf_check.c: -------------------------------------------------------------------------------- 1 | #include 2 | int main() 3 | { 4 | if(elf_version(EV_CURRENT) == EV_NONE) 5 | { 6 | return 1; 7 | } 8 | return 0; 9 | } -------------------------------------------------------------------------------- /cmake_uninstall.cmake.in: -------------------------------------------------------------------------------- 1 | # This file is based on the example from CMake FAQ: 2 | # http://www.cmake.org/Wiki/CMake_FAQ 3 | 4 | IF(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") 5 | MESSAGE(FATAL_ERROR "Cannot find install manifest: \"@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt\"") 6 | ENDIF(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") 7 | 8 | FILE(READ "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt" files) 9 | STRING(REGEX REPLACE "\n" ";" files "${files}") 10 | FOREACH(file ${files}) 11 | MESSAGE(STATUS "Uninstalling \"$ENV{DESTDIR}${file}\"") 12 | IF(EXISTS "$ENV{DESTDIR}${file}") 13 | EXEC_PROGRAM( 14 | "@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\"" 15 | OUTPUT_VARIABLE rm_out 16 | RETURN_VALUE rm_retval 17 | ) 18 | IF(NOT "${rm_retval}" STREQUAL 0) 19 | MESSAGE(FATAL_ERROR "Problem when removing \"$ENV{DESTDIR}${file}\"") 20 | ENDIF(NOT "${rm_retval}" STREQUAL 0) 21 | ELSE(EXISTS "$ENV{DESTDIR}${file}") 22 | MESSAGE(STATUS "File \"$ENV{DESTDIR}${file}\" does not exist.") 23 | ENDIF(EXISTS "$ENV{DESTDIR}${file}") 24 | ENDFOREACH(file) 25 | -------------------------------------------------------------------------------- /common/inat.c: -------------------------------------------------------------------------------- 1 | /* 2 | * x86 instruction attribute tables 3 | * 4 | * Written by Masami Hiramatsu 5 | * 6 | * Handling of extended attributes was implemented by 7 | * Eugene A. Shatokhin . 8 | * 9 | * This program is free software; you can redistribute it and/or modify 10 | * it under the terms of the GNU General Public License as published by 11 | * the Free Software Foundation; either version 2 of the License, or 12 | * (at your option) any later version. 13 | * 14 | * This program is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU General Public License 20 | * along with this program; if not, write to the Free Software 21 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 22 | * 23 | */ 24 | 25 | /* The instruction decoder may be used by both kernel-space and user-space 26 | * components. */ 27 | #ifdef __KERNEL__ 28 | # include 29 | #else 30 | # include 31 | #endif 32 | 33 | #include 34 | 35 | /* Attribute tables are generated from opcode map */ 36 | #include 37 | 38 | insn_attr_t inat_zero_attrs = { 39 | .attributes = 0, 40 | .addr_method1 = 0, 41 | .opnd_type1 = 0, 42 | .addr_method2 = 0, 43 | .opnd_type2 = 0, 44 | }; 45 | 46 | /* Merge attributes in 'attr' and 'other' and return the result. 47 | * 'attr' is usually the set of attributes for a group, 'other' - for an 48 | * insn in that group. */ 49 | static insn_attr_t inat_merge_insn_attr(insn_attr_t attr, insn_attr_t other) 50 | { 51 | attr.attributes |= other.attributes; 52 | 53 | /* If at least some operand information is defined in 'other', the 54 | * data from 'other' (including zero fields) should override the data 55 | * from 'attr'. Note that if the operand type is defined, the 56 | * addressing method must be defined too but not vice versa, so it 57 | * is enough to check just the addressing method. */ 58 | if (other.addr_method1 != 0 || other.addr_method2 != 0) 59 | { 60 | attr.addr_method1 = other.addr_method1; 61 | attr.opnd_type1 = other.opnd_type1; 62 | attr.addr_method2 = other.addr_method2; 63 | attr.opnd_type2 = other.opnd_type2; 64 | } 65 | 66 | return attr; 67 | } 68 | 69 | /* Attribute search APIs */ 70 | insn_attr_t inat_get_opcode_attribute(insn_byte_t opcode) 71 | { 72 | return inat_primary_table[opcode]; 73 | } 74 | 75 | int inat_get_last_prefix_id(insn_byte_t last_pfx) 76 | { 77 | insn_attr_t lpfx_attr; 78 | 79 | lpfx_attr = inat_get_opcode_attribute(last_pfx); 80 | return inat_last_prefix_id(lpfx_attr); 81 | } 82 | 83 | insn_attr_t inat_get_escape_attribute(insn_byte_t opcode, int lpfx_id, 84 | insn_attr_t esc_attr) 85 | { 86 | const insn_attr_t *table; 87 | int n; 88 | 89 | n = inat_escape_id(esc_attr); 90 | 91 | table = inat_escape_tables[n][0]; 92 | if (!table) 93 | return inat_zero_attrs; 94 | if (inat_has_variant(table[opcode]) && lpfx_id) { 95 | table = inat_escape_tables[n][lpfx_id]; 96 | if (!table) 97 | return inat_zero_attrs; 98 | } 99 | return table[opcode]; 100 | } 101 | 102 | insn_attr_t inat_get_group_attribute(insn_byte_t modrm, int lpfx_id, 103 | insn_attr_t grp_attr) 104 | { 105 | const insn_attr_t *table; 106 | int n; 107 | 108 | n = inat_group_id(grp_attr); 109 | 110 | table = inat_group_tables[n][0]; 111 | if (!table) 112 | return inat_group_common_attribute(grp_attr); 113 | if (inat_has_variant(table[X86_MODRM_REG(modrm)]) && lpfx_id) { 114 | table = inat_group_tables[n][lpfx_id]; 115 | if (!table) 116 | return inat_group_common_attribute(grp_attr); 117 | } 118 | 119 | return inat_merge_insn_attr( 120 | table[X86_MODRM_REG(modrm)], 121 | inat_group_common_attribute(grp_attr)); 122 | } 123 | 124 | insn_attr_t inat_get_avx_attribute(insn_byte_t opcode, insn_byte_t vex_m, 125 | insn_byte_t vex_p) 126 | { 127 | const insn_attr_t *table; 128 | if (vex_m > X86_VEX_M_MAX || vex_p > INAT_LSTPFX_MAX) 129 | return inat_zero_attrs; 130 | /* At first, this checks the master table */ 131 | table = inat_avx_tables[vex_m][0]; 132 | if (!table) 133 | return inat_zero_attrs; 134 | if (!inat_is_group(table[opcode]) && vex_p) { 135 | /* If this is not a group, get attribute directly */ 136 | table = inat_avx_tables[vex_m][vex_p]; 137 | if (!table) 138 | return inat_zero_attrs; 139 | } 140 | return table[opcode]; 141 | } 142 | 143 | -------------------------------------------------------------------------------- /common/inat.h: -------------------------------------------------------------------------------- 1 | #ifndef _ASM_X86_INAT_H 2 | #define _ASM_X86_INAT_H 3 | /* 4 | * x86 instruction attributes 5 | * 6 | * Written by Masami Hiramatsu 7 | * 8 | * Handling of extended attributes was implemented by 9 | * Eugene A. Shatokhin . 10 | * 11 | * This program is free software; you can redistribute it and/or modify 12 | * it under the terms of the GNU General Public License as published by 13 | * the Free Software Foundation; either version 2 of the License, or 14 | * (at your option) any later version. 15 | * 16 | * This program is distributed in the hope that it will be useful, 17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | * GNU General Public License for more details. 20 | * 21 | * You should have received a copy of the GNU General Public License 22 | * along with this program; if not, write to the Free Software 23 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 24 | * 25 | */ 26 | #include 27 | 28 | #ifdef __cplusplus 29 | extern "C" { 30 | #endif 31 | 32 | /* 33 | * Internal bits. Don't use bitmasks directly, because these bits are 34 | * unstable. You should use checking functions. 35 | */ 36 | 37 | #define INAT_OPCODE_TABLE_SIZE 256 38 | #define INAT_GROUP_TABLE_SIZE 8 39 | 40 | /* Legacy last prefixes */ 41 | #define INAT_PFX_OPNDSZ 1 /* 0x66 */ /* LPFX1 */ 42 | #define INAT_PFX_REPE 2 /* 0xF3 */ /* LPFX2 */ 43 | #define INAT_PFX_REPNE 3 /* 0xF2 */ /* LPFX3 */ 44 | /* Other Legacy prefixes */ 45 | #define INAT_PFX_LOCK 4 /* 0xF0 */ 46 | #define INAT_PFX_CS 5 /* 0x2E */ 47 | #define INAT_PFX_DS 6 /* 0x3E */ 48 | #define INAT_PFX_ES 7 /* 0x26 */ 49 | #define INAT_PFX_FS 8 /* 0x64 */ 50 | #define INAT_PFX_GS 9 /* 0x65 */ 51 | #define INAT_PFX_SS 10 /* 0x36 */ 52 | #define INAT_PFX_ADDRSZ 11 /* 0x67 */ 53 | /* x86-64 REX prefix */ 54 | #define INAT_PFX_REX 12 /* 0x4X */ 55 | /* AVX VEX prefixes */ 56 | #define INAT_PFX_VEX2 13 /* 2-bytes VEX prefix */ 57 | #define INAT_PFX_VEX3 14 /* 3-bytes VEX prefix */ 58 | 59 | #define INAT_LSTPFX_MAX 3 60 | #define INAT_LGCPFX_MAX 11 61 | 62 | /* Immediate size */ 63 | #define INAT_IMM_BYTE 1 64 | #define INAT_IMM_WORD 2 65 | #define INAT_IMM_DWORD 3 66 | #define INAT_IMM_QWORD 4 67 | #define INAT_IMM_PTR 5 68 | #define INAT_IMM_VWORD32 6 69 | #define INAT_IMM_VWORD 7 70 | 71 | /* Legacy prefix */ 72 | #define INAT_PFX_OFFS 0 73 | #define INAT_PFX_BITS 4 74 | #define INAT_PFX_MAX ((1 << INAT_PFX_BITS) - 1) 75 | #define INAT_PFX_MASK (INAT_PFX_MAX << INAT_PFX_OFFS) 76 | /* Escape opcodes */ 77 | #define INAT_ESC_OFFS (INAT_PFX_OFFS + INAT_PFX_BITS) 78 | #define INAT_ESC_BITS 2 79 | #define INAT_ESC_MAX ((1 << INAT_ESC_BITS) - 1) 80 | #define INAT_ESC_MASK (INAT_ESC_MAX << INAT_ESC_OFFS) 81 | /* Group opcodes (1-16) */ 82 | #define INAT_GRP_OFFS (INAT_ESC_OFFS + INAT_ESC_BITS) 83 | #define INAT_GRP_BITS 5 84 | #define INAT_GRP_MAX ((1 << INAT_GRP_BITS) - 1) 85 | #define INAT_GRP_MASK (INAT_GRP_MAX << INAT_GRP_OFFS) 86 | /* Immediates */ 87 | #define INAT_IMM_OFFS (INAT_GRP_OFFS + INAT_GRP_BITS) 88 | #define INAT_IMM_BITS 3 89 | #define INAT_IMM_MASK (((1 << INAT_IMM_BITS) - 1) << INAT_IMM_OFFS) 90 | /* Flags */ 91 | #define INAT_FLAG_OFFS (INAT_IMM_OFFS + INAT_IMM_BITS) 92 | #define INAT_MODRM (1 << (INAT_FLAG_OFFS)) 93 | #define INAT_FORCE64 (1 << (INAT_FLAG_OFFS + 1)) 94 | #define INAT_SCNDIMM (1 << (INAT_FLAG_OFFS + 2)) 95 | #define INAT_MOFFSET (1 << (INAT_FLAG_OFFS + 3)) 96 | #define INAT_VARIANT (1 << (INAT_FLAG_OFFS + 4)) 97 | #define INAT_VEXOK (1 << (INAT_FLAG_OFFS + 5)) 98 | #define INAT_VEXONLY (1 << (INAT_FLAG_OFFS + 6)) 99 | #define INAT_FLAG_BITS 7 100 | 101 | /* The flags specifying memory access type, i.e. whether a given instruction 102 | * can read data from memory or not, same for writing. 103 | * Note that analysis of some other parts of the instruction 104 | * (ModRM.Mod, for example) may be necessary to determine if it actually 105 | * accesses memory. */ 106 | #define INAT_MEM_OFFS (INAT_FLAG_OFFS + INAT_FLAG_BITS) 107 | #define INAT_MEM_CAN_READ (1 << (INAT_MEM_OFFS)) 108 | #define INAT_MEM_CAN_WRITE (1 << (INAT_MEM_OFFS + 1)) 109 | #define INAT_MEM_BITS 2 110 | 111 | /* Attribute making macros for attribute tables */ 112 | #define INAT_MAKE_PREFIX(pfx) (pfx << INAT_PFX_OFFS) 113 | #define INAT_MAKE_ESCAPE(esc) (esc << INAT_ESC_OFFS) 114 | #define INAT_MAKE_GROUP(grp) ((grp << INAT_GRP_OFFS) | INAT_MODRM) 115 | #define INAT_MAKE_IMM(imm) (imm << INAT_IMM_OFFS) 116 | 117 | /* Codes for the addressing methods and operand types. Their meaning is 118 | * described in Intel Software Developer's Manual Vol. 2B, Appendix A 119 | * "Opcode Map". 120 | * The actual numeric values do not matter as long as they are unique in the 121 | * group. */ 122 | enum inat_amethod 123 | { 124 | INAT_AMETHOD_NONE = 0, 125 | INAT_AMETHOD_A, 126 | INAT_AMETHOD_B, 127 | INAT_AMETHOD_C, 128 | INAT_AMETHOD_D, 129 | INAT_AMETHOD_E, 130 | INAT_AMETHOD_F, 131 | INAT_AMETHOD_G, 132 | INAT_AMETHOD_H, 133 | INAT_AMETHOD_I, 134 | INAT_AMETHOD_J, 135 | INAT_AMETHOD_L, 136 | INAT_AMETHOD_M, 137 | INAT_AMETHOD_N, 138 | INAT_AMETHOD_O, 139 | INAT_AMETHOD_P, 140 | INAT_AMETHOD_Q, 141 | INAT_AMETHOD_R, 142 | INAT_AMETHOD_S, 143 | INAT_AMETHOD_U, 144 | INAT_AMETHOD_V, 145 | INAT_AMETHOD_W, 146 | INAT_AMETHOD_X, 147 | INAT_AMETHOD_Y, 148 | }; 149 | 150 | enum inat_optype 151 | { 152 | INAT_OPTYPE_NONE = 0, 153 | INAT_OPTYPE_A, 154 | INAT_OPTYPE_B, 155 | INAT_OPTYPE_C, 156 | INAT_OPTYPE_D, 157 | INAT_OPTYPE_DQ, 158 | INAT_OPTYPE_P, 159 | INAT_OPTYPE_PD, 160 | INAT_OPTYPE_PI, 161 | INAT_OPTYPE_PS, 162 | INAT_OPTYPE_Q, 163 | INAT_OPTYPE_QQ, 164 | INAT_OPTYPE_S, 165 | INAT_OPTYPE_SD, 166 | INAT_OPTYPE_SS, 167 | INAT_OPTYPE_SI, 168 | INAT_OPTYPE_V, 169 | INAT_OPTYPE_W, 170 | INAT_OPTYPE_X, 171 | INAT_OPTYPE_Y, 172 | INAT_OPTYPE_Z, 173 | }; 174 | 175 | /* Condition codes used in jcc, cmovcc, setcc. Such code is usually given in 176 | * the lower 4 bits of the opcode. 177 | * [NB] Invert the lower bit of the opcode <=> invert the condition. 178 | * See Table B10 "Encoding of Conditional Test (tttn) Field" in 179 | * Intel Software Developer's Manual Vol. 2B. */ 180 | enum inat_cond_code 181 | { 182 | INAT_CC_O = 0x0, 183 | INAT_CC_NO = 0x1, 184 | INAT_CC_B = 0x2, /* B, NAE */ 185 | INAT_CC_NAE = 0x2, 186 | INAT_CC_NB = 0x3, /* NB, AE */ 187 | INAT_CC_AE = 0x3, 188 | INAT_CC_E = 0x4, /* E, Z */ 189 | INAT_CC_Z = 0x4, 190 | INAT_CC_NE = 0x5, /* NE, NZ */ 191 | INAT_CC_NZ = 0x5, 192 | INAT_CC_BE = 0x6, /* BE, NA */ 193 | INAT_CC_NA = 0x6, 194 | INAT_CC_NBE = 0x7, /* NBE, A */ 195 | INAT_CC_A = 0x7, 196 | INAT_CC_S = 0x8, 197 | INAT_CC_NS = 0x9, 198 | INAT_CC_P = 0xa, /* P, PE */ 199 | INAT_CC_PE = 0xa, 200 | INAT_CC_NP = 0xb, /* NP, PO */ 201 | INAT_CC_PO = 0xb, 202 | INAT_CC_L = 0xc, /* L, NGE */ 203 | INAT_CC_NGE = 0xc, 204 | INAT_CC_NL = 0xd, /* NL, GE */ 205 | INAT_CC_GE = 0xd, 206 | INAT_CC_LE = 0xe, /* LE, NG */ 207 | INAT_CC_NG = 0xe, 208 | INAT_CC_NLE = 0xf, /* NLE, G */ 209 | INAT_CC_G = 0xf, 210 | }; 211 | 212 | /* Attribute search APIs */ 213 | extern insn_attr_t inat_get_opcode_attribute(insn_byte_t opcode); 214 | extern int inat_get_last_prefix_id(insn_byte_t last_pfx); 215 | extern insn_attr_t inat_get_escape_attribute(insn_byte_t opcode, 216 | int lpfx_id, 217 | insn_attr_t esc_attr); 218 | extern insn_attr_t inat_get_group_attribute(insn_byte_t modrm, 219 | int lpfx_id, 220 | insn_attr_t esc_attr); 221 | extern insn_attr_t inat_get_avx_attribute(insn_byte_t opcode, 222 | insn_byte_t vex_m, 223 | insn_byte_t vex_pp); 224 | 225 | extern insn_attr_t inat_zero_attrs; 226 | 227 | /* Attribute checking functions */ 228 | static inline int inat_is_legacy_prefix(insn_attr_t ia) 229 | { 230 | unsigned int attr = ia.attributes; 231 | attr &= INAT_PFX_MASK; 232 | return attr && attr <= INAT_LGCPFX_MAX; 233 | } 234 | 235 | static inline int inat_is_address_size_prefix(insn_attr_t ia) 236 | { 237 | unsigned int attr = ia.attributes; 238 | return (attr & INAT_PFX_MASK) == INAT_PFX_ADDRSZ; 239 | } 240 | 241 | static inline int inat_is_operand_size_prefix(insn_attr_t ia) 242 | { 243 | unsigned int attr = ia.attributes; 244 | return (attr & INAT_PFX_MASK) == INAT_PFX_OPNDSZ; 245 | } 246 | 247 | static inline int inat_is_rex_prefix(insn_attr_t ia) 248 | { 249 | unsigned int attr = ia.attributes; 250 | return (attr & INAT_PFX_MASK) == INAT_PFX_REX; 251 | } 252 | 253 | static inline int inat_last_prefix_id(insn_attr_t ia) 254 | { 255 | unsigned int attr = ia.attributes; 256 | if ((attr & INAT_PFX_MASK) > INAT_LSTPFX_MAX) 257 | return 0; 258 | else 259 | return attr & INAT_PFX_MASK; 260 | } 261 | 262 | static inline int inat_is_vex_prefix(insn_attr_t ia) 263 | { 264 | unsigned int attr = ia.attributes; 265 | attr &= INAT_PFX_MASK; 266 | return attr == INAT_PFX_VEX2 || attr == INAT_PFX_VEX3; 267 | } 268 | 269 | static inline int inat_is_vex3_prefix(insn_attr_t ia) 270 | { 271 | unsigned int attr = ia.attributes; 272 | return (attr & INAT_PFX_MASK) == INAT_PFX_VEX3; 273 | } 274 | 275 | static inline int inat_is_escape(insn_attr_t ia) 276 | { 277 | unsigned int attr = ia.attributes; 278 | return attr & INAT_ESC_MASK; 279 | } 280 | 281 | static inline int inat_escape_id(insn_attr_t ia) 282 | { 283 | unsigned int attr = ia.attributes; 284 | return (attr & INAT_ESC_MASK) >> INAT_ESC_OFFS; 285 | } 286 | 287 | static inline int inat_is_group(insn_attr_t ia) 288 | { 289 | unsigned int attr = ia.attributes; 290 | return attr & INAT_GRP_MASK; 291 | } 292 | 293 | static inline int inat_group_id(insn_attr_t ia) 294 | { 295 | unsigned int attr = ia.attributes; 296 | return (attr & INAT_GRP_MASK) >> INAT_GRP_OFFS; 297 | } 298 | 299 | static inline insn_attr_t inat_group_common_attribute(insn_attr_t attr) 300 | { 301 | attr.attributes &= ~INAT_GRP_MASK; 302 | return attr; 303 | } 304 | 305 | static inline int inat_has_immediate(insn_attr_t ia) 306 | { 307 | unsigned int attr = ia.attributes; 308 | return attr & INAT_IMM_MASK; 309 | } 310 | 311 | static inline int inat_immediate_size(insn_attr_t ia) 312 | { 313 | unsigned int attr = ia.attributes; 314 | return (attr & INAT_IMM_MASK) >> INAT_IMM_OFFS; 315 | } 316 | 317 | static inline int inat_has_modrm(insn_attr_t ia) 318 | { 319 | unsigned int attr = ia.attributes; 320 | return attr & INAT_MODRM; 321 | } 322 | 323 | static inline int inat_is_force64(insn_attr_t ia) 324 | { 325 | unsigned int attr = ia.attributes; 326 | return attr & INAT_FORCE64; 327 | } 328 | 329 | static inline int inat_has_second_immediate(insn_attr_t ia) 330 | { 331 | unsigned int attr = ia.attributes; 332 | return attr & INAT_SCNDIMM; 333 | } 334 | 335 | static inline int inat_has_moffset(insn_attr_t ia) 336 | { 337 | unsigned int attr = ia.attributes; 338 | return attr & INAT_MOFFSET; 339 | } 340 | 341 | static inline int inat_has_variant(insn_attr_t ia) 342 | { 343 | unsigned int attr = ia.attributes; 344 | return attr & INAT_VARIANT; 345 | } 346 | 347 | static inline int inat_accept_vex(insn_attr_t ia) 348 | { 349 | unsigned int attr = ia.attributes; 350 | return attr & INAT_VEXOK; 351 | } 352 | 353 | static inline int inat_must_vex(insn_attr_t ia) 354 | { 355 | unsigned int attr = ia.attributes; 356 | return attr & INAT_VEXONLY; 357 | } 358 | 359 | #ifdef __cplusplus 360 | } 361 | #endif 362 | 363 | #endif /* _ASM_X86_INAT_H */ 364 | -------------------------------------------------------------------------------- /common/inat_types.h: -------------------------------------------------------------------------------- 1 | #ifndef _ASM_X86_INAT_TYPES_H 2 | #define _ASM_X86_INAT_TYPES_H 3 | /* 4 | * x86 instruction attributes 5 | * 6 | * Written by Masami Hiramatsu 7 | * 8 | * This program is free software; you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation; either version 2 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program; if not, write to the Free Software 20 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 21 | * 22 | */ 23 | 24 | /* Instruction attributes */ 25 | typedef struct insn_attr 26 | { 27 | /* Attributes of the instruction as a whole */ 28 | unsigned int attributes; 29 | 30 | /* Codes for the addressing method and the operand type for two 31 | * operands */ 32 | unsigned char addr_method1; 33 | unsigned char opnd_type1; 34 | unsigned char addr_method2; 35 | unsigned char opnd_type2; 36 | } insn_attr_t; 37 | 38 | typedef unsigned char insn_byte_t; 39 | typedef signed int insn_value_t; 40 | 41 | #endif 42 | -------------------------------------------------------------------------------- /common/insn.h: -------------------------------------------------------------------------------- 1 | #ifndef _ASM_X86_INSN_H 2 | #define _ASM_X86_INSN_H 3 | /* 4 | * x86 instruction analysis 5 | * 6 | * Written by Masami Hiramatsu 7 | * 8 | * Handling of extended attributes was implemented by 9 | * Eugene A. Shatokhin 10 | * This program is free software; you can redistribute it and/or modify 11 | * it under the terms of the GNU General Public License as published by 12 | * the Free Software Foundation; either version 2 of the License, or 13 | * (at your option) any later version. 14 | * 15 | * This program is distributed in the hope that it will be useful, 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | * GNU General Public License for more details. 19 | * 20 | * You should have received a copy of the GNU General Public License 21 | * along with this program; if not, write to the Free Software 22 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 23 | * 24 | * Copyright (C) IBM Corporation, 2009 25 | */ 26 | 27 | #include 28 | 29 | /* insn_attr_t is defined in inat.h */ 30 | #include 31 | 32 | #ifdef __cplusplus 33 | extern "C" { 34 | #endif 35 | 36 | struct insn_field { 37 | union { 38 | insn_value_t value; 39 | insn_byte_t bytes[4]; 40 | }; 41 | /* !0 if we've run insn_get_xxx() for this field */ 42 | unsigned char got; 43 | unsigned char nbytes; 44 | }; 45 | 46 | struct insn { 47 | struct insn_field prefixes; /* 48 | * Prefixes 49 | * prefixes.bytes[3]: last prefix 50 | */ 51 | struct insn_field rex_prefix; /* REX prefix */ 52 | struct insn_field vex_prefix; /* VEX prefix */ 53 | struct insn_field opcode; /* 54 | * opcode.bytes[0]: opcode1 55 | * opcode.bytes[1]: opcode2 56 | * opcode.bytes[2]: opcode3 57 | */ 58 | struct insn_field modrm; 59 | struct insn_field sib; 60 | struct insn_field displacement; 61 | union { 62 | struct insn_field immediate; 63 | struct insn_field moffset1; /* for 64bit MOV */ 64 | struct insn_field immediate1; /* for 64bit imm or off16/32 */ 65 | }; 66 | union { 67 | struct insn_field moffset2; /* for 64bit MOV */ 68 | struct insn_field immediate2; /* for 64bit imm or seg16 */ 69 | }; 70 | 71 | insn_attr_t attr; 72 | unsigned char opnd_bytes; 73 | unsigned char addr_bytes; 74 | unsigned char length; 75 | unsigned char x86_64; 76 | 77 | const insn_byte_t *kaddr; /* kernel address of insn to analyze */ 78 | const insn_byte_t *end_kaddr; /* kernel address of last insn in buffer */ 79 | const insn_byte_t *next_byte; 80 | }; 81 | 82 | /* See commit 91e5ed49fca09c2b83b262b9757d1376ee2b46c3 in the mainline: 83 | * "x86/asm/decoder: Fix and enforce max instruction size in the insn decoder" 84 | */ 85 | #define MAX_INSN_SIZE 15 86 | 87 | #define X86_MODRM_MOD(modrm) (((modrm) & 0xc0) >> 6) 88 | #define X86_MODRM_REG(modrm) (((modrm) & 0x38) >> 3) 89 | #define X86_MODRM_RM(modrm) ((modrm) & 0x07) 90 | 91 | #define X86_SIB_SCALE(sib) (((sib) & 0xc0) >> 6) 92 | #define X86_SIB_INDEX(sib) (((sib) & 0x38) >> 3) 93 | #define X86_SIB_BASE(sib) ((sib) & 0x07) 94 | 95 | #define X86_REX_W(rex) ((rex) & 8) 96 | #define X86_REX_R(rex) ((rex) & 4) 97 | #define X86_REX_X(rex) ((rex) & 2) 98 | #define X86_REX_B(rex) ((rex) & 1) 99 | 100 | /* VEX bit flags */ 101 | #define X86_VEX_W(vex) ((vex) & 0x80) /* VEX3 Byte2 */ 102 | #define X86_VEX_R(vex) ((vex) & 0x80) /* VEX2/3 Byte1 */ 103 | #define X86_VEX_X(vex) ((vex) & 0x40) /* VEX3 Byte1 */ 104 | #define X86_VEX_B(vex) ((vex) & 0x20) /* VEX3 Byte1 */ 105 | #define X86_VEX_L(vex) ((vex) & 0x04) /* VEX3 Byte2, VEX2 Byte1 */ 106 | /* VEX bit fields */ 107 | #define X86_VEX3_M(vex) ((vex) & 0x1f) /* VEX3 Byte1 */ 108 | #define X86_VEX2_M 1 /* VEX2.M always 1 */ 109 | #define X86_VEX_V(vex) (((vex) & 0x78) >> 3) /* VEX3 Byte2, VEX2 Byte1 */ 110 | #define X86_VEX_P(vex) ((vex) & 0x03) /* VEX3 Byte2, VEX2 Byte1 */ 111 | #define X86_VEX_M_MAX 0x1f /* VEX3.M Maximum value */ 112 | 113 | extern void insn_init(struct insn *insn, const void *kaddr, int buf_len, int x86_64); 114 | extern void insn_get_prefixes(struct insn *insn); 115 | extern void insn_get_opcode(struct insn *insn); 116 | extern void insn_get_modrm(struct insn *insn); 117 | extern void insn_get_sib(struct insn *insn); 118 | extern void insn_get_displacement(struct insn *insn); 119 | extern void insn_get_immediate(struct insn *insn); 120 | extern void insn_get_length(struct insn *insn); 121 | 122 | /* Attribute will be determined after getting ModRM (for opcode groups) */ 123 | static inline void insn_get_attribute(struct insn *insn) 124 | { 125 | insn_get_modrm(insn); 126 | } 127 | 128 | /* Instruction uses RIP-relative addressing */ 129 | extern int insn_rip_relative(struct insn *insn); 130 | 131 | #ifdef __KERNEL__ 132 | /* Init insn for kernel text */ 133 | static inline void kernel_insn_init(struct insn *insn, 134 | const void *kaddr, int buf_len) 135 | { 136 | #ifdef CONFIG_X86_64 137 | insn_init(insn, kaddr, buf_len, 1); 138 | #else /* CONFIG_X86_32 */ 139 | insn_init(insn, kaddr, buf_len, 0); 140 | #endif 141 | } 142 | #endif /* __KERNEL__ */ 143 | 144 | static inline int insn_is_avx(struct insn *insn) 145 | { 146 | if (!insn->prefixes.got) 147 | insn_get_prefixes(insn); 148 | return (insn->vex_prefix.value != 0); 149 | } 150 | 151 | /* Ensure this instruction is decoded completely */ 152 | static inline int insn_complete(struct insn *insn) 153 | { 154 | return insn->opcode.got && insn->modrm.got && insn->sib.got && 155 | insn->displacement.got && insn->immediate.got; 156 | } 157 | 158 | static inline insn_byte_t insn_vex_m_bits(struct insn *insn) 159 | { 160 | if (insn->vex_prefix.nbytes == 2) /* 2 bytes VEX */ 161 | return X86_VEX2_M; 162 | else 163 | return X86_VEX3_M(insn->vex_prefix.bytes[1]); 164 | } 165 | 166 | static inline insn_byte_t insn_vex_p_bits(struct insn *insn) 167 | { 168 | if (insn->vex_prefix.nbytes == 2) /* 2 bytes VEX */ 169 | return X86_VEX_P(insn->vex_prefix.bytes[1]); 170 | else 171 | return X86_VEX_P(insn->vex_prefix.bytes[2]); 172 | } 173 | 174 | /* Get the last prefix id from last prefix or VEX prefix */ 175 | static inline int insn_last_prefix_id(struct insn *insn) 176 | { 177 | if (insn_is_avx(insn)) 178 | return insn_vex_p_bits(insn); /* VEX_p is a SIMD prefix id */ 179 | 180 | if (insn->prefixes.bytes[3]) 181 | return inat_get_last_prefix_id(insn->prefixes.bytes[3]); 182 | 183 | return 0; 184 | } 185 | 186 | /* Offset of each field from kaddr */ 187 | static inline int insn_offset_rex_prefix(struct insn *insn) 188 | { 189 | return insn->prefixes.nbytes; 190 | } 191 | static inline int insn_offset_vex_prefix(struct insn *insn) 192 | { 193 | return insn_offset_rex_prefix(insn) + insn->rex_prefix.nbytes; 194 | } 195 | static inline int insn_offset_opcode(struct insn *insn) 196 | { 197 | return insn_offset_vex_prefix(insn) + insn->vex_prefix.nbytes; 198 | } 199 | static inline int insn_offset_modrm(struct insn *insn) 200 | { 201 | return insn_offset_opcode(insn) + insn->opcode.nbytes; 202 | } 203 | static inline int insn_offset_sib(struct insn *insn) 204 | { 205 | return insn_offset_modrm(insn) + insn->modrm.nbytes; 206 | } 207 | static inline int insn_offset_displacement(struct insn *insn) 208 | { 209 | return insn_offset_sib(insn) + insn->sib.nbytes; 210 | } 211 | static inline int insn_offset_immediate(struct insn *insn) 212 | { 213 | return insn_offset_displacement(insn) + insn->displacement.nbytes; 214 | } 215 | 216 | #ifdef __cplusplus 217 | } 218 | #endif 219 | 220 | #endif /* _ASM_X86_INSN_H */ 221 | -------------------------------------------------------------------------------- /common/util.c: -------------------------------------------------------------------------------- 1 | /* This program is free software; you can redistribute it and/or modify it 2 | * under the terms of the GNU General Public License version 2 as published 3 | * by the Free Software Foundation. 4 | * 5 | * Copyright 2015 Eugene Shatokhin */ 6 | /* ====================================================================== */ 7 | 8 | #include 9 | #include 10 | 11 | /* Convenience macros. */ 12 | #define X86_REX_R_EQ_B(rex) ((X86_REX_R(rex) >> 2) == (X86_REX_B(rex))) 13 | #define X86_REX_R_EQ_X(rex) ((X86_REX_R(rex) >> 2) == (X86_REX_X(rex) >> 1)) 14 | 15 | /* insn_has_prefix() - Determine if the instruction has a given legacy or 16 | * mandatory prefix. 17 | * 18 | * If necessary, decodes the prefixes first. */ 19 | static int 20 | insn_has_prefix(struct insn *insn, insn_byte_t prefix) 21 | { 22 | unsigned char i; 23 | struct insn_field *prefixes = &insn->prefixes; 24 | 25 | /* Decode the opcode and the prefixes */ 26 | insn_get_prefixes(insn); 27 | 28 | for (i = 0; i < prefixes->nbytes; ++i) { 29 | if (prefixes->bytes[i] == prefix) 30 | return 1; 31 | } 32 | return 0; 33 | } 34 | 35 | /* A helper function checking if the given "lea" instruction is a no-op. 36 | * See insn_is_noop() for details and for the description of 'rex' 37 | * parameter. */ 38 | static int 39 | insn_lea_is_noop(struct insn *insn, unsigned char rex) 40 | { 41 | unsigned char modRM = insn->modrm.bytes[0]; 42 | unsigned char modRM_mod, modRM_reg, modRM_rm; 43 | 44 | unsigned char sib = insn->sib.bytes[0]; /* missing fields are 0 */ 45 | unsigned char sib_ss, sib_index, sib_base; 46 | 47 | /* If operand-size override or address-size override are used, it 48 | * may be not a no-op. */ 49 | if (insn_has_prefix(insn, 0x66) || insn_has_prefix(insn, 0x67)) 50 | return 0; 51 | 52 | modRM_mod = X86_MODRM_MOD(modRM); 53 | modRM_reg = X86_MODRM_REG(modRM); 54 | modRM_rm = X86_MODRM_RM(modRM); 55 | 56 | sib_ss = X86_SIB_SCALE(sib); 57 | sib_index = X86_SIB_INDEX(sib); 58 | sib_base = X86_SIB_BASE(sib); 59 | 60 | /* Without REX.W, lea operates on 32-bit values on x86-64 by 61 | * default, and such operations may not be no-ops. Example: 62 | * "lea (%esi), %esi" zeroes the higher 32 bits of %rsi.*/ 63 | if (!X86_REX_W(rex)) 64 | return 0; 65 | 66 | if (modRM_mod == 0) { 67 | if (modRM_rm == 5) /* 101(b), disp32 or RIP-relative */ 68 | return 0; 69 | 70 | if (modRM_rm != 4) { 71 | /* != 100(b) => no SIB, "lea (%regB), %regA" */ 72 | return (modRM_rm == modRM_reg && 73 | X86_REX_R_EQ_B(rex)); 74 | } 75 | 76 | /* modRM_rm == 4 => SIB byte present */ 77 | if (sib_index == 4 && !X86_REX_X(rex)) { 78 | /* SIB.Index == 100(b) and REX.X is not set => no index */ 79 | /* "lea (%regB,,), %regA" */ 80 | return (sib_base != 5 && /* => base is used */ 81 | sib_base == modRM_reg && 82 | X86_REX_R_EQ_B(rex)); 83 | } 84 | 85 | /* SIB.Index != 100(b) or REX.X is set => index register 86 | * is used. "lea disp32(,%regB,1), %regA" */ 87 | return (sib_ss == 0 && 88 | sib_base == 5 && /* => [scaled index] + disp32 */ 89 | sib_index == modRM_reg && 90 | X86_REX_R_EQ_X(rex) && 91 | insn->displacement.value == 0); 92 | } 93 | else if (modRM_mod == 1 || modRM_mod == 2) { 94 | if (modRM_rm != 4) { /* => no SIB byte */ 95 | return (modRM_rm == modRM_reg && 96 | X86_REX_R_EQ_B(rex) && 97 | insn->displacement.value == 0); 98 | } 99 | 100 | /* modRM_rm == 4 => SIB byte present */ 101 | if (sib_index != 4 || X86_REX_X(rex)) 102 | return 0; 103 | /* No noops if index is used. Even if SIB.Base == 5, 104 | * the address would be [scaled index]+disp+[rbp] */ 105 | 106 | /* SIB.Index == 4 && REX.X is not set => no index register */ 107 | if (sib_base == 5) { /* disp8/32 + [rbp] */ 108 | return (modRM_reg == 5 && 109 | !X86_REX_R(rex) && !X86_REX_B(rex) && 110 | insn->displacement.value == 0); 111 | /* %rbp is used if REX.B==0. The docs are not clear 112 | * about %r13 if REX.B==1, so let's go a safer route 113 | * and require REX.R==0 && REX.B==0. */ 114 | } 115 | 116 | /* SIB.base != 5 => base register is used. */ 117 | return (sib_base == modRM_reg && 118 | X86_REX_R_EQ_B(rex) && 119 | insn->displacement.value == 0); 120 | } 121 | 122 | return 0; /* unknown or not a no-op */ 123 | } 124 | 125 | /* insn_is_noop() - Check if the instruction is a no-op of one of the 126 | * commonly used kinds. 127 | * The function returns non-zero for a no-op, 0 if unknown or not a no-op. 128 | * If necessary, decodes the instruction first. */ 129 | static int 130 | insn_is_noop(struct insn *insn) 131 | { 132 | /* REX prefix on x86-64 (0 if absent because struct insn is zeroed 133 | * during initialization). On x86-32, we set it to a "fake" value 134 | * with REX.W set and REX.R, REX.X, REX.B unset. This allows to 135 | * avoid additional #ifdefs in the code because the requirements for 136 | * REX prefix of no-ops on x86-64 will hold for this fake prefix 137 | * on x86-32 too: REX.W==1, REX.X == 0; REX.R==REX.B; REX.X==REX.R, 138 | * where applicable. */ 139 | unsigned char rex; 140 | unsigned char modRM; 141 | 142 | /* Decode the instruction if it is not already decoded. */ 143 | insn_get_length(insn); 144 | rex = insn->x86_64 145 | ? insn->rex_prefix.bytes[0] 146 | : 0x48; /* 01001000(b) */ 147 | 148 | switch (insn->opcode.bytes[0]) { 149 | case 0x90: /* Group: "nop" */ 150 | /* Require REX.R==REX.X==REX.B==0, for simplicity. */ 151 | return ((rex & 0x07) == 0); 152 | 153 | case 0x86: /* Group: "xchg/mov reg8, reg8" */ 154 | case 0x88: 155 | case 0x8a: 156 | modRM = insn->modrm.bytes[0]; 157 | return (X86_MODRM_MOD(modRM) == 3 && 158 | X86_REX_R_EQ_B(rex) && 159 | X86_MODRM_REG(modRM) == X86_MODRM_RM(modRM)); 160 | 161 | case 0x87: /* Group: "xchg/mov reg32/64, reg32/64" */ 162 | case 0x89: 163 | case 0x8b: 164 | modRM = insn->modrm.bytes[0]; 165 | /* We also require REX.W==1 because if xchg/mov work with 166 | * 32-bit registers in 64-bit mode, the higher parts of the 167 | * destination registers will be zeroed => this is not a 168 | * no-op. See Intel Software Developer's Manual Vol1, 169 | * section 3.4.1.1, 170 | * "General-Purpose Registers in 64-Bit Mode". */ 171 | return (X86_REX_W(rex) && 172 | X86_MODRM_MOD(modRM) == 3 && 173 | X86_REX_R_EQ_B(rex) && 174 | X86_MODRM_REG(modRM) == X86_MODRM_RM(modRM)); 175 | 176 | case 0x0f: /* Group: "0f 1f /0, multi-byte nop" */ 177 | modRM = insn->modrm.bytes[0]; 178 | /* struct insn is filled with all 0s during initialization, 179 | * so we don't need to check insn->opcode.nbytes == 2. */ 180 | return (insn->opcode.bytes[1] == 0x1f && 181 | X86_MODRM_REG(modRM) == 0); 182 | 183 | /* Note that we do not consider 0f 0d /[0|1] as a nop. Intel 184 | * manuals mention it as "NOP Ev" only in the opcode table 185 | * but not in the description of NOP instructions. AMD uses 186 | * this opcode for prefetch instructions rather than nop. */ 187 | 188 | case 0x8d: /* Group: "lea" */ 189 | return insn_lea_is_noop(insn, rex); 190 | 191 | default: break; 192 | } 193 | return 0; /* unknown or not a no-op */ 194 | } 195 | 196 | int 197 | insn_is_mem_read(struct insn *insn) 198 | { 199 | struct insn_field *modrm = &insn->modrm; 200 | int modrm_mem = 1; 201 | unsigned int attr; 202 | 203 | /* Besides decoding Mod R/M field, this will make all the attributes 204 | * of the instruction available for querying (if they are not yet 205 | * available). */ 206 | if (!modrm->got) 207 | insn_get_modrm(insn); 208 | 209 | if (inat_has_modrm(insn->attr)) 210 | modrm_mem = (X86_MODRM_MOD(modrm->value) != 3); 211 | 212 | attr = insn->attr.attributes; 213 | return ((attr & INAT_MEM_CAN_READ) && modrm_mem); 214 | } 215 | 216 | int 217 | insn_is_mem_write(struct insn *insn) 218 | { 219 | struct insn_field *modrm = &insn->modrm; 220 | int modrm_mem = 1; 221 | unsigned int attr; 222 | 223 | /* Besides decoding Mod R/M field, this will make all the attributes 224 | * of the instruction available for querying (if they are not yet 225 | * available). */ 226 | if (!modrm->got) 227 | insn_get_modrm(insn); 228 | 229 | if (inat_has_modrm(insn->attr)) 230 | modrm_mem = (X86_MODRM_MOD(modrm->value) != 3); 231 | 232 | attr = insn->attr.attributes; 233 | return ((attr & INAT_MEM_CAN_WRITE) && modrm_mem); 234 | } 235 | 236 | int 237 | is_insn_movbe(struct insn *insn) 238 | { 239 | unsigned char *opcode = insn->opcode.bytes; 240 | 241 | /* We need to check the prefix to distinguish MOVBE from CRC32 insn, 242 | * they have the same opcode. */ 243 | if (insn_has_prefix(insn, 0xf2)) 244 | return 0; 245 | 246 | /* MOVBE: 0F 38 F0 and 0F 38 F1 */ 247 | return (opcode[0] == 0x0f && opcode[1] == 0x38 && 248 | (opcode[2] == 0xf0 || opcode[2] == 0xf1)); 249 | } 250 | 251 | /* insn_is_locked_op() - Check if the instruction is a locked operation. 252 | * 253 | * The function returns nonzero if the instruction is XCHG reg,mem or has 254 | * LOCK prefix, 0 otherwise. 255 | * 256 | * For details, see Intel Software Developer's Manual vol.3A, section 257 | * 8.1, "Locked Atomic Operations". 258 | * 259 | * If necessary, the function decodes the instruction up to (and including) 260 | * Mod R/M byte first. */ 261 | static int 262 | insn_is_locked_op(struct insn *insn) 263 | { 264 | unsigned char opcode; 265 | unsigned char mod; 266 | 267 | insn_get_modrm(insn); 268 | opcode = insn->opcode.bytes[0]; 269 | mod = (unsigned char)X86_MODRM_MOD(insn->modrm.value); 270 | 271 | return (insn_has_prefix(insn, 0xf0) || 272 | ((opcode == 0x86 || opcode == 0x87) && mod != 3)); 273 | } 274 | 275 | /* Returns non-zero if the memory addressing expression uses %rsp/%esp, 276 | * 0 otherwise. Note that only ModRM & SIB are taken into account here, and 277 | * the function returns 0 for the likes of PUSH/POP reg, etc. */ 278 | static int 279 | insn_is_sp_based_access(struct insn *insn) 280 | { 281 | unsigned int base; 282 | unsigned int rex; 283 | 284 | /* %rsp/%esp can only be the 'base' in SIB. */ 285 | insn_get_sib(insn); 286 | if (insn->sib.nbytes == 0) /* no SIB */ 287 | return 0; 288 | 289 | rex = insn->rex_prefix.value; 290 | base = X86_SIB_BASE(insn->sib.value); 291 | 292 | return (!X86_REX_B(rex) && base == 4 /* 100(b) */); 293 | } 294 | /* ====================================================================== */ 295 | 296 | int 297 | is_tracked_memory_access(struct insn *insn, enum EAccessType *atype, 298 | int with_stack, int with_locked) 299 | { 300 | static const enum EAccessType at[2][2] = { 301 | [0] = {AT_BOTH, AT_WRITE}, /* is_read = 0 */ 302 | [1] = {AT_READ, AT_BOTH} /* is_read = 1 */ 303 | }; 304 | int is_read; 305 | int is_write; 306 | 307 | insn_get_length(insn); /* Decode the insn, just in case */ 308 | is_read = insn_is_mem_read(insn) ? 1 : 0; 309 | is_write = insn_is_mem_write(insn) ? 1 : 0; 310 | 311 | if (!is_read && !is_write) 312 | return 0; 313 | 314 | /* Filter out indirect jumps and calls, we do not track these 315 | * memory accesses. */ 316 | if (is_insn_call_near_indirect(insn) || 317 | is_insn_jump_near_indirect(insn) || 318 | is_insn_call_far(insn) || is_insn_jump_far(insn)) 319 | return 0; 320 | 321 | if (insn_is_noop(insn)) 322 | return 0; 323 | 324 | if (!with_stack && insn_is_sp_based_access(insn)) 325 | return 0; 326 | 327 | if (!with_locked && insn_is_locked_op(insn)) 328 | return 0; 329 | 330 | /* Do not process memory accesses that use FS and GS segment 331 | * registers now. */ 332 | if (insn_has_prefix(insn, 0x64) || insn_has_prefix(insn, 0x65)) 333 | return 0; 334 | 335 | if (is_insn_type_e(insn) || is_insn_cmpxchg8b_16b(insn) || 336 | is_insn_movbe(insn)) { 337 | /* Note that MOVBE insn now has its attributes set correctly 338 | * by the decoder and its form writing to the memory is 339 | * recognized. No need to additionally process it here. */ 340 | goto mem_access_common; 341 | } 342 | 343 | /* String ops */ 344 | if (is_insn_type_x(insn) || is_insn_type_y(insn)) 345 | goto mem_access_common; 346 | 347 | if (is_insn_direct_offset_mov(insn) || is_insn_xlat(insn)) 348 | goto mem_access_common; 349 | 350 | return 0; 351 | 352 | mem_access_common: 353 | if (atype) 354 | *atype = at[is_read][is_write]; 355 | return 1; 356 | } 357 | /* ====================================================================== */ 358 | -------------------------------------------------------------------------------- /common/util.h: -------------------------------------------------------------------------------- 1 | #ifndef UTIL_H_1331_INCLUDED 2 | #define UTIL_H_1331_INCLUDED 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | #include 9 | 10 | /* Types of the memory accesses of interest */ 11 | enum EAccessType { 12 | AT_BOTH = 0, /* read and write */ 13 | AT_READ, /* read only */ 14 | AT_WRITE /* write only */ 15 | }; 16 | 17 | /* Returns nonzero, if the given instruction accesses memory and our system 18 | * may need to handle it, 0 otherwise. 19 | * Decodes the instruction if it is not decoded yet. 20 | * Returns the "possible" access type in '*atype'. "Possible" means the 21 | * instruction may access memory this way but is not required to do so (e.g. 22 | * CMOVcc). If that information is not needed, one may pass NULL as 'atype'. 23 | * 24 | * If 'with_stack' is nonzero %esp/%rsp-based accesses should also be 25 | * tracked, otherwise the function returns 0. 26 | * Same rules hold for 'with_locked' and locked operations. */ 27 | int is_tracked_memory_access(struct insn *insn, enum EAccessType *atype, 28 | int with_stack, int with_locked); 29 | 30 | #ifdef __KERNEL__ 31 | /* X86_ADDR_FROM_OFFSET() 32 | * 33 | * Calculate the memory address being the operand of a given instruction 34 | * that uses IP-relative addressing ('call near', 'jmp near', ...). 35 | * 'insn_addr' is the address of the instruction itself, 36 | * 'insn_len' is length of the instruction in bytes, 37 | * 'offset' is the offset of the destination address from the first byte 38 | * past the instruction. 39 | * 40 | * For x86-64 architecture, the offset value is sign-extended here first. 41 | * 42 | * "Intel x86 Instruction Set Reference" states the following 43 | * concerning 'call rel32': 44 | * 45 | * "Call near, relative, displacement relative to next instruction. 46 | * 32-bit displacement sign extended to 64 bits in 64-bit mode." */ 47 | #ifdef CONFIG_X86_64 48 | # define X86_ADDR_FROM_OFFSET(insn_addr, insn_len, offset) \ 49 | (void *)((__s64)(insn_addr) + (__s64)(insn_len) + \ 50 | (__s64)(__s32)(offset)) 51 | 52 | #else /* CONFIG_X86_32 */ 53 | # define X86_ADDR_FROM_OFFSET(insn_addr, insn_len, offset) \ 54 | (void *)((__u32)(insn_addr) + (__u32)(insn_len) + (__u32)(offset)) 55 | #endif 56 | 57 | /* X86_OFFSET_FROM_ADDR() 58 | * 59 | * The reverse of X86_ADDR_FROM_OFFSET: calculates the offset value 60 | * to be used in an instruction given the address and length of the 61 | * instruction and the destination address it must refer to. */ 62 | #define X86_OFFSET_FROM_ADDR(insn_addr, insn_len, dest_addr) \ 63 | (__u32)((unsigned long)(dest_addr) - \ 64 | ((unsigned long)(insn_addr) + (__u32)insn_len)) 65 | #endif /* __KERNEL__ */ 66 | 67 | /* X86_SIGN_EXTEND_V32() 68 | * 69 | * Just a cast to unsigned long on x86-32. 70 | * On x86-64, sign-extends a 32-bit value to and casts the result to 71 | * unsigned long. */ 72 | #define X86_SIGN_EXTEND_V32(val) ((unsigned long)(long)(__s32)(val)) 73 | /* ====================================================================== */ 74 | 75 | /* Query memory access type for the instructions. 76 | * Note that these functions *do* apply to string insns, xlat, etc., rather 77 | * than only to the insns with Mod R/M. */ 78 | 79 | /* Nonzero if the instruction reads data from memory, 0 otherwise. 80 | * The function decodes the relevant parts of the instruction if needed. */ 81 | int insn_is_mem_read(struct insn *insn); 82 | 83 | /* Nonzero if the instruction writes data to memory, 0 otherwise. 84 | * The function decodes the relevant parts of the instruction if needed. */ 85 | int insn_is_mem_write(struct insn *insn); 86 | /* ====================================================================== */ 87 | 88 | /* Check if the insn is MOVBE. */ 89 | int is_insn_movbe(struct insn *insn); 90 | 91 | /* Checks if the instruction has addressing method (type) E and its Mod R/M 92 | * expression refers to memory. 93 | * 94 | * [NB] CMPXCHG, SETcc, etc. also have type E and will be reported by this 95 | * function as such. */ 96 | static inline int 97 | is_insn_type_e(struct insn *insn) 98 | { 99 | insn_attr_t *attr = &insn->attr; 100 | unsigned char modrm = insn->modrm.bytes[0]; 101 | 102 | return ((attr->addr_method1 == INAT_AMETHOD_E || 103 | attr->addr_method2 == INAT_AMETHOD_E) && 104 | X86_MODRM_MOD(modrm) != 3); 105 | } 106 | 107 | static inline int 108 | is_insn_xlat(struct insn *insn) 109 | { 110 | unsigned char *opcode = insn->opcode.bytes; 111 | 112 | /* XLAT: D7 */ 113 | return (opcode[0] == 0xd7); 114 | } 115 | 116 | static inline int 117 | is_insn_direct_offset_mov(struct insn *insn) 118 | { 119 | unsigned char *opcode = insn->opcode.bytes; 120 | 121 | /* Direct memory offset MOVs: A0-A3 */ 122 | return (opcode[0] >= 0xa0 && opcode[0] <= 0xa3); 123 | } 124 | 125 | /* Opcode: FF/2 */ 126 | static inline int 127 | is_insn_call_near_indirect(struct insn *insn) 128 | { 129 | return (insn->opcode.bytes[0] == 0xff && 130 | X86_MODRM_REG(insn->modrm.bytes[0]) == 2); 131 | } 132 | 133 | /* Opcode: FF/4 */ 134 | static inline int 135 | is_insn_jump_near_indirect(struct insn *insn) 136 | { 137 | return (insn->opcode.bytes[0] == 0xff && 138 | X86_MODRM_REG(insn->modrm.bytes[0]) == 4); 139 | } 140 | 141 | /* Opcodes: FF/3 or 9A */ 142 | static inline int 143 | is_insn_call_far(struct insn *insn) 144 | { 145 | unsigned char opcode = insn->opcode.bytes[0]; 146 | unsigned char modrm = insn->modrm.bytes[0]; 147 | 148 | return (opcode == 0x9a || 149 | (opcode == 0xff && X86_MODRM_REG(modrm) == 3)); 150 | } 151 | 152 | /* Opcodes: FF/5 or EA */ 153 | static inline int 154 | is_insn_jump_far(struct insn *insn) 155 | { 156 | unsigned char opcode = insn->opcode.bytes[0]; 157 | unsigned char modrm = insn->modrm.bytes[0]; 158 | 159 | return (opcode == 0xea || 160 | (opcode == 0xff && X86_MODRM_REG(modrm) == 5)); 161 | } 162 | 163 | static inline int 164 | is_insn_cmpxchg(struct insn *insn) 165 | { 166 | unsigned char *opcode = insn->opcode.bytes; 167 | 168 | /* CMPXCHG: 0F B0 and 0F B1 */ 169 | return (opcode[0] == 0x0f && 170 | (opcode[1] == 0xb0 || opcode[1] == 0xb1)); 171 | } 172 | 173 | static inline int 174 | is_insn_cmpxchg8b_16b(struct insn *insn) 175 | { 176 | unsigned char *opcode = insn->opcode.bytes; 177 | unsigned char modrm = insn->modrm.bytes[0]; 178 | 179 | /* CMPXCHG8B/CMPXCHG16B: 0F C7 /1 */ 180 | return (opcode[0] == 0x0f && opcode[1] == 0xc7 && 181 | X86_MODRM_REG(modrm) == 1); 182 | } 183 | 184 | static inline int 185 | is_insn_type_x(struct insn *insn) 186 | { 187 | insn_attr_t *attr = &insn->attr; 188 | return (attr->addr_method1 == INAT_AMETHOD_X || 189 | attr->addr_method2 == INAT_AMETHOD_X); 190 | } 191 | 192 | static inline int 193 | is_insn_type_y(struct insn *insn) 194 | { 195 | insn_attr_t *attr = &insn->attr; 196 | return (attr->addr_method1 == INAT_AMETHOD_Y || 197 | attr->addr_method2 == INAT_AMETHOD_Y); 198 | } 199 | 200 | static inline int 201 | is_insn_cmovcc(struct insn *insn) 202 | { 203 | unsigned char *opcode = insn->opcode.bytes; 204 | unsigned char modrm = insn->modrm.bytes[0]; 205 | 206 | /* CMOVcc: 0F 40 - 0F 4F */ 207 | return (opcode[0] == 0x0f && 208 | ((opcode[1] & 0xf0) == 0x40) && 209 | X86_MODRM_MOD(modrm) != 3); 210 | } 211 | /* ====================================================================== */ 212 | 213 | #ifdef __cplusplus 214 | } 215 | #endif 216 | /* ====================================================================== */ 217 | 218 | #endif /*UTIL_H_1331_INCLUDED*/ 219 | -------------------------------------------------------------------------------- /config.h.in: -------------------------------------------------------------------------------- 1 | #ifndef RH_CONFIG_H_1456_INCLUDED 2 | #define RH_CONFIG_H_1456_INCLUDED 3 | 4 | #define RH_PACKAGE_NAME "@RH_PACKAGE_NAME@" 5 | #define RH_PACKAGE_VERSION "@RH_PACKAGE_VERSION@" 6 | 7 | #endif /* RH_CONFIG_H_1456_INCLUDED */ 8 | -------------------------------------------------------------------------------- /core/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(kmodule_name "racehound") 2 | 3 | kbuild_include_directories( 4 | "${CMAKE_SOURCE_DIR}" 5 | "${CMAKE_BINARY_DIR}" 6 | "${CMAKE_CURRENT_SOURCE_DIR}" 7 | ) 8 | 9 | kbuild_add_module(${kmodule_name} 10 | "racehound_main.c" 11 | "insn_analysis.c" 12 | "insn_analysis.h" 13 | "stackdepot.c" 14 | "stackdepot.h" 15 | "thunks.S" 16 | "${COMMON_SOURCE_DIR}/inat.c" 17 | "${COMMON_SOURCE_DIR}/inat.h" 18 | "${COMMON_SOURCE_DIR}/insn.c" 19 | "${COMMON_SOURCE_DIR}/insn.h" 20 | "${COMMON_SOURCE_DIR}/inat_types.h" 21 | "${COMMON_SOURCE_DIR}/util.c" 22 | "${COMMON_SOURCE_DIR}/util.h" 23 | ) 24 | 25 | add_dependencies(${kmodule_name} insn_decoder_headers) 26 | 27 | rh_install_kmodule(${kmodule_name}) 28 | 29 | # Testing 30 | add_subdirectory(tests) 31 | -------------------------------------------------------------------------------- /core/insn_analysis.h: -------------------------------------------------------------------------------- 1 | /* A subset of the API for the instruction decoder as well as other 2 | * functions RaceHound needs to analyze the machine instructions. 3 | * 4 | * We use a local copy of the instruction decoder here (the in-kernel one 5 | * does not export its API to modules). However, the kernel API changes 6 | * from time to time. 7 | * 8 | * To avoid problems when the headers of the in-kernel decoder are included 9 | * but the local version assumes a different API is used, the API is 10 | * encapsulated here. 11 | * 12 | * DO NOT #include asm/insn.h and asm/inat.h in decoder.* files (but you may 13 | * #include these in other files). Same for the headers that may eventually 14 | * #include these, like , for example. 15 | * 16 | * If you need to use the local copy of the decoder, please rely on the API 17 | * declared in this file (decoder.h).*/ 18 | 19 | #ifndef INSN_ANALYSIS_H_1039_INCLUDED 20 | #define INSN_ANALYSIS_H_1039_INCLUDED 21 | /* ====================================================================== */ 22 | 23 | /* A wrapper around struct insn. */ 24 | struct rh_insn; 25 | 26 | /* kmalloc + kernel_insn_init(). Cannot be used in atomic context. 27 | * Pass the returned pointer to kfree when it is no longer needed/ 28 | * 29 | * Length of the buffer that starts from 'kaddr' and contains the insn to 30 | * decode is assumed to be no less than MAX_INSN_SIZE. This is the case for 31 | * the insn slots provided by Kprobes, for example. */ 32 | struct rh_insn * 33 | rh_insn_create(const void *kaddr); 34 | 35 | /* insn_get_length() + return insn.length */ 36 | unsigned int 37 | rh_insn_get_length(struct rh_insn *rh_insn); 38 | 39 | /* Equivalent to is_tracked_memory_access(&insn, NULL, 1, 1). */ 40 | int 41 | rh_should_process_insn(struct rh_insn *rh_insn); 42 | 43 | /* As of kernel 4.0-rc3, Kprobes consider some insns we are interested in 44 | * not boostable. Examples: some forms of TEST, INC/DEC, etc. Not sure why 45 | * Kprobes do so. For the purposes of RaceHound, let us suppose they are 46 | * boostable and handle them as such. 47 | * 48 | * [NB] 'boostable' means a jump to the next insn in the original 49 | * code can be placed right after the copied insn in the Kprobe's slot. If 50 | * the insn has no post-handler, this jump could be used to avoid single- 51 | * stepping with all its overhead. 52 | * We need to place jumps there too, but the jumps to rh_thunk_post() in 53 | * this case. So, similar restrictions on the insns apply. */ 54 | int 55 | rh_special_boostable(struct rh_insn *rh_insn); 56 | /* ====================================================================== */ 57 | 58 | /* Returns size of the memory area the insn can access. For the string 59 | * operations (MOVS, CMPS, ...) - size of an element accessed at a time. 60 | * The insn must be already decoded. 61 | * 62 | * Do not call this function for the insns is_tracked_memory_access() would 63 | * return 0 for. 64 | * 65 | * The function returns 0 in case of an error. */ 66 | unsigned int 67 | rh_get_base_size(struct rh_insn *rh_insn); 68 | /* ====================================================================== */ 69 | 70 | struct pt_regs; 71 | 72 | /* Information about the memory access that is about to happen. */ 73 | struct rh_ma_info 74 | { 75 | void * addr; /* start address */ 76 | unsigned int size; /* size of the memory area, in bytes */ 77 | int is_write; /* 0 - read, nonzero - write or read+write*/ 78 | }; 79 | 80 | /* Fills in the fields of the given struct rh_ma_info instance using the 81 | * decoded instruction ('rh_insn') and the values of the registers right 82 | * before the insn is executed ('regs'). 83 | * 84 | * 'base_size' - see the description of rh_get_base_size(). 85 | * 86 | * Returns 0 if successful, a negative error code otherwise. 87 | * 88 | * If the insn does not access memory (e.g. CMOVcc when the condition is not 89 | * true), the function sets mi->addr to NULL and returns 0. This is not an 90 | * error. */ 91 | int 92 | rh_fill_ma_info(struct rh_ma_info *mi /* out */, struct rh_insn *rh_insn, 93 | struct pt_regs *regs, unsigned int base_size); 94 | /* ====================================================================== */ 95 | #endif /* INSN_ANALYSIS_H_1039_INCLUDED */ 96 | -------------------------------------------------------------------------------- /core/stackdepot.c: -------------------------------------------------------------------------------- 1 | /* This code was copied from kernel 4.12 with minimal changes made to 2 | * build it into our kernel module and to be able to free the occupied 3 | * memory. */ 4 | 5 | /* 6 | * Generic stack depot for storing stack traces. 7 | * 8 | * Some debugging tools need to save stack traces of certain events which can 9 | * be later presented to the user. For example, KASAN needs to safe alloc and 10 | * free stacks for each object, but storing two stack traces per object 11 | * requires too much memory (e.g. SLUB_DEBUG needs 256 bytes per object for 12 | * that). 13 | * 14 | * Instead, stack depot maintains a hashtable of unique stacktraces. Since alloc 15 | * and free stacks repeat a lot, we save about 100x space. 16 | * Stacks are never removed from depot, so we store them contiguously one after 17 | * another in a contiguos memory allocation. 18 | * 19 | * Author: Alexander Potapenko 20 | * Copyright (C) 2016 Google, Inc. 21 | * 22 | * Based on code by Dmitry Chernenkov. 23 | * 24 | * This program is free software; you can redistribute it and/or 25 | * modify it under the terms of the GNU General Public License 26 | * version 2 as published by the Free Software Foundation. 27 | * 28 | * This program is distributed in the hope that it will be useful, but 29 | * WITHOUT ANY WARRANTY; without even the implied warranty of 30 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 31 | * General Public License for more details. 32 | * 33 | */ 34 | 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | 46 | #include "stackdepot.h" 47 | 48 | #define DEPOT_STACK_BITS (sizeof(depot_stack_handle_t) * 8) 49 | 50 | #define STACK_ALLOC_NULL_PROTECTION_BITS 1 51 | #define STACK_ALLOC_ORDER 2 /* 'Slab' size order for stack depot, 4 pages */ 52 | #define STACK_ALLOC_SIZE (1LL << (PAGE_SHIFT + STACK_ALLOC_ORDER)) 53 | #define STACK_ALLOC_ALIGN 4 54 | #define STACK_ALLOC_OFFSET_BITS (STACK_ALLOC_ORDER + PAGE_SHIFT - \ 55 | STACK_ALLOC_ALIGN) 56 | #define STACK_ALLOC_INDEX_BITS (DEPOT_STACK_BITS - \ 57 | STACK_ALLOC_NULL_PROTECTION_BITS - STACK_ALLOC_OFFSET_BITS) 58 | #define STACK_ALLOC_SLABS_CAP 8192 59 | #define STACK_ALLOC_MAX_SLABS \ 60 | (((1LL << (STACK_ALLOC_INDEX_BITS)) < STACK_ALLOC_SLABS_CAP) ? \ 61 | (1LL << (STACK_ALLOC_INDEX_BITS)) : STACK_ALLOC_SLABS_CAP) 62 | 63 | /* The compact structure to store the reference to stacks. */ 64 | union handle_parts { 65 | depot_stack_handle_t handle; 66 | struct { 67 | u32 slabindex : STACK_ALLOC_INDEX_BITS; 68 | u32 offset : STACK_ALLOC_OFFSET_BITS; 69 | u32 valid : STACK_ALLOC_NULL_PROTECTION_BITS; 70 | }; 71 | }; 72 | 73 | struct stack_record { 74 | struct stack_record *next; /* Link in the hashtable */ 75 | u32 hash; /* Hash in the hastable */ 76 | u32 size; /* Number of frames in the stack */ 77 | union handle_parts handle; 78 | unsigned long entries[1]; /* Variable-sized array of entries. */ 79 | }; 80 | 81 | static void *stack_slabs[STACK_ALLOC_MAX_SLABS]; 82 | 83 | static int depot_index; 84 | static int next_slab_inited; 85 | static size_t depot_offset; 86 | static DEFINE_SPINLOCK(depot_lock); 87 | 88 | static bool init_stack_slab(void **prealloc) 89 | { 90 | if (!*prealloc) 91 | return false; 92 | /* 93 | * This smp_load_acquire() pairs with smp_store_release() to 94 | * |next_slab_inited| below and in depot_alloc_stack(). 95 | */ 96 | if (smp_load_acquire(&next_slab_inited)) 97 | return true; 98 | if (stack_slabs[depot_index] == NULL) { 99 | stack_slabs[depot_index] = *prealloc; 100 | } else { 101 | stack_slabs[depot_index + 1] = *prealloc; 102 | /* 103 | * This smp_store_release pairs with smp_load_acquire() from 104 | * |next_slab_inited| above and in depot_save_stack(). 105 | */ 106 | smp_store_release(&next_slab_inited, 1); 107 | } 108 | *prealloc = NULL; 109 | return true; 110 | } 111 | 112 | /* Allocation of a new stack in raw storage */ 113 | static struct stack_record *depot_alloc_stack(unsigned long *entries, int size, 114 | u32 hash, void **prealloc, gfp_t alloc_flags) 115 | { 116 | int required_size = offsetof(struct stack_record, entries) + 117 | sizeof(unsigned long) * size; 118 | struct stack_record *stack; 119 | 120 | required_size = ALIGN(required_size, 1 << STACK_ALLOC_ALIGN); 121 | 122 | if (unlikely(depot_offset + required_size > STACK_ALLOC_SIZE)) { 123 | if (unlikely(depot_index + 1 >= STACK_ALLOC_MAX_SLABS)) { 124 | WARN_ONCE(1, "Stack depot reached limit capacity"); 125 | return NULL; 126 | } 127 | depot_index++; 128 | depot_offset = 0; 129 | /* 130 | * smp_store_release() here pairs with smp_load_acquire() from 131 | * |next_slab_inited| in depot_save_stack() and 132 | * init_stack_slab(). 133 | */ 134 | if (depot_index + 1 < STACK_ALLOC_MAX_SLABS) 135 | smp_store_release(&next_slab_inited, 0); 136 | } 137 | init_stack_slab(prealloc); 138 | if (stack_slabs[depot_index] == NULL) 139 | return NULL; 140 | 141 | stack = stack_slabs[depot_index] + depot_offset; 142 | 143 | stack->hash = hash; 144 | stack->size = size; 145 | stack->handle.slabindex = depot_index; 146 | stack->handle.offset = depot_offset >> STACK_ALLOC_ALIGN; 147 | stack->handle.valid = 1; 148 | memcpy(stack->entries, entries, size * sizeof(unsigned long)); 149 | depot_offset += required_size; 150 | 151 | return stack; 152 | } 153 | 154 | #define STACK_HASH_ORDER 20 155 | #define STACK_HASH_SIZE (1L << STACK_HASH_ORDER) 156 | #define STACK_HASH_MASK (STACK_HASH_SIZE - 1) 157 | #define STACK_HASH_SEED 0x9747b28c 158 | 159 | static struct stack_record *stack_table[STACK_HASH_SIZE] = { 160 | [0 ... STACK_HASH_SIZE - 1] = NULL 161 | }; 162 | 163 | /* Calculate hash for a stack */ 164 | static inline u32 hash_stack(unsigned long *entries, unsigned int size) 165 | { 166 | return jhash2((u32 *)entries, 167 | size * sizeof(unsigned long) / sizeof(u32), 168 | STACK_HASH_SEED); 169 | } 170 | 171 | /* Find a stack that is equal to the one stored in entries in the hash */ 172 | static inline struct stack_record *find_stack(struct stack_record *bucket, 173 | unsigned long *entries, int size, 174 | u32 hash) 175 | { 176 | struct stack_record *found; 177 | 178 | for (found = bucket; found; found = found->next) { 179 | if (found->hash == hash && 180 | found->size == size && 181 | !memcmp(entries, found->entries, 182 | size * sizeof(unsigned long))) { 183 | return found; 184 | } 185 | } 186 | return NULL; 187 | } 188 | 189 | void depot_fetch_stack(depot_stack_handle_t handle, struct stack_trace *trace) 190 | { 191 | union handle_parts parts = { .handle = handle }; 192 | void *slab; 193 | size_t offset; 194 | struct stack_record *stack; 195 | 196 | slab = stack_slabs[parts.slabindex]; 197 | if (!slab) /* just in case we have cleaned it up already */ 198 | return; 199 | 200 | offset = parts.offset << STACK_ALLOC_ALIGN; 201 | stack = slab + offset; 202 | 203 | trace->nr_entries = trace->max_entries = stack->size; 204 | trace->entries = stack->entries; 205 | trace->skip = 0; 206 | } 207 | 208 | /** 209 | * depot_save_stack - save stack in a stack depot. 210 | * @trace - the stacktrace to save. 211 | * @alloc_flags - flags for allocating additional memory if required. 212 | * 213 | * Returns the handle of the stack struct stored in depot. 214 | */ 215 | depot_stack_handle_t depot_save_stack(struct stack_trace *trace, 216 | gfp_t alloc_flags) 217 | { 218 | u32 hash; 219 | depot_stack_handle_t retval = 0; 220 | struct stack_record *found = NULL, **bucket; 221 | unsigned long flags; 222 | struct page *page = NULL; 223 | void *prealloc = NULL; 224 | 225 | if (unlikely(trace->nr_entries == 0)) 226 | goto fast_exit; 227 | 228 | hash = hash_stack(trace->entries, trace->nr_entries); 229 | bucket = &stack_table[hash & STACK_HASH_MASK]; 230 | 231 | /* 232 | * Fast path: look the stack trace up without locking. 233 | * The smp_load_acquire() here pairs with smp_store_release() to 234 | * |bucket| below. 235 | */ 236 | found = find_stack(smp_load_acquire(bucket), trace->entries, 237 | trace->nr_entries, hash); 238 | if (found) 239 | goto exit; 240 | 241 | /* 242 | * Check if the current or the next stack slab need to be initialized. 243 | * If so, allocate the memory - we won't be able to do that under the 244 | * lock. 245 | * 246 | * The smp_load_acquire() here pairs with smp_store_release() to 247 | * |next_slab_inited| in depot_alloc_stack() and init_stack_slab(). 248 | */ 249 | if (unlikely(!smp_load_acquire(&next_slab_inited))) { 250 | /* 251 | * Zero out zone modifiers, as we don't have specific zone 252 | * requirements. Keep the flags related to allocation in atomic 253 | * contexts and I/O. 254 | */ 255 | alloc_flags &= ~GFP_ZONEMASK; 256 | alloc_flags &= (GFP_ATOMIC | GFP_KERNEL); 257 | alloc_flags |= __GFP_NOWARN; 258 | page = alloc_pages(alloc_flags, STACK_ALLOC_ORDER); 259 | if (page) 260 | prealloc = page_address(page); 261 | } 262 | 263 | spin_lock_irqsave(&depot_lock, flags); 264 | 265 | found = find_stack(*bucket, trace->entries, trace->nr_entries, hash); 266 | if (!found) { 267 | struct stack_record *new = 268 | depot_alloc_stack(trace->entries, trace->nr_entries, 269 | hash, &prealloc, alloc_flags); 270 | if (new) { 271 | new->next = *bucket; 272 | /* 273 | * This smp_store_release() pairs with 274 | * smp_load_acquire() from |bucket| above. 275 | */ 276 | smp_store_release(bucket, new); 277 | found = new; 278 | } 279 | } else if (prealloc) { 280 | /* 281 | * We didn't need to store this stack trace, but let's keep 282 | * the preallocated memory for the future. 283 | */ 284 | WARN_ON(!init_stack_slab(&prealloc)); 285 | } 286 | 287 | spin_unlock_irqrestore(&depot_lock, flags); 288 | exit: 289 | if (prealloc) { 290 | /* Nobody used this memory, ok to free it. */ 291 | free_pages((unsigned long)prealloc, STACK_ALLOC_ORDER); 292 | } 293 | if (found) 294 | retval = found->handle.handle; 295 | fast_exit: 296 | return retval; 297 | } 298 | 299 | /* 300 | * Free the allocated memory and mark the slabs uninitialized. All 301 | * references to the stacks stored in the depot will become invalid. 302 | * 303 | * The caller must ensure that no other stackdepot function is called 304 | * while cleanup is in progress. 305 | */ 306 | void depot_cleanup(void) 307 | { 308 | unsigned int i; 309 | 310 | memset(stack_table, 0, sizeof(stack_table)); 311 | for (i = 0; i < STACK_ALLOC_MAX_SLABS; ++i) 312 | free_pages((unsigned long)stack_slabs[i], STACK_ALLOC_ORDER); 313 | memset(stack_slabs, 0, sizeof(stack_slabs)); 314 | } 315 | -------------------------------------------------------------------------------- /core/stackdepot.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This header file was copied from kernel 4.12. If the kernel has its 3 | * stackdepot.h, it is likely that that file will be used, otherwise, 4 | * here is the fallback. 5 | * 6 | * Do not change the declarations in this file, they are likely to be 7 | * ignored by the build processes. 8 | */ 9 | 10 | /* 11 | * A generic stack depot implementation 12 | * 13 | * Author: Alexander Potapenko 14 | * Copyright (C) 2016 Google, Inc. 15 | * 16 | * Based on code by Dmitry Chernenkov. 17 | * 18 | * This program is free software; you can redistribute it and/or modify 19 | * it under the terms of the GNU General Public License as published by 20 | * the Free Software Foundation; either version 2 of the License, or 21 | * (at your option) any later version. 22 | * 23 | * This program is distributed in the hope that it will be useful, 24 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 25 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 26 | * GNU General Public License for more details. 27 | * 28 | */ 29 | 30 | #ifndef _LINUX_STACKDEPOT_H 31 | #define _LINUX_STACKDEPOT_H 32 | 33 | typedef u32 depot_stack_handle_t; 34 | 35 | struct stack_trace; 36 | 37 | depot_stack_handle_t depot_save_stack(struct stack_trace *trace, gfp_t flags); 38 | 39 | void depot_fetch_stack(depot_stack_handle_t handle, struct stack_trace *trace); 40 | 41 | #endif 42 | -------------------------------------------------------------------------------- /core/tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Build the test modules with debug info. 2 | kbuild_add_definitions("-g") 3 | 4 | add_subdirectory(simple) 5 | add_subdirectory(common_target) 6 | add_subdirectory(basics) 7 | -------------------------------------------------------------------------------- /core/tests/basics/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Prepare the lists of breakpoints for testing 2 | set(BP_FILE_SIMPLE "${CMAKE_BINARY_DIR}/core/tests/simple/test_simple-bps.list") 3 | set(BP_FILE_COMMON "${CMAKE_BINARY_DIR}/core/tests/common_target/test_common_target-bps.list") 4 | 5 | add_custom_command( 6 | OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/breakpoints-all.list" 7 | COMMAND cat "${BP_FILE_SIMPLE}" "${BP_FILE_COMMON}" > "${CMAKE_CURRENT_BINARY_DIR}/breakpoints-all.list" 8 | DEPENDS 9 | "${BP_FILE_SIMPLE}" 10 | "${BP_FILE_COMMON}" 11 | ) 12 | add_custom_command( 13 | OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/breakpoints-single.list" 14 | COMMAND cp -f "${BP_FILE_SIMPLE}" "${CMAKE_CURRENT_BINARY_DIR}/breakpoints-single.list" 15 | DEPENDS "${BP_FILE_SIMPLE}" 16 | ) 17 | add_custom_target(basics-bp-lists 18 | DEPENDS 19 | "${CMAKE_CURRENT_BINARY_DIR}/breakpoints-all.list" 20 | "${CMAKE_CURRENT_BINARY_DIR}/breakpoints-single.list" 21 | ) 22 | # just in case 23 | add_dependencies(basics-bp-lists "test_simple-bps" "test_common_target-bps") 24 | 25 | rh_test_add_target("basics-bp-lists") 26 | 27 | # Configure the test scripts 28 | configure_file("${CMAKE_CURRENT_SOURCE_DIR}/test.sh.in" 29 | "${CMAKE_CURRENT_BINARY_DIR}/test.sh" 30 | @ONLY) 31 | 32 | configure_file("${CMAKE_CURRENT_SOURCE_DIR}/test_active.sh.in" 33 | "${CMAKE_CURRENT_BINARY_DIR}/test_active.sh" 34 | @ONLY) 35 | 36 | configure_file("${CMAKE_CURRENT_SOURCE_DIR}/test_modules.sh.in" 37 | "${CMAKE_CURRENT_BINARY_DIR}/test_modules.sh" 38 | @ONLY) 39 | 40 | # A single breakpoint in hello_plus(), the insn reads from memory. 41 | rh_test_add_script("racehound.basics.01" "test.sh") 42 | 43 | # 2 breakpoints in hello_plus(), read and then write. 44 | rh_test_add_script("racehound.basics.02" "test.sh" "--all") 45 | 46 | # Same as "racehound.basics.02" but the read and write are executed in 47 | # atomic context now. 48 | rh_test_add_script("racehound.basics.03" "test.sh" "--all" "--atomic" ) 49 | 50 | # Attaching RaceHound to the target and detaching from it while the 51 | # target works actively. 52 | rh_test_add_script("racehound.active.01" "test_active.sh") 53 | 54 | # Test how RaceHound handles loading/unloading/reloading of the modules. 55 | rh_test_add_script("racehound.modules.01" "test_modules.sh") 56 | -------------------------------------------------------------------------------- /core/tests/basics/minus.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | echo "minus: $$" 4 | 5 | for i in `seq 200` 6 | do 7 | echo "-" > /sys/kernel/debug/rfindertest/hello 8 | sleep 0.005s 9 | done 10 | -------------------------------------------------------------------------------- /core/tests/basics/minus_infinite.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | echo "$0: $$" 4 | 5 | while true; do 6 | echo "-" > /sys/kernel/debug/rfindertest/hello 7 | sleep 0.01s 8 | done 9 | -------------------------------------------------------------------------------- /core/tests/basics/plus.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | echo "plus: $$" 4 | 5 | for i in `seq 200` 6 | do 7 | echo "+" > /sys/kernel/debug/rfindertest/hello 8 | sleep 0.005s 9 | done 10 | -------------------------------------------------------------------------------- /core/tests/basics/plus_infinite.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | echo "$0: $$" 4 | 5 | while true; do 6 | echo "+" > /sys/kernel/debug/rfindertest/hello 7 | sleep 0.01s 8 | done 9 | -------------------------------------------------------------------------------- /core/tests/basics/test.sh.in: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | ######################################################################## 4 | # This test checks if RaceHound finds simple races in a a kernel module. 5 | # 6 | # Usage: 7 | # sh test.sh [--all] [--atomic] 8 | # 9 | # Without "--all", the SW BPs are set only for the instruction reading 10 | # the variable in hello_plus() function of the target. With "--all" - for 11 | # both the reading and the writing of the variable. 12 | # 13 | # If "--atomic" is specified, the operations on that variable are 14 | # performed in atomic context in hello_plus() (namely, under spinlock). 15 | ######################################################################## 16 | 17 | # Just in case the tools like insmod/rmmod are not in their usual locations. 18 | export PATH=$PATH:/sbin:/bin:/usr/bin:/usr/sbin 19 | ######################################################################## 20 | 21 | # A function to check prerequisites: whether the necessary files exist, 22 | # etc. 23 | checkPrereqs() 24 | { 25 | if test ! -f "${CORE_MODULE}"; then 26 | printf "The core module is missing: ${CORE_MODULE}\n" 27 | exit 1 28 | fi 29 | 30 | if test ! -f "${TARGET_MODULE}"; then 31 | printf "The target module is missing: ${TARGET_MODULE}\n" 32 | exit 1 33 | fi 34 | 35 | if test ! -f "${BPS_FILE}"; then 36 | printf "The list of breakpoints is missing: ${BPS_FILE}\n" 37 | fi 38 | } 39 | ######################################################################## 40 | 41 | cleanupTargets() 42 | { 43 | printf "Cleaning up the target(s)...\n" 44 | 45 | lsmod | grep "${TARGET_NAME}" > /dev/null 2>&1 46 | if test $? -eq 0; then 47 | rmmod "${TARGET_NAME}" 48 | fi 49 | } 50 | ######################################################################## 51 | 52 | # Cleanup function 53 | cleanupAll() 54 | { 55 | cd "${WORK_DIR}" 56 | 57 | cleanupTargets 58 | 59 | lsmod | grep "${CORE_MODULE_NAME}" > /dev/null 2>&1 60 | if test $? -eq 0; then 61 | rmmod "${CORE_MODULE_NAME}" 62 | fi 63 | } 64 | ######################################################################## 65 | 66 | loadTarget() 67 | { 68 | insmod "${TARGET_MODULE}" "${TEST_ATOMIC_PARAM}" 69 | if test $? -ne 0; then 70 | printf "Failed to load the target module: ${TARGET_MODULE}\n" 71 | cleanupAll 72 | exit 1 73 | fi 74 | } 75 | 76 | unloadTarget() 77 | { 78 | lsmod | grep "${TARGET_NAME}" > /dev/null 2>&1 79 | if test $? -eq 0; then 80 | rmmod "${TARGET_NAME}" 81 | if test $? -ne 0; then 82 | printf "Failed to unload the module: ${TARGET_NAME}\n" 83 | cleanupAll 84 | exit 1 85 | fi 86 | fi 87 | } 88 | ######################################################################## 89 | 90 | doTestImpl() 91 | { 92 | # The test itself. 93 | # The repeated operations run concurrently: one in backgroud and one 94 | # in the current thread. 95 | sh @CMAKE_CURRENT_SOURCE_DIR@/plus.sh & 96 | 97 | # Store pid to wait for after the test. 98 | plus_pid=$! 99 | 100 | sh @CMAKE_CURRENT_SOURCE_DIR@/minus.sh 101 | 102 | # Wait until the background process is finished. 103 | wait ${plus_pid} 104 | 105 | # Wait a bit more for the RaceHound to report the races and resolve 106 | # the addresses there before unloading the target module. 107 | sleep 5 108 | } 109 | 110 | checkFoundRaces() 111 | { 112 | race_count=$(cat "${DEBUGFS_DIR}/racehound/race_count") 113 | if test -z "${race_count}"; then 114 | printf "Failed to read the race counter.\n" 115 | cleanupAll 116 | exit 1 117 | elif test "t${race_count}" = "t0"; then 118 | printf "No races found.\n" 119 | cleanupAll 120 | exit 1 121 | else 122 | printf "Found ${race_count} race(s).\n" 123 | fi 124 | } 125 | 126 | doTest() 127 | { 128 | if ! insmod ${CORE_MODULE}; then 129 | printf "Failed to load RaceHound.\n" 130 | cleanupAll 131 | exit 1 132 | fi 133 | 134 | loadTarget 135 | 136 | bp_list_file="${DEBUGFS_DIR}/racehound/breakpoints" 137 | 138 | success=0 139 | for bp in $(cat "${BPS_FILE}"); do 140 | printf "Trying to set BP ${bp}...\n" 141 | echo "${bp}" > "${bp_list_file}" 142 | if test $? -eq 0; then 143 | printf "Successfully set BP \"${bp}\".\n" 144 | success=1 145 | else 146 | printf "Failed to set BP \"${bp}\", continuing...\n" 147 | fi 148 | done 149 | 150 | printf "Here is the list of BPs that were set:\n" 151 | cat "${bp_list_file}" 152 | 153 | if test ${success} -eq 0; then 154 | printf "Error: no BPs were set.\n" 155 | cleanupAll 156 | exit 1 157 | fi 158 | 159 | printf "The first test run.\n" 160 | doTestImpl 161 | checkFoundRaces 162 | 163 | printf "Clearing the race counter.\n" 164 | echo 0 > "${DEBUGFS_DIR}/racehound/race_count" 165 | race_count=$(cat "${DEBUGFS_DIR}/racehound/race_count") 166 | if test "t${race_count}" != "t0"; then 167 | printf "Failed to clear the race counter.\n" 168 | cleanupAll 169 | exit 1 170 | fi 171 | 172 | # Clear the list of the BPs to check if it works and then re-add them. 173 | echo "clear" > "${bp_list_file}" 174 | success=1 175 | for bp in $(cat "${bp_list_file}"); do 176 | success=0 177 | done 178 | 179 | if test ${success} -ne 1; then 180 | printf "Failed to clear the list of BPs. Here is what remains set:\n" 181 | cat "${bp_list_file}" 182 | cleanupAll 183 | exit 1 184 | fi 185 | 186 | printf "Successfully cleared the list of BPs.\n" 187 | 188 | success=0 189 | for bp in $(cat "${BPS_FILE}"); do 190 | printf "Trying to set BP ${bp}...\n" 191 | echo "${bp}" > "${bp_list_file}" 192 | if test $? -eq 0; then 193 | printf "Successfully set BP \"${bp}\".\n" 194 | success=1 195 | else 196 | printf "Failed to set BP \"${bp}\", continuing...\n" 197 | fi 198 | done 199 | 200 | if test ${success} -eq 0; then 201 | printf "Error: no BPs were set after \"clear\".\n" 202 | cleanupAll 203 | exit 1 204 | fi 205 | 206 | # Run the test again to check if the counter still works. 207 | printf "The second test run.\n" 208 | doTestImpl 209 | checkFoundRaces 210 | 211 | printf "Clearing the race counter again.\n" 212 | echo 0 > "${DEBUGFS_DIR}/racehound/race_count" 213 | race_count=$(cat "${DEBUGFS_DIR}/racehound/race_count") 214 | if test "t${race_count}" != "t0"; then 215 | printf "Failed to clear the race counter.\n" 216 | cleanupAll 217 | exit 1 218 | fi 219 | 220 | # Now remove all the BPs, run the test again and check that 221 | # no races are found this time. 222 | for bp in $(cat "${BPS_FILE}"); do 223 | printf "Trying to remove BP ${bp}...\n" 224 | echo "-${bp}" > "${bp_list_file}" 225 | if test $? -eq 0; then 226 | printf "Successfully removed BP \"${bp}\".\n" 227 | else 228 | printf "Failed to removed BP \"${bp}\", continuing...\n" 229 | fi 230 | done 231 | 232 | success=1 233 | for bp in $(cat "${bp_list_file}"); do 234 | success=0 235 | done 236 | 237 | if test ${success} -ne 1; then 238 | printf "Failed to remove BPs. Here is what remains set:\n" 239 | cat "${bp_list_file}" 240 | fi 241 | 242 | printf "The third test run. No races should be found this time.\n" 243 | doTestImpl 244 | race_count=$(cat "${DEBUGFS_DIR}/racehound/race_count") 245 | if test "t${race_count}" != "t0"; then 246 | printf "The race counter is not 0: ${race_count}.\n" 247 | cleanupAll 248 | exit 1 249 | fi 250 | 251 | unloadTarget 252 | 253 | if ! rmmod "${CORE_MODULE_NAME}"; then 254 | printf "Failed to remove \"${CORE_MODULE_NAME}\" module.\n" 255 | cleanupAll 256 | exit 1 257 | fi 258 | } 259 | ######################################################################## 260 | 261 | WORK_DIR=${PWD} 262 | CORE_MODULE_NAME=racehound 263 | CORE_MODULE="@CMAKE_BINARY_DIR@/core/${CORE_MODULE_NAME}.ko" 264 | TARGET_NAME="test_simple" 265 | TARGET_MODULE="@CMAKE_BINARY_DIR@/core/tests/simple/${TARGET_NAME}.ko" 266 | BPS_SINGLE_FILE="@CMAKE_CURRENT_BINARY_DIR@/breakpoints-single.list" 267 | BPS_ALL_FILE="@CMAKE_CURRENT_BINARY_DIR@/breakpoints-all.list" 268 | 269 | TEST_ATOMIC_PARAM="" 270 | if test "t$1" = "t--atomic" || test "t$2" = "t--atomic"; then 271 | TEST_ATOMIC_PARAM="test_atomic=1" 272 | fi 273 | 274 | BPS_FILE="${BPS_SINGLE_FILE}" 275 | if test "t$1" = "t--all" || test "t$2" = "t--all"; then 276 | BPS_FILE="${BPS_ALL_FILE}" 277 | fi 278 | 279 | checkPrereqs 280 | 281 | # Mount debugfs to /sys/kernel/debug if it is not already mounted there, 282 | # the output system needs that. 283 | # The real users will need to mount it there too. 284 | DEBUGFS_DIR="/sys/kernel/debug" 285 | mount | grep "${DEBUGFS_DIR}" > /dev/null 286 | if test $? -ne 0; then 287 | mount -t debugfs none "${DEBUGFS_DIR}" 288 | if test $? -ne 0; then 289 | printf "Failed to mount debugfs to ${DEBUGFS_DIR}.\n" 290 | exit 1 291 | fi 292 | fi 293 | 294 | doTest 295 | cleanupAll 296 | 297 | # Test passed 298 | exit 0 299 | -------------------------------------------------------------------------------- /core/tests/basics/test_active.sh.in: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | ######################################################################## 4 | # This test checks attaching RaceHound to and detaching it from the 5 | # actively working kernel modules. 6 | # 7 | # Usage: 8 | # sh test_active.sh 9 | ######################################################################## 10 | 11 | # Just in case the tools like insmod/rmmod are not in their usual locations. 12 | export PATH=$PATH:/sbin:/bin:/usr/bin:/usr/sbin 13 | ######################################################################## 14 | 15 | # A function to check prerequisites: whether the necessary files exist, 16 | # etc. 17 | checkPrereqs() 18 | { 19 | if test ! -f "${CORE_MODULE}"; then 20 | printf "The core module is missing: ${CORE_MODULE}\n" 21 | exit 1 22 | fi 23 | 24 | if test ! -f "${TARGET_MODULE}"; then 25 | printf "The target module is missing: ${TARGET_MODULE}\n" 26 | exit 1 27 | fi 28 | 29 | if test ! -f "${COMMON_TARGET_MODULE}"; then 30 | printf "The target module is missing: ${COMMON_TARGET_MODULE}\n" 31 | exit 1 32 | fi 33 | 34 | if test ! -f "${BPS_FILE}"; then 35 | printf "The list of breakpoints is missing: ${BPS_FILE}\n" 36 | fi 37 | 38 | if test ! -f "${BPS_SINGLE_FILE}"; then 39 | printf "The list of breakpoints is missing: ${BPS_SINGLE_FILE}\n" 40 | fi 41 | } 42 | ######################################################################## 43 | 44 | cleanupTargets() 45 | { 46 | printf "Cleaning up the target(s)...\n" 47 | 48 | lsmod | grep "${TARGET_NAME}" > /dev/null 2>&1 49 | if test $? -eq 0; then 50 | rmmod "${TARGET_NAME}" 51 | fi 52 | 53 | lsmod | grep "${COMMON_TARGET_NAME}" > /dev/null 2>&1 54 | if test $? -eq 0; then 55 | rmmod "${COMMON_TARGET_NAME}" 56 | fi 57 | } 58 | ######################################################################## 59 | 60 | # Cleanup function 61 | cleanupAll() 62 | { 63 | cd "${WORK_DIR}" 64 | 65 | cleanupTargets 66 | 67 | lsmod | grep "${CORE_MODULE_NAME}" > /dev/null 2>&1 68 | if test $? -eq 0; then 69 | rmmod "${CORE_MODULE_NAME}" 70 | fi 71 | } 72 | ######################################################################## 73 | 74 | # $1 - name of the module to unload 75 | unloadTarget() 76 | { 77 | lsmod | grep "$1" > /dev/null 2>&1 78 | if test $? -eq 0; then 79 | rmmod "$1" 80 | if test $? -ne 0; then 81 | printf "Failed to unload the module: $1\n" 82 | cleanupAll 83 | exit 1 84 | fi 85 | fi 86 | } 87 | 88 | checkFoundRaces() 89 | { 90 | race_count=$(cat "${DEBUGFS_DIR}/racehound/race_count") 91 | if test -z "${race_count}"; then 92 | printf "Failed to read the race counter.\n" 93 | RESULT=1 # fail 94 | elif test "t${race_count}" = "t0"; then 95 | printf "No races found.\n" 96 | RESULT=1 # fail 97 | else 98 | printf "Found ${race_count} race(s).\n" 99 | fi 100 | } 101 | 102 | doTest() 103 | { 104 | # First, load "test_simple" target and make it busy. 105 | insmod "${TARGET_MODULE}" 106 | if test $? -ne 0; then 107 | printf "Failed to load the target module: \"${TARGET_MODULE}\"\n" 108 | cleanupAll 109 | exit 1 110 | fi 111 | 112 | sh @CMAKE_CURRENT_SOURCE_DIR@/plus_infinite.sh & 113 | plus_pid=$! 114 | 115 | sh @CMAKE_CURRENT_SOURCE_DIR@/minus_infinite.sh & 116 | minus_pid=$! 117 | 118 | sleep 5s 119 | 120 | # Now load RaceHound and attach it to the target 121 | if ! insmod ${CORE_MODULE}; then 122 | printf "Failed to load RaceHound.\n" 123 | kill -9 ${plus_pid} 124 | kill -9 ${minus_pid} 125 | cleanupAll 126 | exit 1 127 | fi 128 | 129 | bp_list_file="${DEBUGFS_DIR}/racehound/breakpoints" 130 | 131 | success=0 132 | for bp in $(cat "${BPS_FILE}"); do 133 | #printf "Trying to set BP ${bp}...\n" 134 | echo "${bp}" > "${bp_list_file}" 135 | if test $? -eq 0; then 136 | printf "Successfully set BP \"${bp}\".\n" 137 | success=1 138 | else 139 | printf "Failed to set BP \"${bp}\", continuing...\n" 140 | fi 141 | done 142 | 143 | if test ${success} -eq 0; then 144 | printf "Error: no BPs were set.\n" 145 | kill -9 ${plus_pid} 146 | kill -9 ${minus_pid} 147 | cleanupAll 148 | exit 1 149 | fi 150 | 151 | # Load the second target, just in case, and touch it too. 152 | insmod "${COMMON_TARGET_MODULE}" 153 | if test $? -ne 0; then 154 | printf "Failed to load the target module: \"${COMMON_TARGET_MODULE}\"\n" 155 | kill -9 ${plus_pid} 156 | kill -9 ${minus_pid} 157 | cleanupAll 158 | exit 1 159 | fi 160 | echo "something" > /dev/cfake0 161 | echo "something" > /dev/cfake1 162 | 163 | # Wait a bit more and check that some races have been found 164 | # in test_simple. 165 | sleep 5s 166 | checkFoundRaces 167 | 168 | # Remove some of the BPs. 169 | for bp in $(cat "${BPS_SINGLE_FILE}"); do 170 | echo "-${bp}" > "${bp_list_file}" 171 | if test $? -eq 0; then 172 | printf "Successfully removed BP \"${bp}\".\n" 173 | else 174 | printf "Failed to removed BP \"${bp}\", continuing...\n" 175 | fi 176 | done 177 | 178 | # Unload RaceHound while the targets are still working. 179 | if ! rmmod "${CORE_MODULE_NAME}"; then 180 | printf "Failed to remove \"${CORE_MODULE_NAME}\" module.\n" 181 | RESULT=1 182 | fi 183 | 184 | # Touch the second target again to see if any system errors show up. 185 | echo "test" > /dev/cfake0 186 | echo "test" > /dev/cfake1 187 | 188 | # Give the targets a bit more time and stop them. 189 | kill ${plus_pid} 190 | kill ${minus_pid} 191 | wait ${plus_pid} 192 | wait ${minus_pid} 193 | 194 | # Wait a bit more for the RaceHound to report the races and resolve 195 | # the addresses there before unloading the target module. 196 | sleep 5 197 | 198 | unloadTarget "${TARGET_NAME}" 199 | unloadTarget "${COMMON_TARGET_NAME}" 200 | } 201 | ######################################################################## 202 | 203 | WORK_DIR=${PWD} 204 | 205 | CORE_MODULE_NAME=racehound 206 | CORE_MODULE="@CMAKE_BINARY_DIR@/core/${CORE_MODULE_NAME}.ko" 207 | TARGET_NAME="test_simple" 208 | TARGET_MODULE="@CMAKE_BINARY_DIR@/core/tests/simple/${TARGET_NAME}.ko" 209 | COMMON_TARGET_NAME="test_common_target" 210 | COMMON_TARGET_MODULE="@CMAKE_BINARY_DIR@/core/tests/common_target/${COMMON_TARGET_NAME}.ko" 211 | 212 | BPS_SINGLE_FILE="@CMAKE_CURRENT_BINARY_DIR@/breakpoints-single.list" 213 | BPS_FILE="@CMAKE_CURRENT_BINARY_DIR@/breakpoints-all.list" 214 | 215 | checkPrereqs 216 | 217 | # Mount debugfs to /sys/kernel/debug if it is not already mounted there, 218 | # the output system needs that. 219 | DEBUGFS_DIR="/sys/kernel/debug" 220 | mount | grep "${DEBUGFS_DIR}" > /dev/null 221 | if test $? -ne 0; then 222 | mount -t debugfs none "${DEBUGFS_DIR}" 223 | if test $? -ne 0; then 224 | printf "Failed to mount debugfs to ${DEBUGFS_DIR}.\n" 225 | exit 1 226 | fi 227 | fi 228 | 229 | RESULT=0 230 | doTest 231 | cleanupAll 232 | 233 | exit ${RESULT} 234 | -------------------------------------------------------------------------------- /core/tests/basics/test_modules.sh.in: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | ######################################################################## 4 | # This test checks if RaceHound properly handles loading, unloading and 5 | # reloading of the target kernel modules. 6 | # 7 | # Usage: 8 | # sh test_modules.sh 9 | ######################################################################## 10 | 11 | # Just in case the tools like insmod/rmmod are not in their usual locations. 12 | export PATH=$PATH:/sbin:/bin:/usr/bin:/usr/sbin 13 | ######################################################################## 14 | 15 | # A function to check prerequisites: whether the necessary files exist, 16 | # etc. 17 | checkPrereqs() 18 | { 19 | if test ! -f "${CORE_MODULE}"; then 20 | printf "The core module is missing: ${CORE_MODULE}\n" 21 | exit 1 22 | fi 23 | 24 | if test ! -f "${TARGET_MODULE}"; then 25 | printf "The target module is missing: ${TARGET_MODULE}\n" 26 | exit 1 27 | fi 28 | 29 | if test ! -f "${COMMON_TARGET_MODULE}"; then 30 | printf "The target module is missing: ${COMMON_TARGET_MODULE}\n" 31 | exit 1 32 | fi 33 | 34 | if test ! -f "${BPS_FILE}"; then 35 | printf "The list of breakpoints is missing: ${BPS_FILE}\n" 36 | fi 37 | 38 | if test ! -f "${BPS_SINGLE_FILE}"; then 39 | printf "The list of breakpoints is missing: ${BPS_SINGLE_FILE}\n" 40 | fi 41 | } 42 | ######################################################################## 43 | 44 | cleanupTargets() 45 | { 46 | printf "Cleaning up the target(s)...\n" 47 | 48 | lsmod | grep "${TARGET_NAME}" > /dev/null 2>&1 49 | if test $? -eq 0; then 50 | rmmod "${TARGET_NAME}" 51 | fi 52 | 53 | lsmod | grep "${COMMON_TARGET_NAME}" > /dev/null 2>&1 54 | if test $? -eq 0; then 55 | rmmod "${COMMON_TARGET_NAME}" 56 | fi 57 | } 58 | ######################################################################## 59 | 60 | # Cleanup function 61 | cleanupAll() 62 | { 63 | cd "${WORK_DIR}" 64 | 65 | cleanupTargets 66 | 67 | lsmod | grep "${CORE_MODULE_NAME}" > /dev/null 2>&1 68 | if test $? -eq 0; then 69 | rmmod "${CORE_MODULE_NAME}" 70 | fi 71 | } 72 | ######################################################################## 73 | 74 | # $1 - path to the module 75 | loadTarget() 76 | { 77 | insmod "$1" 78 | if test $? -ne 0; then 79 | printf "Failed to load the target module: \"$1\"\n" 80 | cleanupAll 81 | exit 1 82 | fi 83 | } 84 | 85 | # $1 - name of the module to unload 86 | unloadTarget() 87 | { 88 | # Wait a bit more for the RaceHound to report the races and resolve 89 | # the addresses there before unloading the target module. 90 | sleep 5 91 | 92 | lsmod | grep "$1" > /dev/null 2>&1 93 | if test $? -eq 0; then 94 | rmmod "$1" 95 | if test $? -ne 0; then 96 | printf "Failed to unload the module: $1\n" 97 | cleanupAll 98 | exit 1 99 | fi 100 | fi 101 | } 102 | 103 | doTest() 104 | { 105 | if ! insmod ${CORE_MODULE}; then 106 | printf "Failed to load RaceHound.\n" 107 | cleanupAll 108 | exit 1 109 | fi 110 | 111 | loadTarget "${COMMON_TARGET_MODULE}" 112 | 113 | bp_list_file="${DEBUGFS_DIR}/racehound/breakpoints" 114 | 115 | success=0 116 | for bp in $(cat "${BPS_FILE}"); do 117 | echo "${bp}" > "${bp_list_file}" 118 | if test $? -eq 0; then 119 | printf "Successfully set BP \"${bp}\".\n" 120 | success=1 121 | else 122 | printf "Failed to set BP \"${bp}\", continuing...\n" 123 | fi 124 | done 125 | 126 | if test ${success} -eq 0; then 127 | printf "Error: no BPs were set.\n" 128 | kill -9 ${plus_pid} 129 | kill -9 ${minus_pid} 130 | cleanupAll 131 | exit 1 132 | fi 133 | # [NB] The BPs for test_common_target:init area are pending now. 134 | 135 | echo "something" > /dev/cfake0 136 | echo "something" > /dev/cfake1 137 | 138 | unloadTarget "${COMMON_TARGET_NAME}" 139 | loadTarget "${COMMON_TARGET_MODULE}" 140 | 141 | echo "something" > /dev/cfake0 142 | echo "something" > /dev/cfake1 143 | 144 | unloadTarget "${COMMON_TARGET_NAME}" 145 | 146 | # Now load another target first. 147 | loadTarget "${TARGET_MODULE}" 148 | 149 | sh @CMAKE_CURRENT_SOURCE_DIR@/plus.sh & 150 | plus_pid=$! 151 | 152 | sh @CMAKE_CURRENT_SOURCE_DIR@/minus.sh & 153 | minus_pid=$! 154 | 155 | loadTarget "${COMMON_TARGET_MODULE}" 156 | echo "something" > /dev/cfake0 157 | echo "something" > /dev/cfake1 158 | 159 | wait ${plus_pid} 160 | wait ${minus_pid} 161 | 162 | unloadTarget "${TARGET_NAME}" 163 | unloadTarget "${COMMON_TARGET_NAME}" 164 | 165 | if ! rmmod "${CORE_MODULE_NAME}"; then 166 | printf "Failed to remove \"${CORE_MODULE_NAME}\" module.\n" 167 | cleanupAll 168 | exit 1 169 | fi 170 | } 171 | ######################################################################## 172 | 173 | WORK_DIR=${PWD} 174 | 175 | CORE_MODULE_NAME=racehound 176 | CORE_MODULE="@CMAKE_BINARY_DIR@/core/${CORE_MODULE_NAME}.ko" 177 | TARGET_NAME="test_simple" 178 | TARGET_MODULE="@CMAKE_BINARY_DIR@/core/tests/simple/${TARGET_NAME}.ko" 179 | COMMON_TARGET_NAME="test_common_target" 180 | COMMON_TARGET_MODULE="@CMAKE_BINARY_DIR@/core/tests/common_target/${COMMON_TARGET_NAME}.ko" 181 | 182 | BPS_SINGLE_FILE="@CMAKE_CURRENT_BINARY_DIR@/breakpoints-single.list" 183 | BPS_FILE="@CMAKE_CURRENT_BINARY_DIR@/breakpoints-all.list" 184 | 185 | checkPrereqs 186 | 187 | # Mount debugfs to /sys/kernel/debug if it is not already mounted there, 188 | # the output system needs that. 189 | DEBUGFS_DIR="/sys/kernel/debug" 190 | mount | grep "${DEBUGFS_DIR}" > /dev/null 191 | if test $? -ne 0; then 192 | mount -t debugfs none "${DEBUGFS_DIR}" 193 | if test $? -ne 0; then 194 | printf "Failed to mount debugfs to ${DEBUGFS_DIR}.\n" 195 | exit 1 196 | fi 197 | fi 198 | 199 | doTest 200 | cleanupAll 201 | 202 | exit 0 203 | -------------------------------------------------------------------------------- /core/tests/common_target/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # A module commonly used for testing 2 | set(KMODULE_NAME "test_common_target") 3 | 4 | configure_file("${CMAKE_CURRENT_SOURCE_DIR}/module.makefile.in" 5 | "${CMAKE_CURRENT_BINARY_DIR}/module.makefile" 6 | @ONLY 7 | ) 8 | 9 | configure_file("${CMAKE_CURRENT_SOURCE_DIR}/Kbuild.in" 10 | "${CMAKE_CURRENT_BINARY_DIR}/Kbuild" 11 | @ONLY 12 | ) 13 | 14 | add_custom_target("common_target_module" 15 | DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/${KMODULE_NAME}.ko" 16 | ) 17 | 18 | add_custom_command( 19 | OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${KMODULE_NAME}.ko" 20 | COMMAND $(MAKE) -f module.makefile 21 | DEPENDS 22 | "${CMAKE_CURRENT_BINARY_DIR}/cfake.c" 23 | "${CMAKE_CURRENT_BINARY_DIR}/cfake.h" 24 | "${CMAKE_CURRENT_BINARY_DIR}/Makefile" 25 | "${CMAKE_CURRENT_BINARY_DIR}/Kbuild" 26 | ) 27 | 28 | rule_copy_file("${CMAKE_CURRENT_BINARY_DIR}/cfake.c" 29 | "${CMAKE_CURRENT_SOURCE_DIR}/cfake.c") 30 | rule_copy_file("${CMAKE_CURRENT_BINARY_DIR}/cfake.h" 31 | "${CMAKE_CURRENT_SOURCE_DIR}/cfake.h") 32 | 33 | rh_test_add_target("common_target_module") 34 | 35 | # Generate the list of breakpoints for the tests. 36 | add_custom_command( 37 | OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${KMODULE_NAME}-bps.list" 38 | COMMAND "${CMAKE_BINARY_DIR}/lines2insns/lines2insns" ${KMODULE_NAME}.ko 39 | < "${CMAKE_CURRENT_SOURCE_DIR}/lines.list" 40 | > "${CMAKE_CURRENT_BINARY_DIR}/${KMODULE_NAME}-bps.list" 41 | DEPENDS 42 | "${CMAKE_CURRENT_SOURCE_DIR}/lines.list" 43 | "${CMAKE_CURRENT_BINARY_DIR}/${KMODULE_NAME}.ko" 44 | ) 45 | 46 | add_custom_target(${KMODULE_NAME}-bps 47 | DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/${KMODULE_NAME}-bps.list" 48 | ) 49 | 50 | add_dependencies (${KMODULE_NAME}-bps common_target_module) 51 | rh_test_add_target(${KMODULE_NAME}-bps) 52 | -------------------------------------------------------------------------------- /core/tests/common_target/Kbuild.in: -------------------------------------------------------------------------------- 1 | module_name=@KMODULE_NAME@ 2 | 3 | ccflags-y := -g -I$(src) 4 | obj-m := ${module_name}.o 5 | ${module_name}-y := cfake.o 6 | -------------------------------------------------------------------------------- /core/tests/common_target/cfake.c: -------------------------------------------------------------------------------- 1 | /* cfake.c - implementation of a simple module for a character device 2 | * can be used for testing, demonstrations, etc. */ 3 | 4 | /* ======================================================================== 5 | * Copyright (C) 2012, KEDR development team 6 | * Copyright (C) 2010-2011, Institute for System Programming 7 | * of the Russian Academy of Sciences (ISPRAS) 8 | * Authors: 9 | * Eugene A. Shatokhin 10 | * Andrey V. Tsyvarev 11 | * 12 | * This program is free software; you can redistribute it and/or modify it 13 | * under the terms of the GNU General Public License version 2 as published 14 | * by the Free Software Foundation. 15 | ======================================================================== */ 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #include 32 | 33 | #include "cfake.h" 34 | 35 | MODULE_AUTHOR("Eugene A. Shatokhin"); 36 | MODULE_LICENSE("GPL"); 37 | 38 | #define CFAKE_DEVICE_NAME "cfake" 39 | 40 | /* parameters */ 41 | static int cfake_ndevices = CFAKE_NDEVICES; 42 | static unsigned long cfake_buffer_size = CFAKE_BUFFER_SIZE; 43 | static unsigned long cfake_block_size = CFAKE_BLOCK_SIZE; 44 | 45 | module_param(cfake_ndevices, int, S_IRUGO); 46 | module_param(cfake_buffer_size, ulong, S_IRUGO); 47 | module_param(cfake_block_size, ulong, S_IRUGO); 48 | /* ================================================================ */ 49 | 50 | static unsigned int cfake_major = 0; 51 | static struct cfake_dev *cfake_devices = NULL; 52 | static struct class *cfake_class = NULL; 53 | /* ================================================================ */ 54 | 55 | int 56 | cfake_open(struct inode *inode, struct file *filp) 57 | { 58 | unsigned int mj = imajor(inode); 59 | unsigned int mn = iminor(inode); 60 | 61 | struct cfake_dev *dev = NULL; 62 | 63 | if (mj != cfake_major || mn < 0 || mn >= cfake_ndevices) 64 | { 65 | printk(KERN_WARNING "[target] " 66 | "No device found with minor=%d and major=%d\n", 67 | mj, mn); 68 | return -ENODEV; /* No such device */ 69 | } 70 | 71 | /* store a pointer to struct cfake_dev here for other methods */ 72 | dev = &cfake_devices[mn]; 73 | filp->private_data = dev; 74 | 75 | if (inode->i_cdev != &dev->cdev) 76 | { 77 | printk(KERN_WARNING "[target] open: internal error\n"); 78 | return -ENODEV; /* No such device */ 79 | } 80 | 81 | /* if opened the 1st time, allocate the buffer */ 82 | if (dev->data == NULL) 83 | { 84 | dev->data = (unsigned char*)kzalloc(dev->buffer_size, GFP_KERNEL); 85 | if (dev->data == NULL) 86 | { 87 | printk(KERN_WARNING "[target] open(): out of memory\n"); 88 | return -ENOMEM; 89 | } 90 | } 91 | return 0; 92 | } 93 | 94 | int 95 | cfake_release(struct inode *inode, struct file *filp) 96 | { 97 | return 0; 98 | } 99 | 100 | ssize_t 101 | cfake_read(struct file *filp, char __user *buf, size_t count, 102 | loff_t *f_pos) 103 | { 104 | struct cfake_dev *dev = (struct cfake_dev *)filp->private_data; 105 | ssize_t retval = 0; 106 | 107 | if (mutex_lock_killable(&dev->cfake_mutex)) 108 | return -EINTR; 109 | 110 | if (*f_pos >= dev->buffer_size) /* EOF */ 111 | goto out; 112 | 113 | if (*f_pos + count > dev->buffer_size) 114 | count = dev->buffer_size - *f_pos; 115 | 116 | if (count > dev->block_size) 117 | count = dev->block_size; 118 | 119 | if (copy_to_user(buf, &(dev->data[*f_pos]), count) != 0) 120 | { 121 | retval = -EFAULT; 122 | goto out; 123 | } 124 | 125 | *f_pos += count; 126 | retval = count; 127 | 128 | out: 129 | mutex_unlock(&dev->cfake_mutex); 130 | return retval; 131 | } 132 | 133 | ssize_t 134 | cfake_write(struct file *filp, const char __user *buf, size_t count, 135 | loff_t *f_pos) 136 | { 137 | struct cfake_dev *dev = (struct cfake_dev *)filp->private_data; 138 | ssize_t retval = 0; 139 | 140 | if (mutex_lock_killable(&dev->cfake_mutex)) 141 | return -EINTR; 142 | 143 | if (*f_pos >= dev->buffer_size) { 144 | /* Writing beyond the end of the buffer is not allowed. */ 145 | retval = -EINVAL; 146 | goto out; 147 | } 148 | 149 | if (*f_pos + count > dev->buffer_size) 150 | count = dev->buffer_size - *f_pos; 151 | 152 | if (count > dev->block_size) 153 | count = dev->block_size; 154 | 155 | if (copy_from_user(&(dev->data[*f_pos]), buf, count) != 0) 156 | { 157 | retval = -EFAULT; 158 | goto out; 159 | } 160 | 161 | *f_pos += count; 162 | retval = count; 163 | 164 | out: 165 | mutex_unlock(&dev->cfake_mutex); 166 | return retval; 167 | } 168 | 169 | loff_t 170 | cfake_llseek(struct file *filp, loff_t off, int whence) 171 | { 172 | struct cfake_dev *dev = (struct cfake_dev *)filp->private_data; 173 | loff_t newpos = 0; 174 | 175 | switch(whence) { 176 | case 0: /* SEEK_SET */ 177 | newpos = off; 178 | break; 179 | 180 | case 1: /* SEEK_CUR */ 181 | newpos = filp->f_pos + off; 182 | break; 183 | 184 | case 2: /* SEEK_END */ 185 | newpos = dev->buffer_size + off; 186 | break; 187 | 188 | default: /* can't happen */ 189 | return -EINVAL; 190 | } 191 | if (newpos < 0 || newpos > dev->buffer_size) 192 | return -EINVAL; 193 | 194 | filp->f_pos = newpos; 195 | return newpos; 196 | } 197 | 198 | struct file_operations cfake_fops = { 199 | .owner = THIS_MODULE, 200 | .read = cfake_read, 201 | .write = cfake_write, 202 | .open = cfake_open, 203 | .release = cfake_release, 204 | .llseek = cfake_llseek, 205 | }; 206 | 207 | /* ================================================================ */ 208 | /* Setup and register the device with specific index (the index is also 209 | * the minor number of the device). 210 | * Device class should be created beforehand. 211 | */ 212 | static int 213 | cfake_construct_device(struct cfake_dev *dev, int minor, 214 | struct class *class) 215 | { 216 | int err = 0; 217 | dev_t devno = MKDEV(cfake_major, minor); 218 | struct device *device = NULL; 219 | 220 | BUG_ON(dev == NULL || class == NULL); 221 | 222 | /* Memory is to be allocated when the device is opened the first time */ 223 | dev->data = NULL; 224 | dev->buffer_size = cfake_buffer_size; 225 | dev->block_size = cfake_block_size; 226 | mutex_init(&dev->cfake_mutex); 227 | 228 | cdev_init(&dev->cdev, &cfake_fops); 229 | dev->cdev.owner = THIS_MODULE; 230 | 231 | err = cdev_add(&dev->cdev, devno, 1); 232 | if (err) 233 | { 234 | printk(KERN_WARNING "[target] Error %d while trying to add %s%d", 235 | err, CFAKE_DEVICE_NAME, minor); 236 | return err; 237 | } 238 | 239 | device = device_create(class, NULL, /* no parent device */ 240 | devno, NULL, /* no additional data */ 241 | CFAKE_DEVICE_NAME "%d", minor); 242 | 243 | if (IS_ERR(device)) { 244 | err = PTR_ERR(device); 245 | printk(KERN_WARNING "[target] Error %d while trying to create %s%d", 246 | err, CFAKE_DEVICE_NAME, minor); 247 | cdev_del(&dev->cdev); 248 | return err; 249 | } 250 | return 0; 251 | } 252 | 253 | /* Destroy the device and free its buffer */ 254 | static void 255 | cfake_destroy_device(struct cfake_dev *dev, int minor, 256 | struct class *class) 257 | { 258 | BUG_ON(dev == NULL || class == NULL); 259 | device_destroy(class, MKDEV(cfake_major, minor)); 260 | cdev_del(&dev->cdev); 261 | kfree(dev->data); 262 | return; 263 | } 264 | 265 | /* ================================================================ */ 266 | static void 267 | cfake_cleanup_module(int devices_to_destroy) 268 | { 269 | int i; 270 | 271 | /* Get rid of character devices (if any exist) */ 272 | if (cfake_devices) { 273 | for (i = 0; i < devices_to_destroy; ++i) { 274 | cfake_destroy_device(&cfake_devices[i], i, cfake_class); 275 | } 276 | kfree(cfake_devices); 277 | } 278 | 279 | if (cfake_class) 280 | class_destroy(cfake_class); 281 | 282 | /* [NB] cfake_cleanup_module is never called if alloc_chrdev_region() 283 | * has failed. */ 284 | unregister_chrdev_region(MKDEV(cfake_major, 0), cfake_ndevices); 285 | return; 286 | } 287 | 288 | static int __init 289 | cfake_init_module(void) 290 | { 291 | int err = 0; 292 | int i = 0; 293 | int devices_to_destroy = 0; 294 | dev_t dev = 0; 295 | 296 | if (cfake_ndevices <= 0) 297 | { 298 | printk(KERN_WARNING 299 | "[target] Invalid value of cfake_ndevices: %d\n", 300 | cfake_ndevices); 301 | err = -EINVAL; 302 | return err; 303 | } 304 | 305 | /* Get a range of minor numbers (starting with 0) to work with */ 306 | err = alloc_chrdev_region(&dev, 0, cfake_ndevices, CFAKE_DEVICE_NAME); 307 | if (err < 0) { 308 | printk(KERN_WARNING "[target] alloc_chrdev_region() failed\n"); 309 | return err; 310 | } 311 | cfake_major = MAJOR(dev); 312 | 313 | /* Create device class (before allocation of the array of devices) */ 314 | cfake_class = class_create(THIS_MODULE, CFAKE_DEVICE_NAME); 315 | if (IS_ERR(cfake_class)) { 316 | err = PTR_ERR(cfake_class); 317 | goto fail; 318 | } 319 | 320 | /* Allocate the array of devices */ 321 | cfake_devices = (struct cfake_dev *)kzalloc( 322 | cfake_ndevices * sizeof(struct cfake_dev), 323 | GFP_KERNEL); 324 | if (cfake_devices == NULL) { 325 | err = -ENOMEM; 326 | goto fail; 327 | } 328 | 329 | /* Construct devices */ 330 | for (i = 0; i < cfake_ndevices; ++i) { 331 | err = cfake_construct_device(&cfake_devices[i], i, cfake_class); 332 | if (err) { 333 | devices_to_destroy = i; 334 | goto fail; 335 | } 336 | } 337 | return 0; /* success */ 338 | 339 | fail: 340 | cfake_cleanup_module(devices_to_destroy); 341 | return err; 342 | } 343 | 344 | static void __exit 345 | cfake_exit_module(void) 346 | { 347 | cfake_cleanup_module(cfake_ndevices); 348 | return; 349 | } 350 | 351 | module_init(cfake_init_module); 352 | module_exit(cfake_exit_module); 353 | /* ================================================================ */ 354 | -------------------------------------------------------------------------------- /core/tests/common_target/cfake.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kmrov/racehound/cd0b409561b18dfdc5c86049305bcfed272cd698/core/tests/common_target/cfake.h -------------------------------------------------------------------------------- /core/tests/common_target/lines.list: -------------------------------------------------------------------------------- 1 | cfake.c:82:read 2 | cfake.c:84:write 3 | cfake.c:85:read 4 | cfake.c:272 5 | cfake.c:276 6 | cfake.c:314 7 | cfake.c:321 8 | -------------------------------------------------------------------------------- /core/tests/common_target/module.makefile.in: -------------------------------------------------------------------------------- 1 | module_name=@KMODULE_NAME@ 2 | 3 | KBUILD_DIR=/lib/modules/$(shell uname -r)/build 4 | PWD=$(shell pwd) 5 | 6 | all: ${module_name}.ko 7 | 8 | ${module_name}.ko: cfake.c 9 | $(MAKE) -C ${KBUILD_DIR} M=${PWD} modules 10 | 11 | clean: 12 | $(MAKE) -C ${KBUILD_DIR} M=${PWD} clean 13 | 14 | .PHONY: all clean 15 | -------------------------------------------------------------------------------- /core/tests/simple/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Name of the test module should be unique in the whole project(!) 2 | set(module_name "test_simple") 3 | 4 | # Build module... 5 | kbuild_add_module(${module_name} "module.c") 6 | 7 | # And mark it "for test only" 8 | rh_test_add_target(${module_name}) 9 | 10 | # Generate the list of breakpoints for the tests. 11 | add_custom_command( 12 | OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${module_name}-bps.list" 13 | COMMAND "${CMAKE_BINARY_DIR}/lines2insns/lines2insns" ${module_name}.ko 14 | < "${CMAKE_CURRENT_SOURCE_DIR}/lines.list" 15 | > "${CMAKE_CURRENT_BINARY_DIR}/${module_name}-bps.list" 16 | DEPENDS 17 | "${CMAKE_CURRENT_SOURCE_DIR}/lines.list" 18 | "${CMAKE_CURRENT_BINARY_DIR}/${module_name}.ko" 19 | ) 20 | 21 | add_custom_target(${module_name}-bps 22 | DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/${module_name}-bps.list" 23 | ) 24 | 25 | add_dependencies (${module_name}-bps ${module_name}) 26 | rh_test_add_target(${module_name}-bps) 27 | -------------------------------------------------------------------------------- /core/tests/simple/lines.list: -------------------------------------------------------------------------------- 1 | module.c:41 2 | -------------------------------------------------------------------------------- /core/tests/simple/module.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | static int test_atomic = 0; 11 | module_param(test_atomic, int, S_IRUGO); 12 | 13 | #define DEVICE_NAME "rfindertest" 14 | 15 | static volatile int balance = 0; 16 | 17 | static DEFINE_SPINLOCK(lock); 18 | 19 | static struct dentry *hello_debugfs_dir; 20 | static struct dentry *hello_debugfs_file; 21 | 22 | static int 23 | hello_device_open(struct inode *inode, struct file *file) 24 | { 25 | return 0; 26 | } 27 | 28 | static int 29 | hello_device_release(struct inode *inode, struct file *file) 30 | { 31 | return 0; 32 | } 33 | 34 | static noinline void 35 | hello_plus(void) 36 | { 37 | unsigned long flags; 38 | if (test_atomic) 39 | spin_lock_irqsave(&lock, flags); 40 | 41 | ++balance; 42 | 43 | if (test_atomic) 44 | spin_unlock_irqrestore(&lock, flags); 45 | } 46 | 47 | static noinline void 48 | hello_minus(void) 49 | { 50 | --balance; 51 | } 52 | 53 | static ssize_t 54 | hello_device_write(struct file *filp, const char __user *buffer, 55 | size_t length, loff_t * offset) 56 | { 57 | char data[1]; 58 | int res; 59 | 60 | res = copy_from_user(data, buffer, 1); 61 | if (res != 0) 62 | return -EFAULT; 63 | 64 | if (data[0] == '+') 65 | hello_plus(); 66 | else 67 | hello_minus(); 68 | 69 | return length; 70 | } 71 | 72 | static struct file_operations fops = { 73 | .write = hello_device_write, 74 | .open = hello_device_open, 75 | .release = hello_device_release, 76 | }; 77 | 78 | static int __init 79 | hello_init(void) 80 | { 81 | hello_debugfs_dir = debugfs_create_dir(DEVICE_NAME, NULL); 82 | if (hello_debugfs_dir == NULL) 83 | return -EINVAL; 84 | 85 | hello_debugfs_file = debugfs_create_file( 86 | "hello", 0444, hello_debugfs_dir, NULL, &fops); 87 | if (hello_debugfs_file == NULL) 88 | { 89 | debugfs_remove(hello_debugfs_dir); 90 | return -EINVAL; 91 | } 92 | 93 | pr_info("[test] &balance: %lx\n", (unsigned long) &balance); 94 | return 0; 95 | } 96 | 97 | static void __exit 98 | hello_exit(void) 99 | { 100 | debugfs_remove(hello_debugfs_file); 101 | debugfs_remove(hello_debugfs_dir); 102 | 103 | pr_info("[test] Unregistered. balance=%d.\n", balance); 104 | } 105 | 106 | module_init(hello_init); 107 | module_exit(hello_exit); 108 | 109 | MODULE_LICENSE("GPL"); 110 | -------------------------------------------------------------------------------- /core/thunks.S: -------------------------------------------------------------------------------- 1 | /* Thunks ("mediator functions") used to call the handlers. */ 2 | 3 | #include 4 | #include 5 | 6 | .extern rh_do_before_insn 7 | .extern rh_do_after_insn 8 | 9 | #ifdef CONFIG_X86_32 10 | # define RH_SAVE_SCRATCH_BUT_AX \ 11 | push %ecx; \ 12 | push %edx; 13 | 14 | # define RH_RESTORE_SCRATCH_BUT_AX \ 15 | pop %edx; \ 16 | pop %ecx; 17 | 18 | # define RH_PUSHF pushf 19 | # define RH_POPF popf 20 | 21 | #else 22 | /* x86_64 */ 23 | # define RH_SAVE_SCRATCH_BUT_AX \ 24 | push %rcx; \ 25 | push %rdx; \ 26 | push %rsi; \ 27 | push %rdi; \ 28 | push %r8; \ 29 | push %r9; \ 30 | push %r10; \ 31 | push %r11; 32 | 33 | # define RH_RESTORE_SCRATCH_BUT_AX \ 34 | pop %r11; \ 35 | pop %r10; \ 36 | pop %r9; \ 37 | pop %r8; \ 38 | pop %rdi; \ 39 | pop %rsi; \ 40 | pop %rdx; \ 41 | pop %rcx; 42 | 43 | # define RH_PUSHF pushfq 44 | # define RH_POPF popfq 45 | 46 | #endif /* CONFIG_X86_32 */ 47 | 48 | /* Creates a thunk ("_thunk_name") that calls a given function of RaceHound 49 | * ("_func") preserving the registers and flags. 50 | * "_func" must be a normal function, i.e. with the default calling 51 | * convention / linkage. 52 | * It must have the following type: 53 | * unsigned long _func(void); 54 | * 55 | * "_func" is expected to return the address to pass control to. 56 | * The address will be in %eax/%rax after "call _func". We place it on top 57 | * of the stack while restoring the old value of %eax/%rax and use "ret" to 58 | * pass control there. */ 59 | #define RH_MAKE_THUNK(_thunk_name, _func) \ 60 | .global _thunk_name; \ 61 | .type _thunk_name,@function; \ 62 | _thunk_name: \ 63 | push %_ASM_AX; \ 64 | RH_SAVE_SCRATCH_BUT_AX; \ 65 | RH_PUSHF; \ 66 | call _func; \ 67 | RH_POPF; \ 68 | RH_RESTORE_SCRATCH_BUT_AX; \ 69 | xchg %_ASM_AX, (%_ASM_SP); \ 70 | ret; \ 71 | .size _thunk_name, .-_thunk_name; 72 | 73 | .text 74 | RH_MAKE_THUNK(rh_thunk_pre, rh_do_before_insn) 75 | RH_MAKE_THUNK(rh_thunk_post, rh_do_after_insn) 76 | -------------------------------------------------------------------------------- /examples/check_races.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # Usage (run the script as root): 4 | # check_races.py [OPTIONS] FILE 5 | # 6 | # The script monitors the instructions listed in FILE (one per line) in the 7 | # same format as RaceHound accepts in its 'breakpoints' file. It operates 8 | # in a similar way to what DataCollider tool does on MS Windows. 9 | # 10 | # The script chooses some of the listed instructions and tells RaceHound 11 | # to look for races involving them. RaceHound places breakpoints on these 12 | # instructions, etc. 13 | # 14 | # The initial number of the breakpoints to be set can be specified in 15 | # '--num-bps' parameter. 16 | # 17 | # If the breakpoints hit too often or too rarely, the script removes or adds 18 | # some breakpoints to balance things. See --min_hit_rate and --max_hit_rate 19 | # options. 20 | # 21 | # If a breakpoint hits, the script removes it, randomly chooses another one 22 | # that is not currently set and sets it. 23 | # 24 | # All this allows to check all the given instructions in time. 25 | # 26 | # Note. Python 3.4 or newer is required. 27 | # 28 | # If -v (--verbose) is present, the script will output more info as it 29 | # works. Without it, only the summary of the found races is output at the 30 | # exit as well as the errors. 31 | # 32 | # The script assumes debugfs is mounted to /sys/kernel/debug/. 33 | # 34 | # Note. Per-breakpoint delays ('delay=...' in the beakpoint specification) 35 | # are not supported here. Use the parameters of the kernel part of RaceHound 36 | # to set the delays globally if needed. 37 | 38 | import sys 39 | import os.path 40 | import selectors 41 | import argparse 42 | import re 43 | 44 | from datetime import datetime 45 | from random import SystemRandom 46 | 47 | 48 | BPS_FILE = '/sys/kernel/debug/racehound/breakpoints' 49 | EVENTS_FILE = '/sys/kernel/debug/racehound/events' 50 | 51 | ERR_NO_FILES = ''.join([ 52 | 'Please check that debugfs is mounted to /sys/kernel/debug ', 53 | 'and kernel module \"racehound\" is loaded.']) 54 | 55 | RE_RACE = re.compile(' '.join([ 56 | r'Detected a data race on the memory block at (0x)?[0-9a-f]+', 57 | r'between the instruction at ([^ \t]+) \(comm: \"(.+)\"\)', 58 | r'and the instruction right before (.*) \(comm: \"(.+)\"\)'])) 59 | 60 | RE_RREAD = re.compile(' '.join([ 61 | r'Detected a data race on the memory block at (0x)?[0-9a-f]+', 62 | r'that is about to be accessed by the instruction at ([^ \t]+)', 63 | r'\(comm: \"(.+)\"\):', 64 | r'the memory block was modified during the delay'])) 65 | 66 | # [:]{init|core}+0xoffset 67 | RE_BP_SPEC = re.compile(r'^([^:]+:)?(core|init)\+0x[a-f0-9]{1,8}$') 68 | 69 | # How many breakpoints to set initially. 70 | num_bps = 10 71 | 72 | # How many BPs to add if the hit rate is too low. 73 | bps_to_add = 10 74 | 75 | # The desirable boundaries of the breakpoint hit rate (BPs/second). 76 | min_hit_rate = 0.01 77 | max_hit_rate = 1.0 78 | 79 | # Each HIT_CHECK_INTERVAL seconds, the breakpoint hit rate will be 80 | # evaluated and, if necessary, the set of breakpoints will be adjusted. 81 | HIT_CHECK_INTERVAL = 10 82 | 83 | # See -v/--verbose argument. 84 | verbose = 0 85 | 86 | # The random number generator to use. 87 | rng = SystemRandom() 88 | 89 | 90 | def positive_int(string): 91 | value = int(string) 92 | if value <= 0: 93 | msg = "%r is not a positive integer" % string 94 | raise argparse.ArgumentTypeError(msg) 95 | return value 96 | 97 | 98 | class RaceGroup(object): 99 | '''A group of races between a given pair of instructions 100 | 101 | 'insn' - address of the instruction that under watch, 102 | 'insn_comm' - 'comm' of the process that executed 'insn', 103 | 'conflict_insn' - the address right after the instruction that performed 104 | a conflicting memory access (None if unknown), 105 | 'conflict_comm' - 'comm' of the process that executed 'conflict_insn' 106 | (None if unknown). 107 | ''' 108 | def __init__(self, insn, insn_comm, conflict_insn=None, 109 | conflict_comm=None): 110 | self.insn = insn 111 | self.insn_comms = [insn_comm] 112 | self.conflict_insn = conflict_insn 113 | if conflict_comm: 114 | self.conflict_comms = [conflict_comm] 115 | else: 116 | self.conflict_comms = [] 117 | 118 | # How many times this race was reported. 119 | self.count = 1 120 | 121 | def print_races(self): 122 | if self.conflict_insn: 123 | print('Race between %s and the insn right before %s.' % 124 | (self.insn, self.conflict_insn)) 125 | comms = list(set(self.insn_comms)) 126 | print('The first insn was executed by:', ', '.join(comms)) 127 | comms = list(set(self.conflict_comms)) 128 | print('The second insn was executed by:', ', '.join(comms)) 129 | else: 130 | print('Race between %s and some other code.' % self.insn) 131 | comms = list(set(self.insn_comms)) 132 | print('The insn was executed by:', ', '.join(comms)) 133 | 134 | print('The race was reported %d time(s).' % self.count) 135 | 136 | 137 | def store_race_info(races, str_race): 138 | matched = re.search(RE_RACE, str_race) 139 | if matched: 140 | _, insn, insn_comm, conflict_insn, conflict_comm = matched.groups() 141 | key = insn + '#' + conflict_insn 142 | else: 143 | matched = re.search(RE_RREAD, str_race) 144 | if not matched: 145 | sys.stderr.write( 146 | 'Unknown format of a race report: "%s".\n' % str_race) 147 | return 148 | 149 | _, insn, insn_comm = matched.groups() 150 | conflict_insn = None 151 | conflict_comm = None 152 | key = insn 153 | 154 | if key in races: 155 | races[key].count = races[key].count + 1 156 | races[key].insn_comms.append(insn_comm) 157 | if conflict_comm: 158 | races[key].conflict_comms.append(conflict_comm) 159 | else: 160 | races[key] = RaceGroup( 161 | insn, insn_comm, conflict_insn, conflict_comm) 162 | 163 | 164 | def print_summary(races): 165 | if not races: 166 | print('No races found.') 167 | else: 168 | for _, grp in races.items(): 169 | grp.print_races() 170 | print('') 171 | 172 | 173 | def place_bp(bp, bps): 174 | '''Tell RaceHound to place the given BP and mark the BP as set. 175 | See the Readme for RaceHound for the format and detais.''' 176 | try: 177 | with open(BPS_FILE, 'w') as f: 178 | f.write('{0}\n'.format(bp)) 179 | bps[bp] = True 180 | if verbose: 181 | print('Placed breakpoint at {0}.'.format(bp)) 182 | except OSError as err: 183 | sys.stderr.write( 184 | 'Failed to place a breakpoint at \"{0}\": {1}\n'.format( 185 | bp, err)) 186 | # If the BP cannot be set, it might be incorrect or not applicable 187 | # for some other reason. 188 | del bps[bp] 189 | if not bps: 190 | sys.stderr.write('No valid breakpoints remain, exiting.\n') 191 | sys.exit(1) 192 | 193 | def remove_bp(bp): 194 | '''Tell RaceHound to remove the given BP. 195 | See the Readme for RaceHound for the format and detais.''' 196 | try: 197 | with open(BPS_FILE, 'w') as f: 198 | f.write('-{0}\n'.format(bp)) 199 | if verbose: 200 | print('Removed breakpoint at {0}.'.format(bp)) 201 | except OSError as err: 202 | sys.stderr.write( 203 | 'Failed to remove the breakpoint at \"{0}\": {1}\n'.format( 204 | bp, err)) 205 | 206 | def replace_bp(bp, bps, hit_rate): 207 | '''Remove the given BP and set a new one, if available.''' 208 | remove_bp(bp) 209 | bps[bp] = False 210 | 211 | if hit_rate > max_hit_rate: 212 | if verbose: 213 | print('Hit rate is too high, not adding new BPs this time.') 214 | return 215 | 216 | # The same BP has a chance to be chosen again, which is OK. 217 | add_bps(1, bps) 218 | 219 | 220 | def add_bps(num, bps): 221 | '''Select the given number of BPs randomly and try to set them.''' 222 | available = [bp for bp in bps.keys() if bps[bp] == False] 223 | if not available: 224 | if verbose: 225 | print('No breakpoints can be set.\n') 226 | return 227 | 228 | if num > len(available): 229 | num = len(available) 230 | 231 | selected = rng.sample(available, num) 232 | for bp in selected: 233 | place_bp(bp, bps) 234 | 235 | 236 | if __name__ == '__main__': 237 | desc = 'This script checks the given set of instructions to find races.' 238 | parser = argparse.ArgumentParser(description=desc) 239 | parser.add_argument( 240 | '--num-bps', metavar='N', nargs='?', type=positive_int, 241 | default=num_bps, 242 | help='how many breakpoints to set initially') 243 | parser.add_argument( 244 | '--bps-to-add', metavar='N', nargs='?', type=positive_int, 245 | default=bps_to_add, 246 | help='how many breakpoints to add if hit rate is low') 247 | parser.add_argument( 248 | '-v', '--verbose', action='store_true', 249 | help='output more info about the events, etc.') 250 | parser.add_argument( 251 | '--min-hit-rate', nargs='?', type=float, default=min_hit_rate, 252 | help='the minimum desirable BP hit rate (BPs/sec)') 253 | parser.add_argument( 254 | '--max-hit-rate', nargs='?', type=float, default=max_hit_rate, 255 | help='the maximum desirable BP hit rate (BPs/sec)') 256 | parser.add_argument("input_file", metavar='FILE') 257 | args = parser.parse_args() 258 | 259 | num_bps = args.num_bps 260 | bps_to_add = args.bps_to_add 261 | min_hit_rate = args.min_hit_rate 262 | max_hit_rate = args.max_hit_rate 263 | verbose = args.verbose 264 | 265 | if min_hit_rate < 0 or max_hit_rate < 0: 266 | raise ValueError( 267 | 'min_hit_rate and max_hit_rate should be non-negative.') 268 | if min_hit_rate >= max_hit_rate: 269 | raise ValueError( 270 | 'max_hit_rate should be greater than min_hit_rate.') 271 | 272 | # Mapping: {racing_insns => race_info} 273 | races = {} 274 | 275 | # Mapping BP=>bool (True - BP is set, False - not set) 276 | bps = {} 277 | 278 | with open(args.input_file, 'r') as f: 279 | for line in f: 280 | line = line.strip().lower() 281 | if not line: 282 | continue 283 | if RE_BP_SPEC.match(line): 284 | bps[line] = False 285 | else: 286 | print('Invalid breakpoint specification: %s' % line) 287 | 288 | if not bps: 289 | sys.stderr.write('No valid breakpoints specified.\n') 290 | sys.exit(1) 291 | 292 | if num_bps > len(bps): 293 | num_bps = len(bps) 294 | 295 | for fl in [BPS_FILE, EVENTS_FILE]: 296 | if not os.path.exists(fl): 297 | sys.stderr.write('File not found: %s.\n' % fl) 298 | sys.stderr.write(ERR_NO_FILES) 299 | sys.stderr.write('\n') 300 | sys.exit(1) 301 | 302 | sel = selectors.DefaultSelector() 303 | with open(EVENTS_FILE, 'r') as f: 304 | sel.register(f, selectors.EVENT_READ) 305 | 306 | start_time = datetime.utcnow() 307 | hits = 0 308 | hit_rate = (min_hit_rate + max_hit_rate) / 2.0 309 | 310 | add_bps(num_bps, bps) 311 | 312 | # Poll the "events" file and read the lines from it as they become 313 | # available. 314 | # If the user presses Ctrl-C, just exit. 315 | try: 316 | while True: 317 | events = sel.select(timeout=HIT_CHECK_INTERVAL) 318 | for key, mask in events: 319 | for line in f: 320 | if line.startswith('[race]'): 321 | line = line.rstrip() 322 | store_race_info(races, line) 323 | if verbose: 324 | print(line) 325 | continue 326 | 327 | bp = line.rstrip() 328 | if verbose: 329 | print('BP hit:', bp) 330 | hits = hits + 1 331 | replace_bp(bp, bps, hit_rate) 332 | 333 | elapsed = (datetime.utcnow() - start_time).total_seconds() 334 | if elapsed >= HIT_CHECK_INTERVAL: 335 | hit_rate = float(hits) / elapsed 336 | if hit_rate < min_hit_rate: 337 | if verbose: 338 | print("Hit rate is too low, trying to add BPs.") 339 | add_bps(bps_to_add, bps) 340 | 341 | start_time = datetime.utcnow() 342 | hits = 0 343 | 344 | except KeyboardInterrupt: 345 | print_summary(races) 346 | sys.exit(1) 347 | -------------------------------------------------------------------------------- /examples/events.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # This example demonstrates how to control the set of the locations in the 4 | # binary code of the kernel monitored by RaceHound. 5 | # 6 | # This can be used, for example, to create a system that dynamically adds 7 | # and removes the breakpoints according to some policy to sweep through 8 | # the given area of the kernel and find data races there. Might be similar 9 | # to what DataCollider does for MS Windows. This example does not do this 10 | # yet, however. 11 | # 12 | # The point is, one no longer needs to hack the kernel-mode components to do 13 | # such sweeping with RaceHound. The policies can now be implemented in the 14 | # user space using the interface provided by the kernel-mode part of 15 | # RaceHound via the following files in debugfs: 16 | # 17 | # * racehound/breakpoints - write data here to add or remove the 18 | # breakpoints on the code locations to be monitored. Reading from this 19 | # file lists the currently set breakpoints. 20 | # 21 | # * racehound/events - poll this file to be notified when the breakpoints 22 | # hit or races are found, then read from it to find which events have 23 | # happened. Reading from this file removes the events from the memory 24 | # buffer associated with this file. If the breakpoints hit very often 25 | # and the reader does not keep up, the buffer may become full and the 26 | # new events will be discarded. 27 | # 28 | # That is it. 29 | # 30 | # Note that Python 3.4 or newer is needed here. 31 | # 32 | # Usage (run the script as root): 33 | # events.py [--max-hits=N] [-q] 34 | # 35 | # This script waits on /sys/kernel/debug/racehound/events file and outputs 36 | # information about the events as soon as it is available in that file. 37 | # 38 | # Additionally, if '--max-hits=N' is specified, the breakpoints that hit N 39 | # times or more will be removed. 40 | # 41 | # If -q (--quiet) is present, the script wiil only output a summary of the 42 | # found races at the exit. It will not output the current events when it 43 | # reads them. 44 | # 45 | # The script assumes debugfs is mounted to /sys/kernel/debug/. 46 | 47 | import sys 48 | import os.path 49 | import selectors 50 | import argparse 51 | import re 52 | 53 | 54 | BPS_FILE = '/sys/kernel/debug/racehound/breakpoints' 55 | EVENTS_FILE = '/sys/kernel/debug/racehound/events' 56 | 57 | ERR_NO_FILES = ''.join([ 58 | 'Please check that debugfs is mounted to /sys/kernel/debug ', 59 | 'and kernel module \"racehound\" is loaded.']) 60 | 61 | RE_RACE = re.compile(' '.join([ 62 | r'Detected a data race on the memory block at (0x)?[0-9a-f]+', 63 | r'between the instruction at ([^ \t]+) \(comm: \"(.+)\"\)', 64 | r'and the instruction right before (.*) \(comm: \"(.+)\"\)'])) 65 | 66 | RE_RREAD = re.compile(' '.join([ 67 | r'Detected a data race on the memory block at (0x)?[0-9a-f]+', 68 | r'that is about to be accessed by the instruction at ([^ \t]+)', 69 | r'\(comm: \"(.+)\"\):', 70 | r'the memory block was modified during the delay'])) 71 | 72 | 73 | def positive_int(string): 74 | value = int(string) 75 | if value <= 0: 76 | msg = "%r is not a positive integer" % string 77 | raise argparse.ArgumentTypeError(msg) 78 | return value 79 | 80 | 81 | class RaceGroup(object): 82 | '''A group of races between a given pair of instructions 83 | 84 | 'insn' - address of the instruction that under watch, 85 | 'insn_comm' - 'comm' of the process that executed 'insn', 86 | 'conflict_insn' - the address right after the instruction that performed 87 | a conflicting memory access (None if unknown), 88 | 'conflict_comm' - 'comm' of the process that executed 'conflict_insn' 89 | (None if unknown). 90 | ''' 91 | def __init__(self, insn, insn_comm, conflict_insn=None, 92 | conflict_comm=None): 93 | self.insn = insn 94 | self.insn_comms = [insn_comm] 95 | self.conflict_insn = conflict_insn 96 | if conflict_comm: 97 | self.conflict_comms = [conflict_comm] 98 | else: 99 | self.conflict_comms = [] 100 | 101 | # How many times this race was reported. 102 | self.count = 1 103 | 104 | def print_races(self): 105 | if self.conflict_insn: 106 | print('Race between %s and the insn right before %s.' % 107 | (self.insn, self.conflict_insn)) 108 | comms = list(set(self.insn_comms)) 109 | print('The first insn was executed by:', ', '.join(comms)) 110 | comms = list(set(self.conflict_comms)) 111 | print('The second insn was executed by:', ', '.join(comms)) 112 | else: 113 | print('Race between %s and some other code.' % self.insn) 114 | comms = list(set(self.insn_comms)) 115 | print('The insn was executed by:', ', '.join(comms)) 116 | 117 | print('The race was reported %d time(s).' % self.count) 118 | 119 | 120 | def store_race_info(races, str_race): 121 | matched = re.search(RE_RACE, str_race) 122 | if matched: 123 | _, insn, insn_comm, conflict_insn, conflict_comm = matched.groups() 124 | key = insn + '#' + conflict_insn 125 | else: 126 | matched = re.search(RE_RREAD, str_race) 127 | if not matched: 128 | sys.stderr.write( 129 | 'Unknown format of a race report: "%s".\n' % str_race) 130 | return 131 | 132 | _, insn, insn_comm = matched.groups() 133 | conflict_insn = None 134 | conflict_comm = None 135 | key = insn 136 | 137 | if key in races: 138 | races[key].count = races[key].count + 1 139 | races[key].insn_comms.append(insn_comm) 140 | if conflict_comm: 141 | races[key].conflict_comms.append(conflict_comm) 142 | else: 143 | races[key] = RaceGroup( 144 | insn, insn_comm, conflict_insn, conflict_comm) 145 | 146 | 147 | def print_summary(races): 148 | if not races: 149 | print('No races found.') 150 | else: 151 | for _, grp in races.items(): 152 | grp.print_races() 153 | print('') 154 | 155 | 156 | def remove_bp(bp): 157 | '''Tell RaceHound to remove the given BP. 158 | See the Readme for RaceHound for the format and detais.''' 159 | with open(BPS_FILE, 'w') as f: 160 | f.write('-%s\n' % bp) 161 | 162 | 163 | if __name__ == '__main__': 164 | desc = 'Demo for the API to control RaceHound from user space.' 165 | parser = argparse.ArgumentParser(description=desc) 166 | parser.add_argument( 167 | '--max-hits', metavar='N', nargs='?', type=positive_int, default=0, 168 | help='disable the breakpoint if it hits N times or more') 169 | parser.add_argument( 170 | '-q', '--quiet', action='store_true', 171 | help='do not output the events, just print a summary at exit') 172 | args = parser.parse_args() 173 | 174 | # Mapping: {racing_insns => race_info} 175 | races = {} 176 | 177 | for fl in [BPS_FILE, EVENTS_FILE]: 178 | if not os.path.exists(fl): 179 | sys.stderr.write('File not found: %s.\n' % fl) 180 | sys.stderr.write(ERR_NO_FILES) 181 | sys.stderr.write('\n') 182 | sys.exit(1) 183 | 184 | sel = selectors.DefaultSelector() 185 | with open(EVENTS_FILE, 'r') as f: 186 | sel.register(f, selectors.EVENT_READ) 187 | bp_hits = {} # The mapping {BP_string, number_of_hits} 188 | 189 | # Poll the "events" file and read the lines from it as they become 190 | # available. 191 | # If the user presses Ctrl-C, just exit. 192 | try: 193 | while True: 194 | events = sel.select() 195 | for key, mask in events: 196 | for line in f: 197 | if line.startswith('[race]'): 198 | line = line.rstrip() 199 | store_race_info(races, line) 200 | if not args.quiet: 201 | print(line) 202 | continue 203 | 204 | bp = line.rstrip() 205 | if not args.quiet: 206 | print('BP hit:', bp) 207 | 208 | # Count the number of hits. 209 | # If --max-hits=N is specified and the BP was hit 210 | # N times, remove it. Note that the BP may be hit 211 | # a few more times after this before it is actually 212 | # removed. 213 | if not bp in bp_hits: 214 | bp_hits[bp] = 1 215 | else: 216 | bp_hits[bp] = bp_hits[bp] + 1 217 | 218 | if bp_hits[bp] == args.max_hits: 219 | if not args.quiet: 220 | print( 221 | 'BP %s was hit %d time(s), removing it' % 222 | (bp, args.max_hits)) 223 | remove_bp(bp) 224 | except KeyboardInterrupt: 225 | print_summary(races) 226 | sys.exit(1) 227 | -------------------------------------------------------------------------------- /kernel_patches/Readme.txt: -------------------------------------------------------------------------------- 1 | Additional patches to the kernel that can be useful for RaceHound are stored here. 2 | -------------------------------------------------------------------------------- /kernel_patches/kernel_4.1-4.3/0001-kprobes-x86-boost-Fix-checking-if-there-is-enough-ro.patch: -------------------------------------------------------------------------------- 1 | From f58fd6ef8e0876ef459517a27b0544930fa2dcdb Mon Sep 17 00:00:00 2001 2 | From: Eugene Shatokhin 3 | Date: Fri, 29 May 2015 17:04:40 +0300 4 | Subject: [PATCH 1/2] kprobes/x86: boost: Fix checking if there is enough room 5 | for a jump 6 | 7 | Kprobes' "boost" feature allows to avoid single-stepping in some cases, 8 | along with its overhead. It needs a relative jump placed in the insn 9 | slot right after the instruction. So the length of the instruction plus 10 | 5 (the length of the near relative unconditional jump) must not exceed 11 | the length of the slot. 12 | 13 | However, the code currently checks if that sum is strictly less than 14 | the length of the slot. So "boost" cannot be used for the instructions 15 | of 10 bytes in size (with MAX_INSN_SIZE == 15), like 16 | "movabs $0x45525f4b434f4c43,%rax" (48 b8 43 4c 4f 43 4b 5f 52 45), 17 | "movl $0x1,0xf8dd(%rip)" (c7 05 dd f8 00 00 01 00 00 00), etc. 18 | 19 | This patch fixes that conditional. 20 | 21 | Signed-off-by: Eugene Shatokhin 22 | --- 23 | arch/x86/kernel/kprobes/core.c | 2 +- 24 | 1 file changed, 1 insertion(+), 1 deletion(-) 25 | 26 | diff --git a/arch/x86/kernel/kprobes/core.c b/arch/x86/kernel/kprobes/core.c 27 | index 1deffe6..0a42b76 100644 28 | --- a/arch/x86/kernel/kprobes/core.c 29 | +++ b/arch/x86/kernel/kprobes/core.c 30 | @@ -881,7 +881,7 @@ static void resume_execution(struct kprobe *p, struct pt_regs *regs, 31 | 32 | if (p->ainsn.boostable == 0) { 33 | if ((regs->ip > copy_ip) && 34 | - (regs->ip - copy_ip) + 5 < MAX_INSN_SIZE) { 35 | + (regs->ip - copy_ip) + 5 <= MAX_INSN_SIZE) { 36 | /* 37 | * These instructions can be executed directly if it 38 | * jumps back to correct address. 39 | -- 40 | 2.3.2 41 | 42 | -------------------------------------------------------------------------------- /kernel_patches/kernel_4.1-4.3/0002-kprobes-x86-Use-16-bytes-for-each-instruction-slot-a.patch: -------------------------------------------------------------------------------- 1 | From 97e1c91c1cbaeeb900bd9207465c9479e8d9ec91 Mon Sep 17 00:00:00 2001 2 | From: Eugene Shatokhin 3 | Date: Wed, 3 Jun 2015 10:10:54 +0300 4 | Subject: [PATCH] kprobes/x86: Use 16 bytes for each instruction slot again 5 | 6 | Commit 91e5ed49fca0 ("x86/asm/decoder: Fix and enforce max instruction 7 | size in the insn decoder") has changed MAX_INSN_SIZE from 16 to 15 bytes 8 | on x86. 9 | 10 | As a side effect, the slots Kprobes use to store the instructions became 11 | 1 byte shorter. This is unfortunate because, for example, the Kprobes' 12 | "boost" feature can not be used now for the instructions of length 11, 13 | like a quite common kind of MOV: 14 | * movq $0xffffffffffffffff,-0x3fe8(%rax) (48 c7 80 18 c0 ff ff ff ff ff ff) 15 | * movq $0x0,0x88(%rdi) (48 c7 87 88 00 00 00 00 00 00 00) 16 | and so on. 17 | 18 | This patch makes the insn slots 16 bytes long, like they were before 19 | while keeping MAX_INSN_SIZE intact. 20 | 21 | Other tools may benefit from this change as well. 22 | 23 | Changes in v2: 24 | * Explained in the comments what KPROBE_INSN_SLOT_SIZE is and why it may 25 | be larger than MAX_INSN_SIZE. 26 | * Added a compile-time check that KPROBE_INSN_SLOT_SIZE is not less than 27 | MAX_INSN_SIZE. 28 | 29 | Signed-off-by: Eugene Shatokhin 30 | --- 31 | arch/x86/include/asm/kprobes.h | 15 +++++++++++++++ 32 | arch/x86/kernel/kprobes/core.c | 2 +- 33 | kernel/kprobes.c | 20 ++++++++++++++++++-- 34 | 3 files changed, 34 insertions(+), 3 deletions(-) 35 | 36 | diff --git a/arch/x86/include/asm/kprobes.h b/arch/x86/include/asm/kprobes.h 37 | index 4421b5d..ab6e6a0 100644 38 | --- a/arch/x86/include/asm/kprobes.h 39 | +++ b/arch/x86/include/asm/kprobes.h 40 | @@ -28,6 +28,21 @@ 41 | 42 | #define __ARCH_WANT_KPROBES_INSN_SLOT 43 | 44 | +/* 45 | + * The size of the instruction slot is greater than the maximum length of 46 | + * an instruction (15 bytes) for Kprobes to be able to use "boost" for 47 | + * longer instructions. 48 | + * 49 | + * "Boost" allows to avoid single-stepping over an instruction if the Kprobe 50 | + * has no post handler. A jump to the next instruction is placed after the 51 | + * copied instruction in the slot for that to work. 52 | + * 53 | + * The length of the relative jump instruction is 5 bytes. With the slot 54 | + * size of 16, "boost" can be used for the instructions up to 11 bytes long, 55 | + * including rather common kinds of "MOV r/m64, imm32" (opcode 0xc7). 56 | + */ 57 | +#define KPROBE_INSN_SLOT_SIZE 16 58 | + 59 | struct pt_regs; 60 | struct kprobe; 61 | 62 | diff --git a/arch/x86/kernel/kprobes/core.c b/arch/x86/kernel/kprobes/core.c 63 | index 0a42b76..1067f90 100644 64 | --- a/arch/x86/kernel/kprobes/core.c 65 | +++ b/arch/x86/kernel/kprobes/core.c 66 | @@ -881,7 +881,7 @@ static void resume_execution(struct kprobe *p, struct pt_regs *regs, 67 | 68 | if (p->ainsn.boostable == 0) { 69 | if ((regs->ip > copy_ip) && 70 | - (regs->ip - copy_ip) + 5 <= MAX_INSN_SIZE) { 71 | + (regs->ip - copy_ip) + 5 <= KPROBE_INSN_SLOT_SIZE) { 72 | /* 73 | * These instructions can be executed directly if it 74 | * jumps back to correct address. 75 | diff --git a/kernel/kprobes.c b/kernel/kprobes.c 76 | index c90e417..92788a4 100644 77 | --- a/kernel/kprobes.c 78 | +++ b/kernel/kprobes.c 79 | @@ -57,7 +57,6 @@ 80 | #define KPROBE_HASH_BITS 6 81 | #define KPROBE_TABLE_SIZE (1 << KPROBE_HASH_BITS) 82 | 83 | - 84 | /* 85 | * Some oddball architectures like 64bit powerpc have function descriptors 86 | * so this must be overridable. 87 | @@ -90,6 +89,23 @@ static raw_spinlock_t *kretprobe_table_lock_ptr(unsigned long hash) 88 | static LIST_HEAD(kprobe_blacklist); 89 | 90 | #ifdef __ARCH_WANT_KPROBES_INSN_SLOT 91 | + 92 | +/* 93 | + * An instruction slot contains a copy of the probed instruction, relocated 94 | + * if needed. In some cases, it may be necessary to place additional 95 | + * instructions into that slot, so, the size of the slot may be larger than 96 | + * the maximum length of an instruction. 97 | + * Currently, the slots larger than MAX_INSN_SIZE may only be needed on x86 98 | + * to implement Kprobe "boost". Other architectures do not need to define 99 | + * KPROBE_INSN_SLOT_SIZE explicitly. 100 | + */ 101 | +#ifndef KPROBE_INSN_SLOT_SIZE 102 | +#define KPROBE_INSN_SLOT_SIZE MAX_INSN_SIZE 103 | +#endif 104 | +#if (KPROBE_INSN_SLOT_SIZE < MAX_INSN_SIZE) 105 | +#error "Size of an instruction slot must not be less than MAX_INSN_SIZE." 106 | +#endif 107 | + 108 | /* 109 | * kprobe->ainsn.insn points to the copy of the instruction to be 110 | * single-stepped. x86_64, POWER4 and above have no-exec support and 111 | @@ -135,7 +151,7 @@ struct kprobe_insn_cache kprobe_insn_slots = { 112 | .alloc = alloc_insn_page, 113 | .free = free_insn_page, 114 | .pages = LIST_HEAD_INIT(kprobe_insn_slots.pages), 115 | - .insn_size = MAX_INSN_SIZE, 116 | + .insn_size = KPROBE_INSN_SLOT_SIZE, 117 | .nr_garbage = 0, 118 | }; 119 | static int collect_garbage_slots(struct kprobe_insn_cache *c); 120 | -- 121 | 2.3.2 122 | 123 | -------------------------------------------------------------------------------- /kernel_patches/kernel_4.1-4.3/Readme.txt: -------------------------------------------------------------------------------- 1 | 1. 2 | 0001-kprobes-x86-boost-Fix-checking-if-there-is-enough-ro.patch 3 | 0002-kprobes-x86-Use-16-bytes-for-each-instruction-slot-a.patch 4 | 5 | These two allow Kprobes to make 11-byte instructions (like "MOV r/m64, imm32" (opcode 0xc7)) "boostable". That is, there will be enough room in the buffer containing the insn for a near relative jump as well. 6 | 7 | As RaceHound needs the similar things as the "boost" facilities (enough space in the insn buffer for a jump), these patches help it too. This way it can monitor longer instructions. 8 | 9 | In some cases (12-byte or longer instructions), it may be needed to increase KPROBE_INSN_SLOT_SIZE in arch/x86/include/asm/kprobes.h further. 20 bytes are enough but you may always choose a greater value (say, 32 as a nice round number). 10 | -------------------------------------------------------------------------------- /lines2insns/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(APP_NAME lines2insns) 2 | 3 | enable_language (C) 4 | enable_language(CXX) 5 | ####################################################################### 6 | 7 | include_directories( 8 | "${CMAKE_SOURCE_DIR}" 9 | "${CMAKE_BINARY_DIR}" 10 | "${CMAKE_CURRENT_SOURCE_DIR}" 11 | "${CMAKE_CURRENT_BINARY_DIR}" 12 | ) 13 | 14 | check_dwfl_report_elf() 15 | 16 | # [NB] This file is not named config.h to avoid conflicts with the top-level 17 | # config.h of the project. 18 | configure_file( 19 | "${CMAKE_CURRENT_SOURCE_DIR}/config_${APP_NAME}.h.in" 20 | "${CMAKE_CURRENT_BINARY_DIR}/config_${APP_NAME}.h") 21 | 22 | set(APP_SOURCES 23 | "main.cpp" 24 | "${COMMON_SOURCE_DIR}/inat.c" 25 | "${COMMON_SOURCE_DIR}/insn.c" 26 | "${COMMON_SOURCE_DIR}/util.c" 27 | "${COMMON_SOURCE_DIR}/util.h" 28 | 29 | "${CMAKE_CURRENT_BINARY_DIR}/config_${APP_NAME}.h" 30 | ) 31 | 32 | add_executable(${APP_NAME} ${APP_SOURCES}) 33 | add_dependencies(${APP_NAME} insn_decoder_headers) 34 | 35 | # _GNU_SOURCE is needed because at least getopt_long() is GNU extension. 36 | set_target_properties(${APP_NAME} PROPERTIES 37 | COMPILE_FLAGS "-Wall -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64" 38 | ) 39 | 40 | set_source_files_properties("main.cpp" PROPERTIES 41 | COMPILE_FLAGS ${CPP11_COMPILER_FLAG} 42 | ) 43 | 44 | target_link_libraries(${APP_NAME} "elf" "dw") 45 | ####################################################################### 46 | 47 | install(TARGETS ${APP_NAME} 48 | DESTINATION ${RH_INSTALL_PREFIX_EXEC}) 49 | ####################################################################### 50 | -------------------------------------------------------------------------------- /lines2insns/config_lines2insns.h.in: -------------------------------------------------------------------------------- 1 | #ifndef CONFIG_LINES2INSNS_H_1604_INCLUDED 2 | #define CONFIG_LINES2INSNS_H_1604_INCLUDED 3 | 4 | #define APP_NAME "@APP_NAME@" 5 | 6 | /* Specifies whether dwfl_report_elf() has an additional argument, 7 | * 'add_p_vaddr'. */ 8 | #cmakedefine RH_DWFL_REPORT_ELF_ADD_P_VADDR 9 | 10 | #if defined(RH_DWFL_REPORT_ELF_ADD_P_VADDR) 11 | # define my_dwfl_report_elf(dwfl_, name_, file_name_, fd_, base_) \ 12 | dwfl_report_elf(dwfl_, name_, file_name_, fd_, base_, 0) 13 | #else 14 | # define my_dwfl_report_elf(dwfl_, name_, file_name_, fd_, base_) \ 15 | dwfl_report_elf(dwfl_, name_, file_name_, fd_, base_) 16 | #endif 17 | 18 | #endif /* CONFIG_LINES2INSNS_H_1604_INCLUDED */ 19 | -------------------------------------------------------------------------------- /ma_lines/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Check if the compiler supports -fvisibility option (necessary to build 2 | # the shared library) 3 | include (CheckCCompilerFlag) 4 | check_c_compiler_flag(-fvisibility=hidden GCC_HAS_FVISIBILITY) 5 | if (NOT GCC_HAS_FVISIBILITY) 6 | message(FATAL_ERROR 7 | "This version of GCC does not support \'-fvisibility=hidden\' option.") 8 | endif () 9 | 10 | set (PLUGIN_NAME "ma_lines") 11 | set (PLUGIN_TARGET ${PLUGIN_NAME}-shared) 12 | set (PLUGIN_SOURCES 13 | ma_lines.cpp 14 | common_includes.h 15 | ) 16 | 17 | add_definitions(-Wall -Wextra -fno-rtti) 18 | 19 | # Find the directory with the header files for GCC plugins 20 | execute_process ( 21 | COMMAND ${CMAKE_C_COMPILER} -print-file-name=plugin 22 | OUTPUT_VARIABLE plugin_dir 23 | OUTPUT_STRIP_TRAILING_WHITESPACE 24 | ) 25 | if (NOT plugin_dir) 26 | message (FATAL_ERROR "Failed to find GCC plugin directory") 27 | endif () 28 | 29 | include_directories ( 30 | "${CMAKE_CURRENT_BINARY_DIR}" 31 | "${CMAKE_CURRENT_SOURCE_DIR}" 32 | "${CMAKE_SOURCE_DIR}" 33 | "${plugin_dir}/include" 34 | ) 35 | 36 | set_source_files_properties (${PLUGIN_SOURCES} PROPERTIES 37 | COMPILE_FLAGS "-fvisibility=hidden" 38 | ) 39 | 40 | add_library(${PLUGIN_NAME} MODULE ${PLUGIN_SOURCES}) 41 | set_target_properties(${PLUGIN_NAME} PROPERTIES 42 | PREFIX "" 43 | ) 44 | 45 | install (TARGETS ${PLUGIN_NAME} 46 | RUNTIME DESTINATION ${RH_INSTALL_PREFIX_LIB_AUX} 47 | LIBRARY DESTINATION ${RH_INSTALL_PREFIX_LIB_AUX} 48 | ) 49 | ####################################################################### 50 | 51 | # Testing 52 | add_subdirectory(tests) 53 | ####################################################################### 54 | -------------------------------------------------------------------------------- /ma_lines/Readme.txt: -------------------------------------------------------------------------------- 1 | About 2 | ----- 3 | 4 | GCC plugin "ma_lines" finds the locations in the source code where memory 5 | is read from or written to and outputs these locations 6 | (file:line[:access_type]) to a given file. 7 | 8 | GCC 4.9 or newer is required. 9 | 10 | The plugin operates during the compilation of the kernel and/or modules. 11 | 12 | 'access_type' is 'read' or 'write' depending on how memory is accessed in 13 | that line of the source code. If 'access_type' is omitted, both reads and 14 | writes are possible. 15 | 16 | "ma_lines" only outputs the locations where "potentially global" data are 17 | accessed (that is, the data GCC does not consider function-local). 18 | 19 | How to use 20 | ---------- 21 | 22 | Add 23 | -fplugin= -fplugin-arg-ma_lines-file= 24 | to the compiler options for the source files of your choice. Then build the 25 | kernel or module as usual. 26 | 27 | For the kernel and the modules, there are two places where to add these 28 | options in Kbuild or Makefile: 29 | 30 | * ccflags-y - this way, "ma_lines" will be used for all source files 31 | compiled in the current directory. 32 | 33 | Example: 34 | 35 | ccflags-y += \ 36 | -fplugin=/usr/local/lib/RaceHound/ma_lines.so \ 37 | -fplugin-arg-ma_lines-file=/tmp/ma_lines-ext4.list 38 | 39 | * CFLAGS_.o - "ma_lines" will be used when compiling .c 40 | only (along with the files it #includes). 41 | 42 | Example: 43 | 44 | CFLAGS_r8169.o += \ 45 | -fplugin=/usr/local/lib/RaceHound/ma_lines.so \ 46 | -fplugin-arg-ma_lines-file=/tmp/ma_lines-r8169.list 47 | ------------------- 48 | 49 | The output file(s) will contain one record per line (possibly with 50 | duplicates). For example: 51 | 52 | /home/builder/drivers/net/ethernet/realtek/r8169.c:2038:write 53 | /home/builder/drivers/net/ethernet/realtek/r8169.c:2040:read 54 | /home/builder/drivers/net/ethernet/realtek/r8169.c:2040:write 55 | /home/builder/drivers/net/ethernet/realtek/r8169.c:2043:read 56 | /home/builder/include/uapi/linux/ethtool.h:117:write 57 | /home/builder/include/uapi/linux/ethtool.h:118:write 58 | /home/builder/drivers/net/ethernet/realtek/r8169.c:2099:write 59 | ------------------- 60 | 61 | -------------------------------------------------------------------------------- /ma_lines/common_includes.h: -------------------------------------------------------------------------------- 1 | /* Common #include files */ 2 | #ifndef COMMON_INCLUDES_1814_INCLUDED 3 | #define COMMON_INCLUDES_1814_INCLUDED 4 | 5 | #include "plugin.h" 6 | #include "bversion.h" 7 | #include "config.h" 8 | #include "system.h" 9 | #include "coretypes.h" 10 | #include "tm.h" 11 | #include "tree.h" 12 | #include "version.h" 13 | #include "flags.h" 14 | #include "function.h" 15 | #include "toplev.h" 16 | #include "basic-block.h" 17 | #include "intl.h" 18 | #include "ggc.h" 19 | #include "params.h" 20 | #include "debug.h" 21 | #include "langhooks.h" 22 | #include "cgraph.h" 23 | #include "opts.h" 24 | #include "tree-pretty-print.h" 25 | #include "gimple-pretty-print.h" 26 | #include "c-tree.h" 27 | #include "diagnostic.h" 28 | #include "tree-dump.h" 29 | #include "tree-pass.h" 30 | #include "internal-fn.h" 31 | #include "gimple-expr.h" 32 | #include "context.h" 33 | #include "tree-ssa-alias.h" 34 | #include "stringpool.h" 35 | #include "tree-ssanames.h" 36 | #include "print-tree.h" 37 | #include "tree-eh.h" 38 | #include "tree-nested.h" 39 | #include "gimple.h" 40 | #include "tree-ssa-operands.h" 41 | #include "tree-phinodes.h" 42 | #include "tree-cfg.h" 43 | #include "gimple-iterator.h" 44 | #include "gimple-ssa.h" 45 | #include "ssa-iterators.h" 46 | #include "vec.h" 47 | 48 | #endif /*COMMON_INCLUDES_1814_INCLUDED*/ 49 | -------------------------------------------------------------------------------- /ma_lines/ma_lines.cpp: -------------------------------------------------------------------------------- 1 | /* "ma_lines" - GCC plugin that outputs the list of the source locations 2 | * where memory accesses may happen. 3 | * 4 | * See Readme.txt for the usage details. 5 | * 6 | * This program is free software; you can redistribute it and/or modify it 7 | * under the terms of the GNU General Public License version 2 as published 8 | * by the Free Software Foundation. 9 | * 10 | * Copyright (C) 2014, Eugene Shatokhin 11 | */ 12 | 13 | /* Some of the functions here are based on the implementation of 14 | * ThreadSanitizer from GCC 4.9 (gcc/tsan.c). */ 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #include "common_includes.h" 22 | 23 | #include 24 | #include 25 | 26 | #include 27 | #include 28 | 29 | #include 30 | /* ====================================================================== */ 31 | 32 | /* Use this to mark the functions to be exported from this plugin. The 33 | * remaining functions will not be visible from outside of this plugin even 34 | * if they are not static (-fvisibility=hidden GCC option is used to achieve 35 | * this). */ 36 | #define PLUGIN_EXPORT __attribute__ ((visibility("default"))) 37 | /* ====================================================================== */ 38 | 39 | #if BUILDING_GCC_VERSION >= 6000 40 | typedef gimple * rh_stmt; 41 | #else 42 | typedef gimple rh_stmt; 43 | #endif 44 | /* ====================================================================== */ 45 | 46 | #ifdef __cplusplus 47 | extern "C" { 48 | #endif 49 | 50 | /* This symbol is needed for GCC to know the plugin is GPL-compatible. */ 51 | PLUGIN_EXPORT int plugin_is_GPL_compatible; 52 | 53 | /* Plugin initialization function, the only function to be exported */ 54 | PLUGIN_EXPORT int 55 | plugin_init(struct plugin_name_args *plugin_info, 56 | struct plugin_gcc_version *version); 57 | 58 | #ifdef __cplusplus 59 | } 60 | #endif 61 | /* ====================================================================== */ 62 | 63 | /* The file to dump the info about interesting source locations to. Can be 64 | * specified as an argument to the plugin: 65 | * -fplugin-arg-ma_lines-file= */ 66 | static std::string out_file = "./dump_memory_accesses.list"; 67 | /* ====================================================================== */ 68 | 69 | /* These memory/string operations may be replaced by their implementation 70 | * in assembly in the code. The reads/writes they perform may be needed to 71 | * track. */ 72 | static std::string _funcs[] = { 73 | "strstr", 74 | "strspn", 75 | "strsep", 76 | "strrchr", 77 | "strpbrk", 78 | "strnstr", 79 | "strnlen", 80 | "strnicmp", 81 | "strncpy", 82 | "strncmp", 83 | "strnchr", 84 | "strncat", 85 | "strncasecmp", 86 | "strlen", 87 | "strlcpy", 88 | "strlcat", 89 | "strcspn", 90 | "strcpy", 91 | "strcmp", 92 | "strchr", 93 | "strcat", 94 | "strcasecmp", 95 | "memset", 96 | "memcpy", 97 | "memcmp", 98 | "memscan", 99 | "memmove", 100 | "memchr", 101 | "__memcpy", 102 | }; 103 | static std::set special_functions( 104 | &_funcs[0], &_funcs[0] + sizeof(_funcs) / sizeof(_funcs[0])); 105 | /* ====================================================================== */ 106 | 107 | static void 108 | output_ma_location(rh_stmt stmt, EAccessType ma_type) 109 | { 110 | int ret; 111 | 112 | location_t loc = gimple_location(stmt); 113 | const char *src = LOCATION_FILE(loc); 114 | unsigned int line = LOCATION_LINE(loc); 115 | /* No need to output the column, it seems to be ignored in debug 116 | * info. */ 117 | 118 | if (src == NULL) { 119 | fprintf(stderr, 120 | "[ma_lines] Path to the source file is missing, skipping the statement.\n"); 121 | return; 122 | } 123 | 124 | FILE *out = fopen(out_file.c_str(), "a"); 125 | if (out == NULL) { 126 | fprintf(stderr, 127 | "[ma_lines] Failed to open file \"%s\".\n", out_file.c_str()); 128 | return; 129 | } 130 | 131 | int fd = fileno(out); 132 | if (fd == -1) { 133 | fprintf(stderr, "[ma_lines] Internal error.\n"); 134 | goto end; 135 | } 136 | 137 | /* Lock the output file in case GCC processes 2 or more files at the 138 | * same time. */ 139 | ret = flock(fd, LOCK_EX); 140 | if (ret != 0) { 141 | fprintf(stderr, 142 | "[ma_lines] Failed to lock the output file.\n"); 143 | goto end; 144 | } 145 | 146 | /* Another instance of this plugin might have written to the file 147 | * after we have opened it but before we have got the lock. 148 | * We need to set the current position in the file to its end again. 149 | */ 150 | ret = fseek(out, 0, SEEK_END); 151 | if (ret != 0) { 152 | fprintf(stderr, 153 | "[ma_lines] Failed to seek to the end of file.\n"); 154 | goto unlock; 155 | } 156 | 157 | fprintf(out, "%s:%u", src, line); 158 | if (ma_type == AT_READ) { 159 | fprintf(out, ":read\n"); 160 | } 161 | else if (ma_type == AT_WRITE) { 162 | fprintf(out, ":write\n"); 163 | } 164 | else /* AT_BOTH and all other values */ { 165 | fprintf(out, "\n"); 166 | } 167 | 168 | unlock: 169 | ret = flock(fd, LOCK_UN); 170 | if (ret != 0) { 171 | fprintf(stderr, 172 | "[ma_lines] Failed to unlock the output file.\n"); 173 | } 174 | end: 175 | fclose(out); 176 | } 177 | 178 | /* Report source locations for the calls to the functions of interest (those 179 | * that might be replaced with their implementation in assembly, like 180 | * memcpy(), etc. */ 181 | static void 182 | process_function_call(gimple_stmt_iterator *gsi) 183 | { 184 | rh_stmt stmt = gsi_stmt(*gsi); 185 | tree fndecl = gimple_call_fndecl(stmt); 186 | 187 | if (!fndecl) { 188 | /* Indirect call, nothing to do. */ 189 | return; 190 | } 191 | 192 | const char *name = IDENTIFIER_POINTER(DECL_NAME(fndecl)); 193 | if (special_functions.find(name) != special_functions.end()) { 194 | output_ma_location(stmt, AT_BOTH); 195 | } 196 | } 197 | 198 | static void 199 | process_expr(gimple_stmt_iterator gsi, tree expr, bool is_write) 200 | { 201 | #if BUILDING_GCC_VERSION >= 6000 202 | int reversep = 0; 203 | #endif 204 | rh_stmt stmt = gsi_stmt(gsi); 205 | HOST_WIDE_INT size = int_size_in_bytes(TREE_TYPE (expr)); 206 | if (size < 1) 207 | return; 208 | 209 | /* TODO: Check how this works when bit fields are accessed, update 210 | * if needed (~ report touching the corresponding bytes as a 211 | * whole?) */ 212 | HOST_WIDE_INT bitsize; 213 | HOST_WIDE_INT bitpos; 214 | tree offset; 215 | enum machine_mode mode; 216 | int volatilep = 0; 217 | int unsignedp = 0; 218 | #if BUILDING_GCC_VERSION >= 6000 219 | tree base = get_inner_reference( 220 | expr, &bitsize, &bitpos, &offset, &mode, &unsignedp, 221 | &reversep, &volatilep, false); 222 | #else 223 | tree base = get_inner_reference( 224 | expr, &bitsize, &bitpos, &offset, &mode, &unsignedp, 225 | &volatilep, false); 226 | #endif 227 | if (DECL_P(base)) { 228 | struct pt_solution pt; 229 | memset(&pt, 0, sizeof(pt)); 230 | pt.escaped = 1; 231 | pt.ipa_escaped = flag_ipa_pta != 0; 232 | pt.nonlocal = 1; 233 | if (!pt_solution_includes(&pt, base)) { 234 | //<> 235 | //fprintf(stderr, "[DBG] The decl does not escape.\n"); 236 | //<> 237 | return; 238 | } 239 | if (!is_global_var(base) && !may_be_aliased(base)) { 240 | //<> 241 | //fprintf(stderr, "[DBG] Neither global nor may be aliased.\n"); 242 | //<> 243 | return; 244 | } 245 | } 246 | 247 | if (TREE_READONLY (base) || 248 | (TREE_CODE (base) == VAR_DECL && DECL_HARD_REGISTER (base))) { 249 | //<> 250 | //fprintf(stderr, "[DBG] Read-only or register variable.\n"); 251 | //<> 252 | return; 253 | } 254 | 255 | // TODO: bit field access. How to handle it properly? 256 | if (bitpos % (size * BITS_PER_UNIT) || 257 | bitsize != size * BITS_PER_UNIT) { 258 | return; 259 | } 260 | 261 | output_ma_location(stmt, (is_write ? AT_WRITE : AT_READ)); 262 | } 263 | 264 | static void 265 | process_gimple(gimple_stmt_iterator *gsi) 266 | { 267 | rh_stmt stmt; 268 | stmt = gsi_stmt(*gsi); 269 | 270 | if (is_gimple_call(stmt)) { 271 | process_function_call(gsi); 272 | } 273 | else if (is_gimple_assign(stmt) && !gimple_clobber_p(stmt)) { 274 | if (gimple_store_p(stmt)) { 275 | tree lhs = gimple_assign_lhs(stmt); 276 | process_expr(*gsi, lhs, true); 277 | } 278 | if (gimple_assign_load_p(stmt)) { 279 | tree rhs = gimple_assign_rhs1(stmt); 280 | process_expr(*gsi, rhs, false); 281 | } 282 | } 283 | } 284 | 285 | /* The main function of the pass. Called for each function to be processed. 286 | */ 287 | static unsigned int 288 | do_execute(function *func) 289 | { 290 | //<> 291 | /*fprintf(stderr, "[DBG] Processing function \"%s\".\n", 292 | current_function_name());*/ 293 | //<> 294 | 295 | basic_block bb; 296 | gimple_stmt_iterator gsi; 297 | 298 | FOR_EACH_BB_FN (bb, func) { 299 | for (gsi = gsi_start_bb(bb); !gsi_end_p(gsi); 300 | gsi_next(&gsi)) { 301 | process_gimple(&gsi); 302 | } 303 | } 304 | return 0; 305 | } 306 | /* ====================================================================== */ 307 | 308 | static const struct pass_data my_pass_data = { 309 | .type = GIMPLE_PASS, 310 | .name = "racehound_ma_lines", 311 | .optinfo_flags = OPTGROUP_NONE, 312 | 313 | #if BUILDING_GCC_VERSION < 5000 314 | /* GCC 4.9 */ 315 | .has_gate = false, 316 | .has_execute = true, 317 | #endif 318 | .tv_id = TV_NONE, 319 | .properties_required = PROP_ssa | PROP_cfg, 320 | .properties_provided = 0, 321 | .properties_destroyed = 0, 322 | .todo_flags_start = 0, 323 | .todo_flags_finish = TODO_verify_all | TODO_update_ssa 324 | }; 325 | 326 | namespace { 327 | class my_pass : public gimple_opt_pass { 328 | public: 329 | my_pass() 330 | : gimple_opt_pass(my_pass_data, g) 331 | {} 332 | # if BUILDING_GCC_VERSION >= 5000 333 | bool gate (function *) { return true; } 334 | virtual unsigned int execute(function *func) 335 | { 336 | return do_execute(func); 337 | } 338 | # else 339 | unsigned int execute() { return do_execute(cfun); } 340 | # endif 341 | }; /* class my_pass */ 342 | } /* anon namespace */ 343 | 344 | static struct opt_pass *make_my_pass(void) 345 | { 346 | return new my_pass(); 347 | } 348 | /* ====================================================================== */ 349 | 350 | int 351 | plugin_init(struct plugin_name_args *plugin_info, 352 | struct plugin_gcc_version *version) 353 | { 354 | struct register_pass_info pass_info; 355 | 356 | if (!plugin_default_version_check(version, &gcc_version)) 357 | return 1; 358 | 359 | pass_info.pass = make_my_pass(); 360 | 361 | /* For some reason, GCC does not allow to place this pass after 362 | * "inline" IPA pass, complaining that the latter is not found. 363 | * So I place the new pass before pass_lower_eh_dispatch ("ehdisp") 364 | * which is guaranteed to execute after all IPA passes. */ 365 | pass_info.reference_pass_name = "ehdisp"; 366 | /* consider only the 1st occasion of the reference pass */ 367 | pass_info.ref_pass_instance_number = 1; 368 | pass_info.pos_op = PASS_POS_INSERT_BEFORE; 369 | 370 | if (plugin_info->argc > 0) { 371 | struct plugin_argument *arg = &plugin_info->argv[0]; 372 | if (strcmp(arg->key, "file") == 0) 373 | out_file = arg->value; 374 | } 375 | 376 | /* Register the pass */ 377 | register_callback(plugin_info->base_name, PLUGIN_PASS_MANAGER_SETUP, 378 | NULL, &pass_info); 379 | return 0; 380 | } 381 | /* ====================================================================== */ 382 | -------------------------------------------------------------------------------- /ma_lines/tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(PLUGIN_PATH "${CMAKE_BINARY_DIR}/${PLUGIN_NAME}/${PLUGIN_NAME}.so") 2 | 3 | # Configure the test scripts and other files 4 | configure_file("${CMAKE_CURRENT_SOURCE_DIR}/test.sh.in" 5 | "${CMAKE_CURRENT_BINARY_DIR}/test.sh" 6 | @ONLY) 7 | 8 | configure_file("${CMAKE_CURRENT_SOURCE_DIR}/Kbuild.in" 9 | "${CMAKE_CURRENT_BINARY_DIR}/Kbuild" 10 | @ONLY) 11 | 12 | rh_test_add_script("ma_lines.01" "test.sh") 13 | -------------------------------------------------------------------------------- /ma_lines/tests/Kbuild.in: -------------------------------------------------------------------------------- 1 | module_name=test_common_target 2 | 3 | ccflags-y := -g -I$(src) 4 | 5 | obj-m := ${module_name}.o 6 | ${module_name}-y := cfake.o 7 | 8 | # To analyze the kernel code, one should set "-fplugin=..." for the relevant 9 | # files only. Other files will not be affected. 10 | CFLAGS_cfake.o := \ 11 | -fplugin=@PLUGIN_PATH@ \ 12 | -fplugin-arg-@PLUGIN_NAME@-file=@CMAKE_CURRENT_BINARY_DIR@/build01/ma_lines_out.list 13 | -------------------------------------------------------------------------------- /ma_lines/tests/test.sh.in: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | ######################################################################## 4 | # This test checks if test_common_target.ko module can be built with 5 | # ma_lines enabled and that the latter finds the source lines with racy 6 | # accesses. 7 | # 8 | # Usage: 9 | # sh test.sh 10 | ######################################################################## 11 | 12 | # Just in case the tools like insmod/rmmod are not in their usual locations. 13 | export PATH=$PATH:/sbin:/bin:/usr/bin:/usr/sbin 14 | ######################################################################## 15 | 16 | doTest() 17 | { 18 | # Build the module 19 | make -C ${KBUILD_DIR} M=$(pwd) modules 20 | if test $? -ne 0; then 21 | printf "Failed to build the module.\n" 22 | exit 1 23 | fi 24 | 25 | # Check the list of the source lines the plugin has found. 26 | for expected in "cfake.c:82:read" "cfake.c:84:write"; do 27 | grep -F $expected ./ma_lines_out.list > /dev/null 28 | if test $? -ne 0; then 29 | printf "Failed to find \"$expected\" in ma_lines_out.list\n" 30 | exit 1 31 | fi 32 | done 33 | 34 | # Check if the module is operational. 35 | insmod ./test_common_target.ko 36 | if test $? -ne 0; then 37 | printf "Failed to load test_common_target.ko.\n" 38 | exit 1 39 | fi 40 | 41 | success=1 42 | echo "Abracadabra" > /dev/cfake0 43 | if test $? -ne 0; then 44 | success=0 45 | printf "Failed to write data to /dev/cfake0.\n" 46 | fi 47 | 48 | printf "Reading: " 49 | dd if=/dev/cfake0 bs=30 count=1 50 | if test $? -ne 0; then 51 | success=0 52 | printf "Failed to read data from /dev/cfake0.\n" 53 | fi 54 | 55 | rmmod test_common_target 56 | if test $? -ne 0; then 57 | printf "Failed to unload \"test_common_target\" module.\n" 58 | exit 1 59 | fi 60 | 61 | if test $success -ne 1; then 62 | exit 1 63 | fi 64 | } 65 | ######################################################################## 66 | 67 | WORK_DIR=${PWD} 68 | SRCDIR="@CMAKE_SOURCE_DIR@/core/tests/common_target" 69 | BUILDDIR="@CMAKE_CURRENT_BINARY_DIR@/build01" 70 | KBUILD_DIR=/lib/modules/$(uname -r)/build 71 | 72 | rm -rf "${BUILDDIR}" 73 | mkdir -p "${BUILDDIR}" 74 | if test $? -ne 0; then 75 | printf "Failed to create directory ${BUILDDIR}\n" 76 | exit 1 77 | fi 78 | 79 | pushd "${SRCDIR}" || exit 1 80 | for ff in cfake.c cfake.h; do 81 | cp ${ff} "${BUILDDIR}/" 82 | if test $? -ne 0; then 83 | printf "Failed to copy ${ff}.\n" 84 | fi 85 | done 86 | popd 87 | 88 | cp "@CMAKE_CURRENT_BINARY_DIR@/Kbuild" "${BUILDDIR}/" 89 | if test $? -ne 0; then 90 | printf "Failed to copy Kbuild file.\n" 91 | fi 92 | 93 | pushd "${BUILDDIR}" || exit 1 94 | doTest 95 | popd 96 | 97 | # Test passed 98 | exit 0 99 | --------------------------------------------------------------------------------