├── .bumpversion.cfg ├── .cmake └── Toolchains │ ├── Toolchain-gcc-darwin-i686.cmake │ ├── Toolchain-gcc-generic.cmake │ ├── Toolchain-gcc-i686.cmake │ └── Toolchain-mingw-w64.cmake ├── .gitignore ├── .travis.yml ├── CMakeLists.txt ├── ChangeLog ├── LICENSE ├── README.md ├── appveyor.yml ├── doc └── Doxyfile ├── include ├── mimick.h └── mimick │ ├── alloc.h │ ├── assert.h │ ├── matcher.h │ ├── mock.h │ ├── preprocess.h │ ├── string.h │ ├── unmocked.h │ ├── va.h │ ├── verify.h │ └── when.h ├── sample ├── CMakeLists.txt └── strdup │ ├── CMakeLists.txt │ ├── strdup.c │ ├── strdup.h │ └── test.c ├── src ├── CMakeLists.txt ├── asm │ ├── mangling.h │ ├── mangling │ │ ├── leading-underscore.h │ │ └── none.h │ ├── trampoline-aarch64.S │ ├── trampoline-arm.S │ ├── trampoline-i386-cdecl.S │ ├── trampoline-i386-cdecl.asm │ ├── trampoline-x86_64-systemv.S │ ├── trampoline-x86_64-win.S │ └── trampoline-x86_64-win.asm ├── common.h ├── config.h.in ├── core.c ├── core.h ├── matcher.c ├── mock.c ├── mock.h ├── plt-elf.c ├── plt-elf.h ├── plt-mach-o.c ├── plt-mach-o.h ├── plt-pe.c ├── plt-pe.h ├── plt.c ├── plt.h ├── stub.c ├── stub.h ├── threadlocal.h ├── trampoline.c ├── trampoline.h ├── verify.c ├── vitals.c ├── vitals.h └── when.c └── test ├── CMakeLists.txt ├── libfoo.c ├── libfoo.h └── test.c /.bumpversion.cfg: -------------------------------------------------------------------------------- 1 | [bumpversion] 2 | current_version = 0.3.0 3 | commit = False 4 | 5 | [bumpversion:file:appveyor.yml] 6 | 7 | [bumpversion:file:README.md] 8 | 9 | [bumpversion:file:doc/Doxyfile] 10 | 11 | -------------------------------------------------------------------------------- /.cmake/Toolchains/Toolchain-gcc-darwin-i686.cmake: -------------------------------------------------------------------------------- 1 | SET (CMAKE_SYSTEM_NAME Darwin) 2 | SET (CMAKE_SYSTEM_PROCESSOR i686) 3 | 4 | SET (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -m32" CACHE STRING "C Flags") 5 | SET (CMAKE_ASM_FLAGS "${CMAKE_C_FLAGS} -m32" CACHE STRING "ASM Flags") 6 | -------------------------------------------------------------------------------- /.cmake/Toolchains/Toolchain-gcc-generic.cmake: -------------------------------------------------------------------------------- 1 | SET(CMAKE_SYSTEM_NAME Linux) 2 | SET(CMAKE_SYSTEM_PROCESSOR ${ARCH}) 3 | 4 | SET(CMAKE_C_COMPILER ${ARCH}-${TARGET}-gcc) 5 | 6 | SET(CMAKE_FIND_ROOT_PATH /usr/${ARCH}-${TARGET}) 7 | set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) 8 | set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) 9 | set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) 10 | -------------------------------------------------------------------------------- /.cmake/Toolchains/Toolchain-gcc-i686.cmake: -------------------------------------------------------------------------------- 1 | SET (CMAKE_SYSTEM_NAME Linux) 2 | SET (CMAKE_SYSTEM_PROCESSOR i686) 3 | 4 | SET (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -m32" CACHE STRING "C Flags") 5 | SET (CMAKE_ASM_FLAGS "${CMAKE_C_FLAGS} -m32" CACHE STRING "ASM Flags") 6 | -------------------------------------------------------------------------------- /.cmake/Toolchains/Toolchain-mingw-w64.cmake: -------------------------------------------------------------------------------- 1 | # the name of the target operating system 2 | set(CMAKE_SYSTEM_NAME Windows) 3 | 4 | if (CMAKE_SYSTEM_PROCESSOR MATCHES "^x86$") 5 | set (CMAKE_SYSTEM_PROCESSOR i686) 6 | set (BITS 32) 7 | elseif (CMAKE_SYSTEM_PROCESSOR MATCHES "^x64$") 8 | set (CMAKE_SYSTEM_PROCESSOR x86_64) 9 | set (BITS 64) 10 | endif () 11 | 12 | if (WIN32) 13 | set(CMAKE_FIND_ROOT_PATH "C:/msys64/mingw${BITS}" "C:/msys64/mingw${BITS}/${CMAKE_SYSTEM_PROCESSOR}-w64-mingw32") 14 | set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM ONLY) 15 | set(ENV{PATH} "C:\\msys64\\mingw${BITS}\\bin;C:\\msys64\\mingw${BITS}\\${CMAKE_SYSTEM_PROCESSOR}-w64-mingw32\\bin;$ENV{PATH}") 16 | else () 17 | set(CMAKE_FIND_ROOT_PATH /usr/${CMAKE_SYSTEM_PROCESSOR}-w64-mingw32) 18 | set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) 19 | endif () 20 | 21 | # which compilers to use for C and C++ 22 | set(CMAKE_AR ${CMAKE_SYSTEM_PROCESSOR}-w64-mingw32-gcc-ar CACHE FILEPATH "Archiver") 23 | set(CMAKE_C_COMPILER ${CMAKE_SYSTEM_PROCESSOR}-w64-mingw32-gcc) 24 | set(CMAKE_CXX_COMPILER ${CMAKE_SYSTEM_PROCESSOR}-w64-mingw32-g++) 25 | set(CMAKE_RC_COMPILER ${CMAKE_SYSTEM_PROCESSOR}-w64-mingw32-windres) 26 | 27 | # adjust the default behaviour of the FIND_XXX() commands: 28 | # search headers and libraries in the target environment, search 29 | # programs in the host environment 30 | set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) 31 | set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) 32 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !*/ 3 | 4 | !.gitignore 5 | !.bumpversion.cfg 6 | !.ci/* 7 | !.cmake/* 8 | 9 | !*.c 10 | !*.h 11 | !*.S 12 | !*.asm 13 | !*.in 14 | !.cmake/*/*.cmake 15 | 16 | !LICENSE 17 | !ChangeLog 18 | 19 | CMakeFiles/ 20 | !CMakeLists.txt 21 | 22 | build 23 | 24 | *~ 25 | *.swp 26 | .*.swp 27 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: c 2 | 3 | sudo: false 4 | 5 | _packages: 6 | - &i386-packages 7 | apt: 8 | packages: 9 | - gcc-multilib 10 | - &arm-packages 11 | apt: 12 | packages: 13 | - qemu-user 14 | - gcc-arm-linux-gnueabihf 15 | - libc6-dev-armhf-cross 16 | - &aarch64-packages 17 | apt: 18 | packages: 19 | - qemu-user 20 | - gcc-aarch64-linux-gnu 21 | - libc6-dev-arm64-cross 22 | 23 | _environments: 24 | - &i386-env 25 | - TOOLCHAIN=gcc-i686 26 | - &i386-darwin-env 27 | - TOOLCHAIN=gcc-darwin-i686 28 | - &arm-env 29 | - ARCH=arm 30 | - TARGET=linux-gnueabihf 31 | - TOOLCHAIN=gcc-generic 32 | - CMAKE_OPTS=-DUSE_QEMU=ON 33 | - &aarch64-env 34 | - ARCH=aarch64 35 | - TARGET=linux-gnu 36 | - TOOLCHAIN=gcc-generic 37 | - CMAKE_OPTS=-DUSE_QEMU=ON 38 | - &coverity-key 39 | secure: "L/VOabxezhZm26zaNQN0KjIvNukSzkxdC3bgMZ6ZIA0WUP5WcyqxNTfrlM0SpN04Mu5NReItJDCFGSWcqqkuRVs1Va6xIdsTtmiM0xLeQWOx2s0bwpaxt+2jJ7RlBjVNRrkR5DnJTL5uOztEyxYIYkJc9rswWFDJjBKqcdlXzEE3m6m4hUSoSAoq58EvjAc8CKYMvbLIcN/b77ZGc/IhblrDfI6rxmCA7xq2tDXFSk48snhFQ5NeF3KeQxNH6jxxt4rVU6LLlC85E/YRH9GXu1uxW4O7Vf5bmznpYnGoBwAR7y3IeJp5n2xM8vghD+QPd/94Xy/vYn3gFT0pk/evO8vEgO9Vs3Jj+CyO19m2C+a0hGjukL/97rrtyCNaDtWTzVTjCbWJHqXk1D8W267xywxLggCDN+hu/wnBGqXmwYp4wtXONZaE/aBU6mvHHWonq/8n7q7Sg8OVNMSGyI1MP1zATkX2SzX2fKafRk0G8cj2T98xHk+zNnguvCAFucvaefCr+J9wt+XhI3zjGVO3wdcQztrL3gLefNFEJCiiMAd/aap+siFLpVKfbMEyQmHBe0KiAZobrRcuVz7wroc8EEItszhGcv+Y/Ezre1k30FDVRslUOo420xpZIB0YfwkqE1+sW/9V875Gm4IPhEteY7uPIxlUlWAV3xNvFKnPImM=" 40 | 41 | matrix: 42 | include: 43 | # Linux 44 | # x86_64 45 | - env: CONFIGURATION=Debug 46 | compiler: gcc 47 | - env: CONFIGURATION=Release 48 | compiler: gcc 49 | - env: CONFIGURATION=RelWithDebInfo 50 | compiler: gcc 51 | # i386 52 | - env: [CONFIGURATION=Debug, *i386-env] 53 | addons: *i386-packages 54 | compiler: gcc 55 | - env: [CONFIGURATION=Release, *i386-env] 56 | addons: *i386-packages 57 | compiler: gcc 58 | - env: [CONFIGURATION=RelWithDebInfo, *i386-env] 59 | addons: *i386-packages 60 | compiler: gcc 61 | # ARM 62 | - env: [CONFIGURATION=Debug, *arm-env] 63 | addons: *arm-packages 64 | compiler: gcc 65 | - env: [CONFIGURATION=Release, *arm-env] 66 | addons: *arm-packages 67 | compiler: gcc 68 | - env: [CONFIGURATION=RelWithDebInfo, *arm-env] 69 | addons: *arm-packages 70 | compiler: gcc 71 | # AArch64 72 | - env: [CONFIGURATION=Debug, *aarch64-env] 73 | sudo: required 74 | dist: trusty 75 | addons: *aarch64-packages 76 | compiler: gcc 77 | - env: [CONFIGURATION=Release, *aarch64-env] 78 | sudo: required 79 | dist: trusty 80 | addons: *aarch64-packages 81 | compiler: gcc 82 | - env: [CONFIGURATION=RelWithDebInfo, *aarch64-env] 83 | sudo: required 84 | dist: trusty 85 | addons: *aarch64-packages 86 | compiler: gcc 87 | 88 | # OS X 89 | # x86_64 90 | - env: CONFIGURATION=Debug 91 | os: osx 92 | compiler: clang 93 | - env: CONFIGURATION=Release 94 | os: osx 95 | compiler: clang 96 | - env: CONFIGURATION=RelWithDebInfo 97 | os: osx 98 | compiler: clang 99 | - env: [CONFIGURATION=Debug, *i386-darwin-env] 100 | os: osx 101 | compiler: clang 102 | - env: [CONFIGURATION=Release, *i386-darwin-env] 103 | os: osx 104 | compiler: clang 105 | - env: [CONFIGURATION=RelWithDebInfo, *i386-darwin-env] 106 | os: osx 107 | compiler: clang 108 | 109 | # Coverity scan, when applicable 110 | - compiler: gcc 111 | env: [*coverity-key] 112 | 113 | allow_failures: 114 | - env: [*coverity-key] 115 | 116 | before_install: 117 | - if [ -z "${TARGET}" ]; then export TARGET=${ARCH}; fi 118 | - if [ -n "${SUDO_PKG}" ]; then sudo apt-get install -qq ${SUDO_PKG}; fi 119 | 120 | script: 121 | - mkdir -p build && cd $_ 122 | - | 123 | if [ -n "${TOOLCHAIN}" ]; then 124 | TC_FILE="../.cmake/Toolchains/Toolchain-${TOOLCHAIN}.cmake" 125 | if [ -n "${ARCH}" ]; then 126 | export CMAKE_OPTS="${CMAKE_OPTS} -DARCH=${ARCH}" 127 | export CMAKE_OPTS="${CMAKE_OPTS} -DTARGET=${TARGET}" 128 | fi 129 | export CMAKE_OPTS="${CMAKE_OPTS} -DCMAKE_TOOLCHAIN_FILE=${TC_FILE}" 130 | fi 131 | - > 132 | cmake 133 | -Wno-dev 134 | -DCMAKE_BUILD_TYPE=${CONFIGURATION} 135 | -DCMAKE_INSTALL_PREFIX=mimick-${TRAVIS_TAG} 136 | -DCMAKE_C_FLAGS="-Werror" 137 | ${CMAKE_OPTS} 138 | .. 139 | - TERM=dumb cmake --build . -- -j4 140 | - | 141 | if [ "${TESTS:-ON}" = "ON" ]; then 142 | ctest 143 | fi 144 | 145 | addons: 146 | coverity_scan: 147 | project: 148 | name: "Snaipe/Mimick" 149 | description: "A KISS, cross-platform C Mocking library" 150 | notification_email: franklinmathieu@gmail.com 151 | build_command_prepend: "cmake ." 152 | build_command: "make -j4" 153 | branch_pattern: coverity_scan 154 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2016 Franklin "Snaipe" Mathieu. 2 | # Redistribution and use of this file is allowed according to the terms of the MIT license. 3 | # For details see the LICENSE file distributed with Mimick. 4 | 5 | cmake_minimum_required (VERSION 2.8) 6 | 7 | project (Mimick C) 8 | 9 | include (CheckSymbolExists) 10 | include (GNUInstallDirs) 11 | 12 | set (MODULE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/.cmake/Modules") 13 | set (CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${MODULE_DIR}) 14 | 15 | set (I386 "^(i[3-7]|x)86$") 16 | set (AMD64 "^(x86_|x86-|AMD|amd|x)64$") 17 | set (ARM32 "^(arm|ARM|A)(32)?$") 18 | set (ARM64 "^(arm|ARM|A|aarch|AARCH)64$") 19 | 20 | if (CMAKE_GENERATOR_PLATFORM) 21 | set (_ARCH "${CMAKE_GENERATOR_PLATFORM}") 22 | else () 23 | set (_ARCH "${CMAKE_SYSTEM_PROCESSOR}") 24 | endif () 25 | 26 | set (MMK_MANGLING "none") 27 | 28 | if (MSVC) 29 | if (NOT CMAKE_GENERATOR_PLATFORM) 30 | set (_ARCH "x86") 31 | endif () 32 | 33 | enable_language (ASM_MASM) 34 | if (CMAKE_GENERATOR MATCHES "^.*Win64$" OR _ARCH MATCHES "${AMD64}") 35 | set (MMK_ARCH "x86_64") 36 | set (MMK_ABI "win") 37 | set (MMK_BITS 64) 38 | set (MMK_ARCH_x86_64 1) 39 | elseif (_ARCH MATCHES "${I386}") 40 | set (MMK_ARCH "i386") 41 | set (MMK_ABI "cdecl") 42 | set (MMK_BITS 32) 43 | set (MMK_ARCH_x86 1) 44 | set (MMK_MANGLING "leading-underscore") 45 | set (CMAKE_ASM_MASM_FLAGS "${CMAKE_ASM_MASM_FLAGS} /safeseh") 46 | else () 47 | message (FATAL_ERROR "Architecture '${_ARCH}' is not supported.") 48 | endif () 49 | set (ASM_EXTENSION ".asm") 50 | else () 51 | set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99 -Wall -Wextra -Wno-unused-parameter") 52 | 53 | enable_language (ASM) 54 | if (_ARCH MATCHES "${I386}") 55 | set (MMK_ARCH "i386") 56 | set (MMK_ABI "cdecl") 57 | set (MMK_BITS 32) 58 | set (MMK_ARCH_x86 1) 59 | elseif (_ARCH MATCHES "${AMD64}") 60 | if (WIN32) 61 | set (MMK_ABI "win") 62 | else () 63 | set (MMK_ABI "systemv") 64 | if (CMAKE_C_COMPILER_ID MATCHES "GNU") 65 | set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fcf-protection") 66 | set (CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,-zibt -Wl,-zshstk") 67 | set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-zibt -Wl,-zshstk") 68 | endif () 69 | endif () 70 | set (MMK_ARCH "x86_64") 71 | set (MMK_BITS 64) 72 | set (MMK_ARCH_x86_64 1) 73 | elseif (_ARCH MATCHES "${ARM32}") 74 | set (MMK_ARCH "arm") 75 | set (MMK_ABI "arm") 76 | set (MMK_BITS 32) 77 | set (MMK_ARCH_ARM 1) 78 | elseif (_ARCH MATCHES "${ARM64}") 79 | set (MMK_ARCH "aarch64") 80 | set (MMK_ABI "aarch64") 81 | set (MMK_BITS 64) 82 | set (MMK_ARCH_ARM64 1) 83 | else () 84 | message (FATAL_ERROR "Architecture '${_ARCH}' is not supported.") 85 | endif () 86 | if (APPLE) 87 | set (MMK_MANGLING "leading-underscore") 88 | endif() 89 | set (ASM_EXTENSION ".S") 90 | endif () 91 | 92 | option (USE_QEMU "Use QEMU to run the tests" OFF) 93 | 94 | if (CMAKE_SYSTEM_NAME MATCHES "Linux") 95 | set (MMK_EXE_FORMAT elf) 96 | set (MMK_EXE_FMT_ELF 1) 97 | add_definitions(-D_GNU_SOURCE) 98 | elseif (CMAKE_SYSTEM_NAME MATCHES "Darwin") 99 | set (MMK_EXE_FORMAT mach-o) 100 | set (MMK_EXE_FMT_MACH_O 1) 101 | set (MMK_MANGLING "leading-underscore") 102 | elseif (CMAKE_SYSTEM_NAME MATCHES "Windows") 103 | set (MMK_EXE_FORMAT pe) 104 | set (MMK_EXE_FMT_PE 1) 105 | add_definitions (-D_CRT_SECURE_NO_WARNINGS) 106 | add_definitions (-DWIN32_LEAN_AND_MEAN) 107 | if (MMK_ARCH_x86) 108 | set (MMK_MANGLING "leading-underscore") 109 | endif () 110 | elseif (CMAKE_SYSTEM_NAME MATCHES "(Free|Net|Open)BSD") 111 | set (MMK_EXE_FORMAT elf) 112 | set (MMK_EXE_FMT_ELF 1) 113 | elseif (CMAKE_SYSTEM_NAME MATCHES "Solaris|SunOS") 114 | set (MMK_EXE_FORMAT elf) 115 | set (MMK_EXE_FMT_ELF 1) 116 | elseif (CMAKE_SYSTEM_NAME MATCHES "Generic") 117 | option (EXE_FORMAT "The executable format" "") 118 | if (NOT EXE_FORMAT) 119 | message (WARNING "No executable format specified for Generic platform, assuming elf") 120 | set (MMK_EXE_FORMAT elf) 121 | set (MMK_EXE_FMT_ELF 1) 122 | else () 123 | set (MMK_EXE_FORMAT "${EXE_FORMAT}") 124 | string (TOUPPER "${EXE_FORMAT}" _EXE_SUFFIX) 125 | string (REGEX REPLACE "[^A-Z0-9_]" "_" _EXE_SUFFIX "${_EXE_SUFFIX}") 126 | set ("MMK_EXE_FMT_${_EXE_SUFFIX}" 1) 127 | endif () 128 | else () 129 | message (FATAL_ERROR "Platform '${CMAKE_SYSTEM_NAME}' is not supported.") 130 | endif () 131 | 132 | function (mmk_check_type_exists _T _H _VAR) 133 | include (CheckCSourceCompiles) 134 | check_c_source_compiles (" 135 | #include <${_H}> 136 | typedef ${_T} checked_type; 137 | int main(void) { return 0; } 138 | " ${_VAR}) 139 | endfunction () 140 | 141 | list(APPEND CMAKE_REQUIRED_DEFINITIONS -D_GNU_SOURCE) 142 | 143 | check_symbol_exists(__stdio_common_vfprintf stdio.h HAVE___STDIO_COMMON_VFPRINTF) 144 | 145 | if (MMK_EXE_FMT_ELF) 146 | check_symbol_exists(_r_debug link.h HAVE__R_DEBUG) 147 | check_symbol_exists(_DYNAMIC link.h HAVE__DYNAMIC) 148 | 149 | mmk_check_type_exists(Elf${MMK_BITS}_auxv_t elf.h HAVE_ELF_AUXV_T) 150 | mmk_check_type_exists(Elf${MMK_BITS}_Auxinfo elf.h HAVE_ELF_AUXINFO) 151 | endif () 152 | 153 | if (NOT CMAKE_SYSTEM_NAME MATCHES "Windows") 154 | check_symbol_exists(mmap sys/mman.h HAVE_MMAP) 155 | if (NOT HAVE_MMAP) 156 | message (FATAL_ERROR "Mimick require mmap on POSIX platforms") 157 | endif () 158 | check_symbol_exists(MAP_ANONYMOUS sys/mman.h HAVE_MMAP_MAP_ANONYMOUS) 159 | check_symbol_exists(MAP_ANON sys/mman.h HAVE_MMAP_MAP_ANON) 160 | endif () 161 | 162 | list(REMOVE_ITEM CMAKE_REQUIRED_DEFINITIONS -D_GNU_SOURCE) 163 | 164 | include_directories(include src "${PROJECT_BINARY_DIR}/src") 165 | add_subdirectory (src) 166 | 167 | add_library (mimick STATIC ${SOURCE_FILES}) 168 | 169 | foreach (F ${INTERFACE_FILES}) 170 | get_filename_component(DEST "${F}" PATH) 171 | install(FILES "${F}" DESTINATION "${DEST}") 172 | endforeach () 173 | 174 | install(TARGETS mimick 175 | RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} 176 | LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} 177 | ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) 178 | 179 | add_custom_target(mimick_tests ALL) 180 | 181 | function (add_mimick_sample _NAME) 182 | add_dependencies (mimick_tests ${_NAME}) 183 | 184 | if (USE_QEMU) 185 | add_test (${_NAME} qemu-${MMK_ARCH} -L "${CMAKE_FIND_ROOT_PATH}" ${_NAME}) 186 | else () 187 | add_test (${_NAME} ${_NAME}) 188 | endif () 189 | endfunction () 190 | 191 | function (add_mimick_test _NAME) 192 | add_executable (${_NAME} EXCLUDE_FROM_ALL ${ARGN}) 193 | 194 | if (NOT MSVC) 195 | foreach (ARG ${ARGN}) 196 | set_source_files_properties (${ARG} PROPERTIES COMPILE_FLAGS -O0) 197 | endforeach () 198 | endif () 199 | 200 | target_link_libraries (${_NAME} mimick foo) 201 | add_mimick_sample(${_NAME}) 202 | endfunction () 203 | 204 | enable_testing () 205 | add_subdirectory (test) 206 | add_subdirectory (sample) 207 | -------------------------------------------------------------------------------- /ChangeLog: -------------------------------------------------------------------------------- 1 | 2017-07-31 Franklin "Snaipe" Mathieu 2 | 3 | * mimick: Version 0.3.0. 4 | * api: **Breaking**: Fixed MMK_MATCHER_BIT_CMP being an invalid enum value. 5 | * api: **Breaking**: Renamed neq,leq,geq to ne,le,ge. 6 | * api: **Breaking**: Use comparisons operators when comparing parameters 7 | rather than using memcmp. This is more correct as memcmp doesn't 8 | account for endianness, but non-comparable types are no longer supported 9 | as parameters. 10 | * api: Added .noabort option to mmk_mock, to return MMK_MOCK_INVALID 11 | instead of aborting if the mock initialization fails. 12 | * api: Fixed comparison matchers always matching for equality. 13 | * api: Fixed mmk_that not working. 14 | * arch: (x86_64,system-v) Fixed rax not being preserved when calling stubs. 15 | * arch: Added AArch64 support. 16 | * core: **Breaking**: Ignore zero .times values in mmk_verify. 17 | * core: Better symbol lookup code, causing less crashes overall. 18 | * core: Fixed "self" selector not working. 19 | * core: Fixed crashes with PIEs. 20 | * core: Fixed linux crash when a symbol isn't found. 21 | * core: Fixed vfprintf and abort mocks breaking mimick's panic handling. 22 | 23 | 2016-04-09 Franklin "Snaipe" Mathieu 24 | 25 | * mimick: version 0.2.0. 26 | * api: **Breaking**: Removed parameter names from mmk_mock_define. 27 | * api: **Breaking**: Removed mmk_mock_vdefine (mmk_mock_define now accepts 28 | void as a return type). 29 | * api: Added error handling for stub & mock creation. 30 | * api: Added support for variadic functions. 31 | * arch: Added ARM support. 32 | * core: Fixed sym selector not working for elf and mach-o. 33 | * doc: Added doxygen documentation in mimick.h. 34 | * matchers: **Breaking**: Mixing matchers and non-matchers parameters 35 | is now unsupported. 36 | * matchers: Added mmk_eq. 37 | 38 | 2016-03-30 Franklin "Snaipe" Mathieu 39 | 40 | * mimick: Initial release 41 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright © 2016 Franklin "Snaipe" Mathieu 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Mimick 2 | ====== 3 | 4 | [![Unix Build Status](https://api.travis-ci.org/diacritic/Mimick.svg?branch=master)](https://travis-ci.org/diacritic/Mimick) 5 | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/github/diacritic/Mimick?svg=true&branch=master)](https://ci.appveyor.com/project/diacritic/Mimick/branch/master) 6 | [![Coverity](https://scan.coverity.com/projects/8728/badge.svg?flat=1)](https://scan.coverity.com/projects/snaipe-mimick) 7 | [![License](https://img.shields.io/badge/license-MIT-blue.svg?style=flat)](https://github.com/diacritic/Mimick/blob/master/LICENSE) 8 | [![Version](https://img.shields.io/badge/version-v0.3.0-orange.svg?style=flat)](https://github.com/diacritic/Mimick/releases) 9 | [![Gitter Room](https://img.shields.io/gitter/room/diacritic/Mimick.svg?style=flat)](https://gitter.im/diacritic/Mimick) 10 | 11 | A KISS, cross-platform C Mocking library 12 | 13 | ## Philosophy 14 | 15 | **Warning**: this library is still experimental. APIs *will* change before the beta phase and things *will* break. Use at your own risk. 16 | 17 | Mimick aims to be a simple of use and powerful mocking and stubbing library for C. 18 | 19 | It doesn't rely on external code generation or compiler plugin to work -- simply 20 | link the library to your tests, and you're good to go! 21 | 22 | Mimick works by poisoning the GOT of the target dynamic module at runtime, making 23 | any call to the target functions routed to one of its own stubs. The mock API 24 | is only sugar on top of the stub API, and provides a default stub suitable for 25 | testing interactions and specifying behaviour. 26 | 27 | ## Samples 28 | 29 | Here is a simple usage of Mimick to mock the `malloc` function: 30 | 31 | ```c 32 | #include 33 | #include 34 | #include 35 | 36 | /* Define the blueprint of a mock identified by `malloc_proto` 37 | that returns a `void *` and takes a `size_t` parameter. */ 38 | mmk_mock_define (malloc_mock, void *, size_t); 39 | 40 | int main(void) { 41 | /* Mock the malloc function in the current module using 42 | the `malloc_mock` blueprint. */ 43 | mmk_mock("malloc@self", malloc_mock); 44 | 45 | /* Tell the mock to return NULL and set errno to ENOMEM 46 | whatever the given parameter is. */ 47 | void *result = NULL; 48 | mmk_when(malloc(mmk_any(size_t)), 49 | .then_return = &result, 50 | .then_errno = ENOMEM); 51 | 52 | assert(malloc(42) == result && errno == ENOMEM); 53 | 54 | mmk_reset(malloc); 55 | } 56 | ``` 57 | 58 | Or, alternatively: 59 | 60 | ```c 61 | malloc_mock mock = mmk_mock("malloc@self", malloc_mock); 62 | 63 | void *result = NULL; 64 | mmk_when(mock(mmk_any(size_t)), 65 | .then_return = &result, 66 | .then_errno = ENOMEM); 67 | 68 | assert(malloc(42) == result && errno == ENOMEM); 69 | 70 | mmk_reset(mock); 71 | ``` 72 | 73 | Other samples may be found in the [samples](./sample/) directory. 74 | 75 | ## Compatibility matrix 76 | 77 | Supported Compilers: GCC 4.6+, Clang 3.5+, MSVC 14+ 78 | 79 | #### Legend: 80 | **✓**: Supported. 81 | **✕**: Unsupported. 82 | **?**: Not Tested, but is expected to work. 83 | **∄**: Does not exist / not applicable. 84 | **~**: Works on some conditions. 85 | 86 | | Arch | Linux | OS X/iOS | FreeBSD | Windows | 87 | | --- |:---:|:---:|:---:|:---:| 88 | | x86 | ✓ | ✓ | ✓ | ✓ | 89 | | x86\_64 | ✓ | ✓ | ✓ | ✓ | 90 | | ARM | ✓ | ? | ✓ | ✕ | 91 | | ARM64 | ✓ | ? | ? | ∄ | 92 | | PPC | ✕ | ✕ | ✕ | ∄ | 93 | 94 | ## F.A.Q. 95 | 96 | **Q. Can I mock/stub static functions?** 97 | A. No. Static functions are, by definition, *private* implementations details, 98 | and because they are not exported nor visible to the outside world they cannot 99 | (and should not) be mocked/stubbed. 100 | 101 | **Q. Can I mock/stub functions inside a static library?** 102 | A. *Maybe*. Functions inside a static library are moved inside the executable and are not 103 | called using the PLT by default. You need to build your library to use Position-Independent 104 | Code, otherwise the functions cannot be stubbed nor mocked. 105 | 106 | **Q. I am mocking a standard library function, but `mmk_when` and `mmk_verify` 107 | are not working, why is this happening?** 108 | A. It's very possible that your compiler is optimizing away your function 109 | call inside `mmk_when` or `mmk_verify` since it has determined that there 110 | are no visible side effects in not calling the function. Compile your tests 111 | without optimizations (with a compiler flag or the `mmk_no_optimize` 112 | function attribute if it is available to your compiler), or use the function 113 | pointer returned by `mmk_mock` rather than the function itself. 114 | 115 | **Q. Does this run on embedded systems / bare metal?** 116 | A. Yes, but on very specific conditions. Your code must be position-independent, 117 | call functions using a global offset table or similar mechanism, and use 118 | an executable format that Mimick can handle (ELF, PE, Mach-O). 119 | You also need to provide definitions for some functions like malloc, realloc, 120 | and free. 121 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | version: 0.3.0_b{build}-{branch} 2 | 3 | init: 4 | - git config --global core.autocrlf input 5 | - set MSYSTEM=MINGW64 6 | - ps: | 7 | $ErrorActionPreference='silentlycontinue' 8 | if ($env:COMPILER -eq "mingw") { 9 | $env:PATH = "C:\msys64\usr\bin;" + $env:PATH 10 | if ($env:PLATFORM -eq "x86") { 11 | $env:PATH = "C:\msys64\mingw32\bin;" + $env:PATH 12 | pacman --noconfirm --needed -S cmake make mingw-w64-i686-toolchain mingw-w64-i686-cmake 2> $NULL 13 | } elseif ($env:PLATFORM -eq "x64") { 14 | $env:PATH = "C:\msys64\mingw64\bin;" + $env:PATH 15 | pacman --noconfirm --needed -S cmake make mingw-w64-x86_64-toolchain mingw-w64-x86_64-cmake 2> $NULL 16 | } 17 | } else { 18 | if ($env:PLATFORM -ne "x86") { 19 | $env:CMAKE_OPTS = $env:CMAKE_OPTS + " -A $env:PLATFORM" 20 | } 21 | } 22 | $error.Clear() 23 | - echo CMAKE_OPTS = %CMAKE_OPTS% 24 | - set "PATH=%PATH%;%APPVEYOR_BUILD_FOLDER%\build;%APPVEYOR_BUILD_FOLDER%\build\%CONFIGURATION%" 25 | 26 | environment: 27 | COVERALLS_REPO_TOKEN: 28 | secure: 5nuCg+faxFPeppoNNcSwVobswAVFUf8ut83vw8CX/4W2y0kZkGmwEfCUxSQWiQDU 29 | CI_NAME: appveyor 30 | CI_JOB_ID: $(APPVEYOR_JOB_ID) 31 | GCOV_PREFIX: $(APPVEYOR_BUILD_FOLDER) 32 | matrix: 33 | - COMPILER: mingw 34 | GENERATOR: "MSYS Makefiles" 35 | CMAKE_OPTS: -DCMAKE_TOOLCHAIN_FILE="../.cmake/Toolchains/Toolchain-mingw-w64.cmake" 36 | - COMPILER: msvc 37 | GENERATOR: "Visual Studio 14 2015" 38 | CFLAGS: /MP 39 | CXXFLAGS: /MP 40 | 41 | clone_depth: 5 42 | 43 | platform: 44 | - x86 45 | - x64 46 | 47 | configuration: 48 | - Debug 49 | - Release 50 | - RelWithDebInfo 51 | 52 | install: 53 | - ps: $env:RELEASE_NAME = $env:APPVEYOR_REPO_BRANCH -replace "/", "-" 54 | # Hack to make git think it is on the tip of the repo branch 55 | - 'git checkout -B %APPVEYOR_REPO_BRANCH%' 56 | # Configure project 57 | - 'mkdir build && cd build' 58 | - > 59 | cmake 60 | -Wno-dev 61 | -DCMAKE_INSTALL_PREFIX="mimick-%RELEASE_NAME%" 62 | -DCMAKE_BUILD_TYPE="%CONFIGURATION%" 63 | -DCMAKE_SYSTEM_PROCESSOR="%PLATFORM%" 64 | -DCMAKE_C_FLAGS="-Werror" 65 | %CMAKE_OPTS% 66 | -G "%GENERATOR%" 67 | .. 68 | 69 | build_script: 70 | - cmake --build . 71 | 72 | before_deploy: 73 | - ps: | 74 | $archive = "mimick-$env:RELEASE_NAME-windows-$env:COMPILER-$env:PLATFORM" 75 | cmake --build . --target install 76 | 7z a -ttar "$archive.tar" "mimick-$env:RELEASE_NAME" 77 | 7z a -tbzip2 "../$archive.tar.bz2" "$archive.tar" 78 | Push-AppveyorArtifact "../$archive.tar.bz2" 79 | 80 | test_script: 81 | - ps: | 82 | ctest --output-on-failure --timeout 10 83 | if (-not $lastexitcode -eq 0) { 84 | type Testing/Temporary/LastTest.log 85 | $host.setshouldexit(1) 86 | } 87 | 88 | notifications: 89 | 90 | - provider: Email 91 | to: [franklinmathieu@gmail.com] 92 | on_build_status_changed: true 93 | 94 | deploy: 95 | provider: GitHub 96 | auth_token: 97 | secure: MnZZQeoxBVnpV9GSSvVok5Je0/N2d/fzG4+ITw95/tYSgZ8rleBV23a5sCwAea3r 98 | artifact: 'mimick-$(RELEASE_NAME)-windows-$(COMPILER)-$(PLATFORM).tar.bz2' 99 | draft: false 100 | prerelease: false 101 | on: 102 | appveyor_repo_tag: true 103 | configuration: RelWithDebInfo 104 | -------------------------------------------------------------------------------- /include/mimick.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright © 2016 Franklin "Snaipe" Mathieu 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | #ifndef MIMICK_H_ 25 | # define MIMICK_H_ 26 | 27 | # include 28 | # include "mimick/preprocess.h" 29 | 30 | struct mmk_va_param { 31 | size_t size; 32 | char data[]; 33 | }; 34 | 35 | typedef void(*mmk_fn)(void); 36 | typedef struct mmk_stub *mmk_stub; 37 | 38 | /** 39 | * An invalid stub. Used for error checking if creating a stub fails. 40 | */ 41 | # define MMK_STUB_INVALID ((mmk_stub)0) 42 | 43 | /** 44 | * An invalid mock. Used for error checking if creating a mock fails. 45 | */ 46 | # define MMK_MOCK_INVALID ((mmk_fn)0) 47 | 48 | /* Stub API */ 49 | 50 | /** 51 | * @defgroup stub_api Stub API 52 | * @{ 53 | */ 54 | 55 | /** 56 | * Gets the current stub object from within a stub function. 57 | * 58 | * It is undefined behaviour to call mmk_ctx outside of a stub function. 59 | * 60 | * @returns The current stub object. 61 | */ 62 | mmk_stub mmk_ctx(void); 63 | 64 | /** 65 | * Get the user context from a stub object. 66 | * 67 | * The passed stub object must be a valid stub object returned by 68 | * mmk_stub_create that has not yet been destroyed by mmk_stub_destroy. 69 | * 70 | * NULL is returned if MMK_STUB_INVALID is passed. 71 | * 72 | * @param[in] stub A stub object. 73 | * 74 | * @returns The user context or NULL 75 | */ 76 | void *mmk_stub_context(mmk_stub stub); 77 | 78 | /** 79 | * Creates a stub object and hijacks calls to the function targeted by 80 | * the specified symbol target string to the specified function. 81 | * 82 | * It is undefined behaviour if the prototype of the passed function pointer 83 | * does not match the targeted function, or if the passed function pointer 84 | * does not reference a valid C function. 85 | * 86 | * If the function cannot be located in the specified target, mmk_stub_create 87 | * fails and returns MMK_STUB_INVALID. 88 | * 89 | * @see symbol_target_string 90 | * 91 | * @param[in] target The symbol target string. 92 | * @param[in] fn The replacing function. 93 | * @param[in] ctx The user context -- can be anything you want. 94 | * May be NULL. 95 | * 96 | * @returns The created stub object or MMK_STUB_INVALID. 97 | */ 98 | mmk_stub mmk_stub_create(const char *target, mmk_fn fn, void *ctx); 99 | 100 | /** 101 | * Destroy a stub object and restores the original function it targets. 102 | * 103 | * The passed stub object must be a valid stub object returned by 104 | * mmk_stub_create, or MMK_STUB_INVALID. 105 | * 106 | * It is undefined behaviour if mmk_stub_destroy is called on a stub object 107 | * that has already been destroyed previously. 108 | * 109 | * @param[in] stub The stub object to destroy. 110 | */ 111 | void mmk_stub_destroy(mmk_stub stub); 112 | 113 | /** @} */ 114 | 115 | /* Mock API */ 116 | 117 | /** 118 | * @defgroup mock_api Mock API 119 | * @{ 120 | */ 121 | 122 | /** 123 | * Convenience wrapper to get an addressable temporary object from a lvalue. 124 | */ 125 | # define mmk_val(Type, ...) (&(Type) { __VA_ARGS__ }) 126 | 127 | /** 128 | * Defines a mock blueprint and typedef the Id to a function pointer type 129 | * matching the mock prototype. 130 | * 131 | * @param[in] Id The Id of this blueprint. 132 | * @param[in] ReturnType The return type of the mock prototype. 133 | * @param[in] ... The list of parameter types of the mock prototype. 134 | * Leave empty if the function takes no parameters. 135 | */ 136 | # define mmk_mock_define(Id, ReturnType, ...) 137 | 138 | /** 139 | * Instanciates a function mock from a symbol target string and a blueprint ID. 140 | * 141 | * The blueprint must have been previously defined with mmk_mock_define. 142 | * 143 | * It is undefined behaviour if the prototype of the specified blueprint 144 | * does not match the prototype of the target function. 145 | * 146 | * When the instanciation succeeds, the macro returns a function pointer 147 | * representing the newly created mock object. This function pointer can 148 | * be used identically as the targeted function. 149 | * 150 | * If the instanciation fails, the macro either aborts the process or, 151 | * if the noabort option is set, returns MMK_MOCK_INVALID. 152 | * 153 | * The macro optionally takes a designated initializer list for setting options 154 | * during the mock creation. Valid options are: 155 | * 156 | * * .noabort = 1: Return MMK_MOCK_INVALID if the mock creation fails 157 | * instead of aborting the process. 158 | * 159 | * @see mmk_stub_create 160 | * @see symbol_target_string 161 | * 162 | * @param[in] Target The symbol target string. 163 | * @param[in] Id The Id of a matching mock blueprint. 164 | * @param[in] ... Optional mock options as a designated initializer list. 165 | * e.g: `.noabort = 1`. 166 | * 167 | * @returns The mock object or MMK_MOCK_INVALID. 168 | */ 169 | # define mmk_mock(Target, Id, ...) 170 | 171 | /** 172 | * Defines a contract for a defined set of parameters. 173 | * 174 | * The macro takes a expression that calls exactly once a mocked function 175 | * with either values or matchers. 176 | * 177 | * If a matcher is specified, all parameters must be matchers. 178 | * 179 | * The macro takes after the calling expression one or more designated 180 | * initializer `.key = value` pairs to specify the expected behaviour 181 | * when a call matches the parameters set in the calling expression. 182 | * 183 | * Valid keys are: 184 | * 185 | * * `.then_return = `: return the dereferenced value of ``. 186 | * 187 | * * `.then_errno = `: set errno to the `` integer value. 188 | * 189 | * * `.then_call = `: calls the function pointed at by `` 190 | * and returns the value the function itself returns. 191 | * 192 | * `.then_return` overrides the returned value, but the function still 193 | * gets executed. 194 | * 195 | * The function pointer **must** match the prototype of the mock function. 196 | * 197 | * @param[in] CallExpr The calling expression 198 | * @param[in] ... The expected behaviour key/value pairs 199 | */ 200 | # define mmk_when(CallExpr, ...) 201 | 202 | /** 203 | * Verifies the interactions of a mocked function call matching a set of 204 | * parameters. 205 | * 206 | * The macro takes a expression that calls exactly once a mocked function 207 | * with either values or matchers. 208 | * 209 | * If a matcher is specified, all parameters must be matchers. 210 | * 211 | * The macro takes after the calling expression one or more designated 212 | * initializer `.key = value` pairs to verify how many times a function 213 | * was called with matching parameters. 214 | * 215 | * Valid keys are: 216 | * 217 | * * `.times = `: Was the function called exactly `` times? 218 | * * `.at_least = `: Was the function called at least `` times? 219 | * (can be specified together with `.at_most`) 220 | * * `.at_most = `: Was the function called at most `` times? 221 | * (can be specified together with `.at_least`) 222 | * * `.never = 1`: Was the function never called? 223 | * * `.matching = `: Is the number of times the function was called 224 | * meeting the requirements of ``? 225 | * 226 | * `` is a function pointer of type `int (*)(size_t)` that takes 227 | * the number of time a function has been called with matching parameters, 228 | * and returns either non-zero if the verification passes or zero otherwise. 229 | * 230 | * @param[in] CallExpr The calling expression 231 | * @param[in] ... The verification key/value pairs 232 | * 233 | * @returns non-zero if the verification passes, zero otherwise. 234 | */ 235 | # define mmk_verify(CallExpr, ...) 236 | 237 | /** 238 | * Destroys the mock object and restores the original 'unmocked' function. 239 | * 240 | * The function can take either a mock object returned by `mmk_mock` (including 241 | * MMK_MOCK_INVALID), or a mocked function. 242 | * 243 | * If MMK_MOCK_INVALID is passed, the function returns and nothing happens. 244 | * 245 | * It is undefined behaviour to pass a function that isn't mocked. 246 | * 247 | * @param[in] Fn The mock object or the mocked function 248 | */ 249 | # define mmk_reset(Fn) 250 | 251 | /** 252 | * Placeholder type for variadic function parameters for use in 253 | * mmk_mock_define. 254 | */ 255 | # define mmk_va_args 256 | 257 | /** @} */ 258 | 259 | /* Matcher API */ 260 | 261 | /** 262 | * Match any value of type `Type`. 263 | * 264 | * @param[in] Type The type of the value to match. 265 | */ 266 | # define mmk_any(Type) 267 | 268 | /** 269 | * Match any value equal to `Val`. 270 | * 271 | * @param[in] Type The type of the value to match. 272 | * @param[in] Val The reference to compare values with. 273 | */ 274 | # define mmk_eq(Type, Val) 275 | 276 | /** 277 | * Match any value not equal to `Val`. 278 | * 279 | * @param[in] Type The type of the value to match. 280 | * @param[in] Val The reference to compare values with. 281 | */ 282 | # define mmk_ne(Type, Val) 283 | 284 | /** 285 | * Match any value strictly lower than `Val`. 286 | * 287 | * @param[in] Type The type of the value to match. 288 | * @param[in] Val The reference to compare values with. 289 | */ 290 | # define mmk_lt(Type, Val) 291 | 292 | /** 293 | * Match any value lower or equal to `Val`. 294 | * 295 | * @param[in] Type The type of the value to match. 296 | * @param[in] Val The reference to compare values with. 297 | */ 298 | # define mmk_le(Type, Val) 299 | 300 | /** 301 | * Match any value strictly greater than `Val`. 302 | * 303 | * @param[in] Type The type of the value to match. 304 | * @param[in] Val The reference to compare values with. 305 | */ 306 | # define mmk_gt(Type, Val) 307 | 308 | /** 309 | * Match any value greater or equal to `Val`. 310 | * 311 | * @param[in] Type The type of the value to match. 312 | * @param[in] Val The reference to compare values with. 313 | */ 314 | # define mmk_ge(Type, Val) 315 | 316 | /** 317 | * Match any value accepted by a predicate. 318 | * 319 | * The predicate must have the prototype `int predicate()`, where 320 | * `` is the type of the non-variadic parameter to match. 321 | * 322 | * If the parameter is in a variadic parameter pack, the prototype of 323 | * the predicate becomes `int predicate(struct mmk_va_param *)`. 324 | * 325 | * The predicate shall return zero if the parameter does not match 326 | * its rules, and non-zero otherwise. 327 | * 328 | * @param[in] Predicate A function pointer to a predicate. 329 | */ 330 | # define mmk_that(Predicate) 331 | 332 | /* Documentation concepts */ 333 | 334 | /** 335 | * @defgroup symbol_target_string Symbol target strings 336 | * @ingroup general_concepts 337 | * @{ 338 | * 339 | * Symbol target strings are strings of the form `"symbol_name(@scope)?"`, 340 | * i.e. a valid C identifier followed by an optional scope. 341 | * 342 | * A scope specifies the module where the function is mocked. Valid values 343 | * are either: 344 | * 345 | * 1. `self` which always refers to the main executable. Not specifying a 346 | * scope is equivalent to using the `self` scope. 347 | * 348 | * Regardless of the scope, the function is always mocked in the main 349 | * executable, `self` only specifies that it shall only be mocked there. 350 | * 351 | * 2. Values of the form `selector:value`, where `selector` is one of `file`, 352 | * `lib`, or `sym`. 353 | * 354 | * 1. If the `file` selector is specified, then `value` is interpreted as a 355 | * full path and the scope is the loaded module at this specific path. 356 | * 357 | * 2. If the `lib` selector is specified, then `value` is interpreted as a 358 | * name, and the scope is the loaded module with this name. 359 | * 360 | * Note: This resolves differently between platforms and is the 361 | * platform-independent way of specifying a library: 362 | * 363 | * - On Linux, this resolves to a loaded library named `lib.so`. 364 | * - On Windows, this finds a loaded library named `.dll`. 365 | * - On Darwin, this finds a loaded library named `lib.dylib`. 366 | * 367 | * 3. If the `sym` selector is specified, then `value` is interpreted as a 368 | * symbol name, and the scope is the first loaded module defining this 369 | * symbol. 370 | * 371 | * Examples: 372 | * 373 | * * "foo": Mock the `foo` function in the main executable only. 374 | * 375 | * * "foo@self": Same as `foo` 376 | * 377 | * * "foo@file:/lib/libbar.so": Mock the `foo` function in `/lib/libbbar.so`. 378 | * 379 | * * "foo@lib:bar": Mock the `foo` function in the `bar` library. 380 | * 381 | * * "foo@sym:bar": Mock the `foo` function in the first loaded library that 382 | * defines `bar`. 383 | * 384 | * @} 385 | */ 386 | 387 | /* Internals */ 388 | 389 | # include "mimick/mock.h" 390 | # include "mimick/matcher.h" 391 | 392 | # ifndef MMK_DO_NOT_UNOPTIMIZE 393 | # if defined __GNUC__ && !defined __clang__ 394 | # pragma GCC optimize "O0" 395 | # elif defined _MSC_VER 396 | # pragma optimize("", off) 397 | # endif 398 | # endif 399 | 400 | # if defined __clang__ 401 | # define mmk_no_optimize __attribute__((optnone)) 402 | # elif __GNUC__ >= 4 && __GNUC_MINOR__ >= 4 403 | # define mmk_no_optimize __attribute__((optimize(0))) 404 | # else 405 | # define mmk_no_optimize 406 | # endif 407 | 408 | # ifndef MMK_DO_NOT_DISABLE_WARNINGS 409 | # if defined __GNUC__ 410 | # pragma GCC diagnostic ignored "-Wunused-result" 411 | # elif defined _MSC_VER 412 | # pragma warning(disable : 6031) 413 | # endif 414 | # endif 415 | 416 | #endif /* !MIMICK_H_ */ 417 | -------------------------------------------------------------------------------- /include/mimick/alloc.h: -------------------------------------------------------------------------------- 1 | #ifndef MMK_ALLOC_H_ 2 | # define MMK_ALLOC_H_ 3 | 4 | void *mmk_malloc(size_t size); 5 | void *mmk_realloc(void *ptr, size_t size); 6 | void mmk_free(void *ptr); 7 | 8 | #endif /* !MMK_ALLOC_H_ */ 9 | -------------------------------------------------------------------------------- /include/mimick/assert.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright © 2016 Franklin "Snaipe" Mathieu 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | #ifndef MMK_ASSERT_H_ 25 | # define MMK_ASSERT_H_ 26 | 27 | # include 28 | # include 29 | 30 | # include "unmocked.h" 31 | 32 | # define mmk_assert(Cond) do { \ 33 | if (!(Cond)) { \ 34 | mmk_fprintf(stderr, "%s:%d: Assertion failed: %s\n", \ 35 | __FILE__, __LINE__, #Cond); \ 36 | mmk_abort(); \ 37 | } \ 38 | } while (0) 39 | 40 | #endif /* !MMK_ASSERT_H_ */ 41 | -------------------------------------------------------------------------------- /include/mimick/matcher.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright © 2016 Franklin "Snaipe" Mathieu 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | #ifndef MIMICK_MATCHER_H_ 25 | # define MIMICK_MATCHER_H_ 26 | 27 | # include 28 | 29 | /* Using anonymous structs in compound literals is perfectly valid C, 30 | and we use this behaviour in Mimick. This MSVC warning does nothing 31 | useful in that regard, so we simply disable it. */ 32 | # ifdef _MSC_VER 33 | # pragma warning (disable: 4116) 34 | # endif 35 | 36 | # define MMK_MATCHER_BIT_CMP (1 << (8 * sizeof (int) - 2)) 37 | # define MMK_MATCHER_BIT_EQ (1 << 1) 38 | # define MMK_MATCHER_BIT_LT (1 << 2) 39 | # define MMK_MATCHER_BIT_GT (1 << 3) 40 | 41 | enum mmk_matcher_kind { 42 | MMK_MATCHER_ANY, 43 | MMK_MATCHER_THAT, 44 | 45 | MMK_MATCHER_EQ = MMK_MATCHER_BIT_CMP | MMK_MATCHER_BIT_EQ, 46 | MMK_MATCHER_NEQ = MMK_MATCHER_BIT_CMP | MMK_MATCHER_BIT_LT 47 | | MMK_MATCHER_BIT_GT, 48 | MMK_MATCHER_LT = MMK_MATCHER_BIT_CMP | MMK_MATCHER_BIT_LT, 49 | MMK_MATCHER_LEQ = MMK_MATCHER_BIT_CMP | MMK_MATCHER_BIT_EQ 50 | | MMK_MATCHER_BIT_LT, 51 | MMK_MATCHER_GT = MMK_MATCHER_BIT_CMP | MMK_MATCHER_BIT_GT, 52 | MMK_MATCHER_GEQ = MMK_MATCHER_BIT_CMP | MMK_MATCHER_BIT_EQ 53 | | MMK_MATCHER_BIT_GT, 54 | }; 55 | 56 | struct mmk_matcher { 57 | enum mmk_matcher_kind kind; 58 | size_t prio; 59 | void *data; 60 | struct mmk_matcher *next; 61 | }; 62 | 63 | # define mmk_matcher_val_(Kind, Type, Val) (mmk_matcher_add(Kind, __COUNTER__), ((Type) Val)) 64 | # undef mmk_any 65 | # define mmk_any(Type) mmk_matcher_val_(MMK_MATCHER_ANY, Type, { 0 }) 66 | # undef mmk_eq 67 | # define mmk_eq(Type, Val) mmk_matcher_val_(MMK_MATCHER_EQ, Type, Val) 68 | # undef mmk_ne 69 | # define mmk_ne(Type, Val) mmk_matcher_val_(MMK_MATCHER_NEQ, Type, Val) 70 | # undef mmk_lt 71 | # define mmk_lt(Type, Val) mmk_matcher_val_(MMK_MATCHER_LT, Type, Val) 72 | # undef mmk_le 73 | # define mmk_le(Type, Val) mmk_matcher_val_(MMK_MATCHER_LEQ, Type, Val) 74 | # undef mmk_gt 75 | # define mmk_gt(Type, Val) mmk_matcher_val_(MMK_MATCHER_GT, Type, Val) 76 | # undef mmk_ge 77 | # define mmk_ge(Type, Val) mmk_matcher_val_(MMK_MATCHER_GEQ, Type, Val) 78 | # undef mmk_that 79 | # define mmk_that(Type, Predicate) (mmk_matcher_add_fn(MMK_MATCHER_THAT, __COUNTER__, (void(*)(void))*(int(*[1])(Type)) {(Predicate)}), ((Type) { 0 })) 80 | 81 | void mmk_matcher_init(int kind); 82 | void mmk_matcher_add(enum mmk_matcher_kind kind, int counter); 83 | void mmk_matcher_add_fn(enum mmk_matcher_kind kind, int counter, void (*fn)(void)); 84 | void mmk_matcher_add_data(enum mmk_matcher_kind kind, int counter, void *data); 85 | void mmk_matcher_term(void); 86 | struct mmk_matcher *mmk_matcher_ctx(void); 87 | void (*mmk_matcher_get_predicate(struct mmk_matcher *m))(void); 88 | 89 | #endif /* !MIMICK_MATCHER_H_ */ 90 | -------------------------------------------------------------------------------- /include/mimick/mock.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright © 2016 Franklin "Snaipe" Mathieu 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | #ifndef MIMICK_MOCK_H_ 25 | # define MIMICK_MOCK_H_ 26 | 27 | typedef struct mmk_mock_ctx *mmk_mock_ctx; 28 | 29 | # include 30 | # include 31 | 32 | # include "matcher.h" 33 | # include "alloc.h" 34 | # include "string.h" 35 | # include "when.h" 36 | # include "verify.h" 37 | # include "va.h" 38 | 39 | struct mmk_params { 40 | struct mmk_matcher *matcher_ctx; 41 | struct mmk_params *next; 42 | }; 43 | 44 | struct mmk_mock_options { 45 | unsigned sentinel_ : 1; 46 | unsigned noabort : 1; 47 | }; 48 | 49 | struct mmk_params *mmk_mock_get_params(void); 50 | void *mmk_mock_params_begin(struct mmk_mock_ctx *mock); 51 | void *mmk_mock_params_next(struct mmk_mock_ctx *mock, void *prev); 52 | mmk_fn mmk_mock_create_internal(const char *target, mmk_fn fn, 53 | struct mmk_mock_options opts); 54 | 55 | void mmk_mock_report_call(void); 56 | void mmk_mock_reset_call(const char *file, int line); 57 | 58 | # undef mmk_reset 59 | void mmk_reset(mmk_fn fn); 60 | # define mmk_reset(Fn) mmk_reset((mmk_fn) Fn); 61 | 62 | # undef mmk_mock 63 | # define mmk_mock(Target, ...) \ 64 | (MMK_MANGLE(MMK_VA_HEAD(__VA_ARGS__), create)( \ 65 | (Target), \ 66 | (struct mmk_mock_options) { \ 67 | .sentinel_ = 1, \ 68 | MMK_VA_TAIL(__VA_ARGS__) \ 69 | })) 70 | 71 | # define MMK_MK_ARG_STR(_, X) #X, 72 | 73 | # define MMK_MANGLE_(Id, Name) mmkuser_ ## Id ## _ ## Name 74 | # define MMK_MANGLE(Id, Name) MMK_MANGLE_(Id, Name) 75 | 76 | # define MMK_DEF_VERIFY_PARAM_VA(Id, ...) \ 77 | MMK_EXPAND(MMK_DEF_VERIFY_PARAM_VA_ ## Id (__VA_ARGS__)) 78 | # define MMK_DEF_VERIFY_PARAM_VA_WITH(...) 79 | 80 | # define MMK_DEF_VERIFY_PARAM__(X) .X = X, 81 | # define MMK_DEF_VERIFY_PARAM_VA_WITHOUT(N, Id, T, ...) \ 82 | MMK_DEF_VERIFY_PARAM__(param ## N) 83 | 84 | # define MMK_DEF_VERIFY_PARAM_(N, Id, T) \ 85 | MMK_COND_VA(MMK_DEF_VERIFY_PARAM_VA, (WITHOUT, N, Id, T,), (WITH,), T) 86 | # define MMK_DEF_VERIFY_PARAM(N, Id, T) \ 87 | MMK_EXPAND(MMK_DEF_VERIFY_PARAM_(N, Id, T)) 88 | 89 | # define MMK_DEF_FIELD_VA(Id, ...) \ 90 | MMK_EXPAND(MMK_DEF_FIELD_VA_ ## Id (__VA_ARGS__)) 91 | # define MMK_DEF_FIELD_VA_WITH(...) 92 | # define MMK_DEF_FIELD_VA_WITHOUT(N, T, ...) T param ## N; 93 | 94 | # define MMK_DEF_FIELD_(N, T) \ 95 | MMK_COND_VA(MMK_DEF_FIELD_VA, (WITHOUT, N, T,), (WITH,), T) 96 | # define MMK_DEF_FIELD(N, _, T) MMK_EXPAND(MMK_DEF_FIELD_(N, T)) 97 | 98 | # define MMK_TRYMATCH_VA(Id, ...) \ 99 | MMK_EXPAND(MMK_TRYMATCH_VA_ ## Id (__VA_ARGS__)) 100 | # define MMK_TRYMATCH_VA_WITH(...) 101 | 102 | # define MMK_TRYMATCH_(N, Name, T) \ 103 | MMK_COND_VA(MMK_TRYMATCH_VA, (WITHOUT, N, Name, T,), (WITH,), T) 104 | # define MMK_TRYMATCH(N, Name, T) MMK_EXPAND(MMK_TRYMATCH_(N, Name, T)) 105 | 106 | # define MMK_TRYMATCH_VA_WITHOUT(N, Name, Type, ...) \ 107 | if (m->next) { \ 108 | m = m->next; \ 109 | if (m->kind == MMK_MATCHER_ANY) { \ 110 | /* Ugly but keeps the indentation level as it is */ \ 111 | } else if (m->kind & MMK_MATCHER_BIT_CMP) { \ 112 | if (param ## N == bind->params.param ## N \ 113 | && !(m->kind & MMK_MATCHER_BIT_EQ)) \ 114 | continue; \ 115 | if (param ## N < bind->params.param ## N \ 116 | && !(m->kind & MMK_MATCHER_BIT_LT)) \ 117 | continue; \ 118 | if (param ## N > bind->params.param ## N \ 119 | && !(m->kind & MMK_MATCHER_BIT_GT)) \ 120 | continue; \ 121 | } else if (m->kind == MMK_MATCHER_THAT) { \ 122 | int (*predicate)(Type) = (int (*)(Type)) \ 123 | mmk_matcher_get_predicate(m); \ 124 | if (!predicate(param ## N)) \ 125 | continue; \ 126 | } \ 127 | } else if (param ## N != bind->params.param ## N) { \ 128 | continue; \ 129 | } 130 | 131 | # define MMK_TRYVERIFY_VA(Id, ...) \ 132 | MMK_EXPAND(MMK_TRYVERIFY_VA_ ## Id (__VA_ARGS__)) 133 | 134 | # define MMK_TRYVERIFY_VA_WITH(...) 135 | 136 | # define MMK_TRYVERIFY_(N, Name, T) \ 137 | MMK_COND_VA(MMK_TRYVERIFY_VA, (WITHOUT, N, Name, T,), (WITH,), T) 138 | 139 | # define MMK_TRYVERIFY(N, Name, T) MMK_EXPAND(MMK_TRYVERIFY_(N, Name, T)) 140 | 141 | # define MMK_TRYVERIFY_VA_WITHOUT(N, Name, Type, ...) \ 142 | if (m->next) { \ 143 | m = m->next; \ 144 | if (m->kind == MMK_MATCHER_ANY) { \ 145 | /* Ugly but keeps the indentation level as it is */ \ 146 | } else if (m->kind & MMK_MATCHER_BIT_CMP) { \ 147 | if (p->param ## N == param ## N \ 148 | && !(m->kind & MMK_MATCHER_BIT_EQ)) \ 149 | goto fail; \ 150 | if (p->param ## N < param ## N \ 151 | && !(m->kind & MMK_MATCHER_BIT_LT)) \ 152 | goto fail; \ 153 | if (p->param ## N > param ## N \ 154 | && !(m->kind & MMK_MATCHER_BIT_GT)) \ 155 | goto fail; \ 156 | } else if (m->kind == MMK_MATCHER_THAT) { \ 157 | int (*predicate)(Type) = (int (*)(Type)) \ 158 | mmk_matcher_get_predicate(m); \ 159 | if (!predicate(p->param ## N)) \ 160 | goto fail; \ 161 | } \ 162 | } else if (p->param ## N != param ## N) { \ 163 | goto fail; \ 164 | } 165 | 166 | # define MMK_SET_PARAMS_VA(Id, ...) \ 167 | MMK_EXPAND(MMK_SET_PARAMS_VA_ ## Id (__VA_ARGS__)) 168 | 169 | # define MMK_SET_PARAMS_VA_WITH(...) 170 | # define MMK_SET_PARAMS_VA_WITHOUT(N, Id, T, ...) \ 171 | bind->params.param ## N = param ## N; 172 | 173 | # define MMK_SET_PARAMS_(N, Id, T) \ 174 | MMK_COND_VA(MMK_SET_PARAMS_VA, (WITHOUT, N, Id, T,), (WITH,), T) 175 | 176 | # define MMK_SET_PARAMS(N, Id, T) MMK_EXPAND(MMK_SET_PARAMS_(N, Id, T)) 177 | 178 | # define MMK_VA_TYPE(...) ... 179 | # define MMK_VA_LIST_TYPE(...) va_list vl__ 180 | # define MMK_VA_LIST_TYPE_SRLZ(...) va_list vl__, \ 181 | size_t *mmk_size_out, struct mmk_va_param ***data_out 182 | 183 | # define MMK_MOCK_DEFINE_TYPES(Id, ...) \ 184 | typedef MMK_VA_HEAD(__VA_ARGS__) MMK_MANGLE(Id, returntype); \ 185 | typedef MMK_MANGLE(Id, returntype)(*Id)( \ 186 | MMK_EXPAND(MMK_PARAM_LIST(MMK_VA_TYPE, __VA_ARGS__))); \ 187 | typedef MMK_MANGLE(Id, returntype)(*MMK_MANGLE(Id, delegate))( \ 188 | MMK_EXPAND(MMK_PARAM_LIST(MMK_VA_LIST_TYPE, __VA_ARGS__))); \ 189 | typedef int (*MMK_MANGLE(Id, va_serializer))( \ 190 | MMK_EXPAND(MMK_PARAM_LIST(MMK_VA_LIST_TYPE_SRLZ, __VA_ARGS__))); \ 191 | struct MMK_MANGLE(Id, params) { \ 192 | size_t mmk_times__; \ 193 | size_t mmk_va_args_sz__; \ 194 | struct mmk_va_param **mmk_va_args__; \ 195 | MMK_EXPAND(MMK_APPLY_N(MMK_DEF_FIELD, Id, __VA_ARGS__)) \ 196 | }; \ 197 | struct MMK_MANGLE(Id, binding) { \ 198 | struct mmk_params item; \ 199 | struct mmk_result result; \ 200 | struct MMK_MANGLE(Id, params) params; \ 201 | }; \ 202 | 203 | # define MMK_MOCK_VA_PREDECL(Id, ...) \ 204 | MMK_MANGLE(Id, va_serializer) mmk_va_serializer__ = \ 205 | MMK_MANGLE(Id, serialize_va); \ 206 | va_list vl__, vl_cpy__; \ 207 | va_start(vl__, MMK_PARAM_N(param, \ 208 | MMK_DEC(MMK_DEC(MMK_VA_NARGS(MMK_VA_TAIL(__VA_ARGS__))))) \ 209 | ) 210 | 211 | # define MMK_MOCK_VA_COPY(...) \ 212 | va_copy(vl_cpy__, vl__) 213 | 214 | # define MMK_MOCK_VA_COPY_END(...) \ 215 | va_end(vl_cpy__) 216 | 217 | # define MMK_MOCK_VA_END(...) \ 218 | va_end(vl__) 219 | 220 | # define MMK_MOCK_VA_BIND(Params, ...) do { \ 221 | size_t mmk_va_args_sz__ = 0; \ 222 | struct mmk_va_param **mmk_va_args__ = NULL; \ 223 | MMK_MOCK_VA_COPY(_); \ 224 | int mmk_ok__ = \ 225 | mmk_va_serializer__(MMK_EXPAND(MMK_PARAM_PACK(__VA_ARGS__)), \ 226 | &mmk_va_args_sz__, &mmk_va_args__); \ 227 | MMK_MOCK_VA_COPY_END(_); \ 228 | if (mmk_ok__) { \ 229 | (Params)->mmk_va_args_sz__ = mmk_va_args_sz__; \ 230 | (Params)->mmk_va_args__ = mmk_va_args__; \ 231 | } \ 232 | } while (0) 233 | 234 | # define MMK_MOCK_VA_VERIFY(Id, Params, ...) do { \ 235 | size_t mmk_va_args_sz__ = 0; \ 236 | struct mmk_va_param **mmk_va_args__ = NULL; \ 237 | MMK_MOCK_VA_COPY(_); \ 238 | int mmk_ok__ = \ 239 | mmk_va_serializer__(MMK_EXPAND(MMK_PARAM_PACK(__VA_ARGS__)), \ 240 | &mmk_va_args_sz__, &mmk_va_args__); \ 241 | MMK_MOCK_VA_COPY_END(_); \ 242 | if (!mmk_ok__ || (Params)->mmk_va_args_sz__ != mmk_va_args_sz__) \ 243 | goto fail; \ 244 | for (size_t i = 0; i < mmk_va_args_sz__; ++i) { \ 245 | struct mmk_va_param *mmk_vp_ref = (Params)->mmk_va_args__[i]; \ 246 | struct mmk_va_param *mmk_vp = mmk_va_args__[i]; \ 247 | if (mmk_vp_ref->size != mmk_vp->size) \ 248 | goto fail; \ 249 | if (m->next) { \ 250 | m = m->next; \ 251 | if (m->kind == MMK_MATCHER_ANY) { \ 252 | /* Ugly but keeps the indentation level as it is */ \ 253 | } else if (m->kind & MMK_MATCHER_BIT_CMP) { \ 254 | int res = mmk_memcmp(mmk_vp_ref->data, \ 255 | mmk_vp->data, mmk_vp->size); \ 256 | if (res == 0 && !(m->kind & MMK_MATCHER_BIT_EQ)) \ 257 | goto fail; \ 258 | if (res < 0 && !(m->kind & MMK_MATCHER_BIT_LT)) \ 259 | goto fail; \ 260 | if (res > 0 && !(m->kind & MMK_MATCHER_BIT_GT)) \ 261 | goto fail; \ 262 | } else if (m->kind == MMK_MATCHER_THAT) { \ 263 | int (*predicate)(struct mmk_va_param *) = \ 264 | (int (*)(struct mmk_va_param *)) \ 265 | mmk_matcher_get_predicate(m); \ 266 | if (!predicate(mmk_vp_ref)) \ 267 | goto fail; \ 268 | } \ 269 | } else { \ 270 | if (mmk_memcmp(mmk_vp_ref->data, mmk_vp->data, mmk_vp->size)) \ 271 | goto fail; \ 272 | } \ 273 | mmk_free(mmk_vp); \ 274 | } \ 275 | mmk_free(mmk_va_args__); \ 276 | } while (0) 277 | 278 | # define MMK_MOCK_VA_REGISTER_CALL(RegParams, ...) do { \ 279 | size_t mmk_va_args_sz__ = 0; \ 280 | struct mmk_va_param **mmk_va_args__ = NULL; \ 281 | MMK_MOCK_VA_COPY(_); \ 282 | int mmk_ok__ = \ 283 | mmk_va_serializer__(MMK_EXPAND(MMK_PARAM_PACK(__VA_ARGS__)), \ 284 | &mmk_va_args_sz__, &mmk_va_args__); \ 285 | MMK_MOCK_VA_COPY_END(_); \ 286 | if (mmk_ok__) { \ 287 | (RegParams)->mmk_va_args_sz__ = mmk_va_args_sz__; \ 288 | (RegParams)->mmk_va_args__ = mmk_va_args__; \ 289 | } \ 290 | } while (0) 291 | 292 | # define MMK_MOCK_DEFINE(PreDecl, Return, ReturnSE, VaMacro, Id, ...) \ 293 | MMK_EXPAND(MMK_MOCK_DEFINE_TYPES(Id, __VA_ARGS__)) \ 294 | static MMK_MANGLE(Id, returntype) MMK_MANGLE(Id, stub)( \ 295 | MMK_EXPAND(MMK_PARAM_LIST(MMK_VA_TYPE, __VA_ARGS__))) \ 296 | { \ 297 | VaMacro(PREDECL)(Id, __VA_ARGS__); \ 298 | PreDecl(Id); \ 299 | struct mmk_matcher *matcher_ctx = mmk_matcher_ctx(); \ 300 | if (matcher_ctx) { \ 301 | struct mmk_mock_ctx *mock = mmk_stub_context(mmk_ctx ()); \ 302 | mmk_mock_report_call(); \ 303 | if (matcher_ctx->kind == 0) { \ 304 | struct MMK_MANGLE(Id, binding) *bind = \ 305 | mmk_malloc(sizeof (struct MMK_MANGLE(Id, binding))); \ 306 | bind->result = *mmk_when_get_result(); \ 307 | MMK_APPLY_N(MMK_SET_PARAMS, Id, __VA_ARGS__) \ 308 | MMK_EXPAND(VaMacro(BIND)(&bind->params, __VA_ARGS__)); \ 309 | mmk_when_impl(mock, bind); \ 310 | } else if (matcher_ctx->kind == 1) { \ 311 | size_t times = 0; \ 312 | for (struct MMK_MANGLE(Id, params) *p = \ 313 | mmk_mock_params_begin(mock); \ 314 | p; p = mmk_mock_params_next(mock, p)) \ 315 | { \ 316 | struct mmk_matcher *m = matcher_ctx; \ 317 | (void) m; \ 318 | MMK_EXPAND(MMK_APPLY_N(MMK_TRYVERIFY, Id, __VA_ARGS__)) \ 319 | MMK_EXPAND(VaMacro(VERIFY)(Id, p, __VA_ARGS__)); \ 320 | times += p->mmk_times__; \ 321 | goto fail; \ 322 | fail:; \ 323 | } \ 324 | for (struct mmk_matcher *m = matcher_ctx, *next; m;) { \ 325 | next = m->next; \ 326 | mmk_free (m); \ 327 | m = next; \ 328 | } \ 329 | mmk_verify_set_times(times); \ 330 | } \ 331 | VaMacro(END)(_); \ 332 | Return(zero__); \ 333 | } \ 334 | struct MMK_MANGLE(Id, params) reg_params = { \ 335 | .mmk_times__ = 0, \ 336 | MMK_APPLY_N(MMK_DEF_VERIFY_PARAM, Id, __VA_ARGS__) \ 337 | }; \ 338 | MMK_EXPAND(VaMacro(REGISTER_CALL)(®_params, __VA_ARGS__)); \ 339 | mmk_verify_register_call(®_params, sizeof (reg_params)); \ 340 | struct mmk_params *param = mmk_mock_get_params(); \ 341 | for (; param; param = param->next) { \ 342 | struct MMK_MANGLE(Id, binding) *bind = (void*) param; \ 343 | struct mmk_matcher *m = param->matcher_ctx; \ 344 | (void) m; \ 345 | MMK_EXPAND(MMK_APPLY_N(MMK_TRYMATCH, Id, __VA_ARGS__)) \ 346 | if (bind->result.then_errno) \ 347 | errno = bind->result.then_errno; \ 348 | if (bind->result.then_call && !bind->result.then_return) { \ 349 | ReturnSE(Id, VaMacro, \ 350 | ((MMK_MANGLE(Id, delegate)) bind->result.then_call)( \ 351 | MMK_EXPAND(MMK_PARAM_PACK(__VA_ARGS__)) \ 352 | )); \ 353 | } \ 354 | if (bind->result.then_call && bind->result.then_return) { \ 355 | VaMacro(COPY)(_); \ 356 | (void) ((MMK_MANGLE(Id, delegate)) bind->result.then_call)( \ 357 | MMK_EXPAND(MMK_PARAM_PACK(__VA_ARGS__)) \ 358 | ); \ 359 | VaMacro(COPY_END)(_); \ 360 | VaMacro(END)(_); \ 361 | Return(*(MMK_MANGLE(Id, returntype)*)bind->result.then_return);\ 362 | } \ 363 | if (bind->result.then_return) { \ 364 | VaMacro(END)(_); \ 365 | Return(*(MMK_MANGLE(Id, returntype)*)bind->result.then_return);\ 366 | } \ 367 | VaMacro(END)(_); \ 368 | Return(zero__); \ 369 | } \ 370 | VaMacro(END)(_); \ 371 | Return(zero__); \ 372 | } \ 373 | static inline Id MMK_MANGLE(Id, create)(const char *tgt, \ 374 | struct mmk_mock_options opts) \ 375 | { \ 376 | return (Id) mmk_mock_create_internal(tgt, \ 377 | (mmk_fn) MMK_MANGLE(Id, stub), opts); \ 378 | } \ 379 | typedef int MMK_MANGLE(Id, dummy) 380 | 381 | # define MMK_MOCK_DEFINE_ZERO(Id) static MMK_MANGLE(Id, returntype) zero__ 382 | 383 | # define MMK_MOCK_VALUE_RETURN(Val) return (Val) 384 | # define MMK_MOCK_VALUE_RETURN_SE(Id, VaMacro, Val) do { \ 385 | VaMacro(COPY)(_); \ 386 | MMK_MANGLE(Id, returntype) mmk_val__ = (Val); \ 387 | VaMacro(COPY_END)(_); \ 388 | VaMacro(END)(_); \ 389 | return mmk_val__; \ 390 | } while (0) 391 | 392 | # define MMK_MOCK_VOID_RETURN(Val) return 393 | # define MMK_MOCK_VOID_RETURN_SE(Id, VaMacro, Val) do { \ 394 | VaMacro(COPY)(_); \ 395 | (void) (Val); \ 396 | VaMacro(COPY_END)(_); \ 397 | VaMacro(END)(_); \ 398 | return; \ 399 | } while (0) 400 | 401 | # define MMK_MOCK_VA(Id) MMK_MOCK_VA_ ## Id 402 | # define MMK_MOCK_VA_WITH(Id) MMK_EXPAND(MMK_MOCK_VA_ ## Id) 403 | # define MMK_MOCK_VA_WITHOUT(Id) MMK_NOOP_FN 404 | 405 | # undef mmk_mock_define 406 | # define mmk_mock_define(Id, ...) \ 407 | MMK_EXPAND(MMK_MOCK_DEFINE_IMPL(Id, \ 408 | MMK_EXPAND(MMK_COND_VA( \ 409 | MMK_MOCK_VA, \ 410 | (WITHOUT), \ 411 | (WITH), \ 412 | MMK_LAST(__VA_ARGS__))), \ 413 | __VA_ARGS__)) 414 | 415 | # define MMK_MOCK_DEFINE_IMPL(Id, VaMacro, ...) \ 416 | MMK_EXPAND(MMK_COND_VOID( \ 417 | MMK_MOCK_DEFINE, \ 418 | (MMK_MOCK_DEFINE_ZERO, \ 419 | MMK_MOCK_VALUE_RETURN, \ 420 | MMK_MOCK_VALUE_RETURN_SE, \ 421 | VaMacro, \ 422 | Id, __VA_ARGS__), \ 423 | (MMK_NOOP_FN, \ 424 | MMK_MOCK_VOID_RETURN, \ 425 | MMK_MOCK_VOID_RETURN_SE, \ 426 | VaMacro, \ 427 | Id, __VA_ARGS__), \ 428 | MMK_VA_HEAD(__VA_ARGS__))) 429 | 430 | #endif /* !MIMICK_MOCK_H_ */ 431 | -------------------------------------------------------------------------------- /include/mimick/string.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright © 2016 Franklin "Snaipe" Mathieu 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | #ifndef MMK_STRING_H_ 25 | # define MMK_STRING_H_ 26 | 27 | int mmk_memcmp(const void *s1, const void *s2, size_t n); 28 | void *mmk_memcpy(void *dst, const void *src, size_t n); 29 | 30 | #endif /* !MMK_STRING_H_ */ 31 | -------------------------------------------------------------------------------- /include/mimick/unmocked.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright © 2016 Franklin "Snaipe" Mathieu 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | #ifndef MMK_UNMOCKED_H_ 25 | # define MMK_UNMOCKED_H_ 26 | 27 | # include 28 | 29 | # include "alloc.h" 30 | 31 | void mmk_fprintf(FILE *f, const char *str, ...); 32 | void mmk_abort(void); 33 | 34 | #endif /* !MMK_UNMOCKED_H_ */ 35 | -------------------------------------------------------------------------------- /include/mimick/va.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright © 2016 Franklin "Snaipe" Mathieu 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | #ifndef MMK_VA_H_ 25 | # define MMK_VA_H_ 26 | 27 | # include "string.h" 28 | # include "../mimick.h" 29 | 30 | /* mmk_va_args is actually just a preprocessed token without any real 31 | definition -- we just reserve it anyway in mimick.h. */ 32 | # undef mmk_va_args 33 | 34 | # define mmk_make_va_param(vd, vl, type) do { \ 35 | (vd) = mmk_malloc(sizeof (struct mmk_va_param) + sizeof (type)); \ 36 | (vd)->size = sizeof (type); \ 37 | type val = va_arg(vl, type); \ 38 | mmk_memcpy((vd)->data, &val, sizeof (type)); \ 39 | } while (0) 40 | 41 | #endif /* !MMK_VA_H_ */ 42 | -------------------------------------------------------------------------------- /include/mimick/verify.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright © 2016 Franklin "Snaipe" Mathieu 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | #ifndef MMK_VERIFY_H_ 25 | # define MMK_VERIFY_H_ 26 | 27 | # include 28 | 29 | struct mmk_verify_params { 30 | int sentinel_; 31 | int never; 32 | size_t at_least; 33 | size_t at_most; 34 | size_t times; 35 | int (*matching)(size_t); 36 | }; 37 | 38 | void mmk_verify_set_times(size_t times); 39 | int mmk_verify_times(struct mmk_verify_params *params); 40 | void mmk_verify_register_call(void *params, size_t size); 41 | 42 | # undef mmk_verify 43 | # define mmk_verify(CallExpr, ...) \ 44 | (mmk_matcher_init(1), \ 45 | (void) (CallExpr), \ 46 | mmk_matcher_term(), \ 47 | mmk_mock_reset_call(__FILE__, __LINE__), \ 48 | mmk_verify_times(&(struct mmk_verify_params) { \ 49 | __VA_ARGS__, \ 50 | .sentinel_=1, \ 51 | })) 52 | 53 | #endif /* !MMK_VERIFY_H_ */ 54 | -------------------------------------------------------------------------------- /include/mimick/when.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright © 2016 Franklin "Snaipe" Mathieu 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | #ifndef MMK_WHEN_H_ 25 | # define MMK_WHEN_H_ 26 | 27 | # include "va.h" 28 | 29 | struct mmk_result { 30 | int sentinel_; 31 | int then_errno; 32 | void *then_return; 33 | void (*then_call)(void); 34 | struct mmk_va_info *with_va; 35 | }; 36 | 37 | void mmk_when_init(struct mmk_result *res); 38 | void mmk_when_impl(struct mmk_mock_ctx *mock, void *data); 39 | struct mmk_result *mmk_when_get_result(void); 40 | 41 | # undef mmk_when 42 | # define mmk_when(CallExpr, ...) \ 43 | (mmk_matcher_init(0), \ 44 | mmk_when_init(&(struct mmk_result) { __VA_ARGS__, .sentinel_ = 0, }), \ 45 | (void) (CallExpr), \ 46 | mmk_mock_reset_call(__FILE__, __LINE__), \ 47 | mmk_matcher_term()) 48 | 49 | #endif /* !MMK_WHEN_H_ */ 50 | -------------------------------------------------------------------------------- /sample/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | if (NOT MSVC) 2 | set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pedantic") 3 | endif () 4 | 5 | add_subdirectory (strdup) 6 | -------------------------------------------------------------------------------- /sample/strdup/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library (strdup SHARED strdup.c) 2 | add_executable (strdup_test test.c) 3 | target_link_libraries (strdup_test strdup mimick) 4 | 5 | if (NOT MSVC) 6 | set_source_files_properties (test.c PROPERTIES COMPILE_FLAGS -O0) 7 | endif () 8 | 9 | add_mimick_sample (strdup_test) 10 | -------------------------------------------------------------------------------- /sample/strdup/strdup.c: -------------------------------------------------------------------------------- 1 | #define DLL_EXPORT 1 2 | #include 3 | #include 4 | #include "strdup.h" 5 | 6 | char *my_strdup(const char *s) 7 | { 8 | size_t len = strlen (s); 9 | char *dst = malloc (len + 1); 10 | if (!dst) 11 | return NULL; 12 | memcpy (dst, s, len + 1); 13 | return dst; 14 | } 15 | -------------------------------------------------------------------------------- /sample/strdup/strdup.h: -------------------------------------------------------------------------------- 1 | #ifndef STRDUP_H_ 2 | # define STRDUP_H_ 3 | 4 | # ifdef _MSC_VER 5 | # ifdef DLL_EXPORT 6 | # define API __declspec(dllexport) 7 | # else 8 | # define API __declspec(dllimport) 9 | # endif 10 | # else 11 | # define API 12 | # endif 13 | 14 | API char *my_strdup(const char *s); 15 | 16 | #endif /* !STRDUP_H_ */ 17 | -------------------------------------------------------------------------------- /sample/strdup/test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "strdup.h" 6 | 7 | mmk_mock_define(malloc_mock, void *, size_t); 8 | 9 | void test_simple_case(void) 10 | { 11 | static char ref[] = "hello"; 12 | char buf[sizeof (ref)]; 13 | 14 | malloc_mock m = mmk_mock("malloc@lib:strdup", malloc_mock); 15 | mmk_assert((mmk_fn) m != MMK_MOCK_INVALID); 16 | 17 | mmk_when(malloc(sizeof(ref)), .then_return = &(char*) { buf }); 18 | 19 | char *dup = my_strdup("hello"); 20 | 21 | /* mmk_verify is overkill in this case, mmk_verify should be used when you 22 | * care about testing interactions rather than external behaviour */ 23 | int pass = mmk_verify(malloc(mmk_ge(size_t, sizeof (ref))), .times = 1); 24 | 25 | mmk_assert(pass); 26 | mmk_assert(dup == buf && !strcmp(ref, buf)); 27 | 28 | mmk_reset(malloc); 29 | } 30 | 31 | void test_error_case(void) 32 | { 33 | /* Alternative usage */ 34 | malloc_mock mock = mmk_mock("malloc@lib:strdup", malloc_mock); 35 | mmk_assert((mmk_fn) mock != MMK_MOCK_INVALID); 36 | 37 | mmk_when(mock(mmk_any(size_t)), 38 | .then_return = &(void *) { NULL }, 39 | .then_errno = ENOMEM); 40 | 41 | char *dup = my_strdup("foo"); 42 | mmk_assert(dup == NULL && errno == ENOMEM); 43 | 44 | mmk_reset(mock); 45 | } 46 | 47 | int main(void) 48 | { 49 | test_simple_case(); 50 | test_error_case(); 51 | return 0; 52 | } 53 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2016 Franklin "Snaipe" Mathieu. 2 | # Redistribution and use of this file is allowed according to the terms of the MIT license. 3 | # For details see the LICENSE file distributed with Mimick. 4 | 5 | if (MMK_ARCH STREQUAL MMK_ABI) 6 | set (ASM_ID ${MMK_ARCH}) 7 | else () 8 | set (ASM_ID ${MMK_ARCH}-${MMK_ABI}) 9 | endif () 10 | 11 | set (ASM_FILES src/asm/trampoline-${ASM_ID}${ASM_EXTENSION}) 12 | 13 | set (SOURCE_FILES ${ASM_FILES} 14 | src/core.c 15 | src/core.h 16 | src/matcher.c 17 | src/mock.c 18 | src/mock.h 19 | src/plt.c 20 | src/plt.h 21 | src/plt-${MMK_EXE_FORMAT}.c 22 | src/plt-${MMK_EXE_FORMAT}.h 23 | src/stub.c 24 | src/stub.h 25 | src/threadlocal.h 26 | src/trampoline.c 27 | src/trampoline.h 28 | src/verify.c 29 | src/vitals.c 30 | src/vitals.h 31 | src/when.c 32 | ) 33 | 34 | set (INTERFACE_FILES 35 | include/mimick/alloc.h 36 | include/mimick/assert.h 37 | include/mimick/matcher.h 38 | include/mimick/mock.h 39 | include/mimick/preprocess.h 40 | include/mimick/string.h 41 | include/mimick/unmocked.h 42 | include/mimick/va.h 43 | include/mimick/verify.h 44 | include/mimick/when.h 45 | include/mimick.h 46 | ) 47 | 48 | configure_file( 49 | "${CMAKE_CURRENT_SOURCE_DIR}/config.h.in" 50 | "${CMAKE_CURRENT_BINARY_DIR}/config.h" 51 | ) 52 | 53 | set (SOURCE_FILES ${SOURCE_FILES} PARENT_SCOPE) 54 | set (INTERFACE_FILES ${INTERFACE_FILES} PARENT_SCOPE) 55 | -------------------------------------------------------------------------------- /src/asm/mangling.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright © 2016 Franklin "Snaipe" Mathieu 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | #ifndef ASM_MANGLING_H_ 25 | # define ASM_MANGLING_H_ 26 | 27 | # include "config.h" 28 | 29 | # define MANGLING_STR_(x) #x 30 | # define MANGLING_STR(x) MANGLING_STR_(x) 31 | 32 | # define MANGLING_HEADER_ mangling/MMK_MANGLING.h 33 | # define MANGLING_HEADER MANGLING_STR(MANGLING_HEADER_) 34 | 35 | # include MANGLING_HEADER 36 | 37 | #endif /* !ASM_MANGLING_H_ */ 38 | -------------------------------------------------------------------------------- /src/asm/mangling/leading-underscore.h: -------------------------------------------------------------------------------- 1 | # define MANGLE(x) _ ## x 2 | -------------------------------------------------------------------------------- /src/asm/mangling/none.h: -------------------------------------------------------------------------------- 1 | # define MANGLE(x) x 2 | -------------------------------------------------------------------------------- /src/asm/trampoline-aarch64.S: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright © 2016 Franklin "Snaipe" Mathieu 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | #include "mangling.h" 25 | 26 | .align 8 27 | .globl MANGLE(mmk_trampoline) 28 | MANGLE(mmk_trampoline): 29 | adr x16, . 30 | 31 | stp x7, x6, [sp, #-16]! 32 | stp x5, x4, [sp, #-16]! 33 | stp x3, x2, [sp, #-16]! 34 | stp x1, x0, [sp, #-16]! 35 | 36 | stp x16, x30, [sp, #-16]! 37 | 38 | ldr x0, [x16, #-16] // Call mmk_set_ctx 39 | ldr x16, [x0, #8] 40 | blr x16 41 | 42 | ldp x16, x30, [sp] 43 | 44 | ldr x16, [x16, #-16] // Check if context was asked 45 | ldr x16, [x16] 46 | blr x16 47 | cmp x0, #0 48 | b.ne ret_ctx 49 | 50 | ldp x16, x30, [sp], #16 51 | ldp x1, x0, [sp], #16 52 | ldp x3, x2, [sp], #16 53 | ldp x5, x4, [sp], #16 54 | ldp x7, x6, [sp], #16 55 | 56 | ldr x16, [x16, #-8] // Trampoline jump 57 | br x16 58 | 59 | ret_ctx: 60 | 61 | ldp x16, x30, [sp], #16 62 | ldp x1, x0, [sp], #16 63 | ldp x3, x2, [sp], #16 64 | ldp x5, x4, [sp], #16 65 | ldp x7, x6, [sp], #16 66 | stp x16, x30, [sp, #-16]! 67 | 68 | ldr x16, [x16, #-16] // Call mmk_ctx 69 | ldr x16, [x16, #16] 70 | blr x16 71 | 72 | ldp x16, x30, [sp], #16 73 | ret 74 | 75 | .align 8 76 | .globl MANGLE(mmk_trampoline_end) 77 | MANGLE(mmk_trampoline_end): 78 | nop 79 | -------------------------------------------------------------------------------- /src/asm/trampoline-arm.S: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright © 2016 Franklin "Snaipe" Mathieu 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | .globl mmk_trampoline 26 | mmk_trampoline: 27 | adr ip, . 28 | 29 | push {r0, r1, r2, r3} 30 | push {ip, lr} 31 | 32 | ldr r0, [ip, #-8] // Call mmk_set_ctx 33 | ldr ip, [r0, #4] 34 | blx ip 35 | 36 | pop {ip, lr} 37 | push {ip, lr} 38 | 39 | ldr ip, [ip, #-8] // Check if context was asked 40 | ldr ip, [ip] 41 | blx ip 42 | cmp r0, #0 43 | bne ret_ctx 44 | 45 | pop {ip, lr} 46 | pop {r0, r1, r2, r3} 47 | 48 | ldr pc, [ip, #-4] // Trampoline jump 49 | 50 | ret_ctx: 51 | 52 | pop {ip, lr} 53 | pop {r0, r1, r2, r3} 54 | push {ip, lr} 55 | 56 | ldr ip, [ip, #-8] // Call mmk_ctx 57 | ldr ip, [ip, #8] 58 | blx ip 59 | 60 | pop {ip, pc} 61 | 62 | .globl mmk_trampoline_end 63 | mmk_trampoline_end: 64 | nop 65 | -------------------------------------------------------------------------------- /src/asm/trampoline-i386-cdecl.S: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright © 2016 Franklin "Snaipe" Mathieu 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | #include "mangling.h" 25 | 26 | .globl MANGLE(mmk_trampoline) 27 | MANGLE(mmk_trampoline): 28 | start: 29 | call next // Retrieve IP 30 | next: 31 | pop %eax 32 | 33 | push %eax // Setup mock context 34 | mov (start - next - 0x8)(%eax), %eax 35 | push %eax // Call mmk_set_ctx 36 | mov 0x4(%eax), %eax 37 | call *%eax 38 | pop %eax 39 | 40 | mov (%eax), %eax // Check if context was asked 41 | call *%eax 42 | test %eax, %eax 43 | jnz ret_ctx 44 | 45 | pop %eax 46 | mov (start - next - 0x4)(%eax), %eax // Retrieve offset at 47 | // the start of the map 48 | jmp *%eax 49 | 50 | ret_ctx: 51 | pop %eax 52 | mov (start - next - 0x8)(%eax), %eax 53 | mov 0x8(%eax), %eax // Call mmk_ctx 54 | call *%eax 55 | ret 56 | 57 | .globl MANGLE(mmk_trampoline_end) 58 | MANGLE(mmk_trampoline_end): 59 | nop 60 | -------------------------------------------------------------------------------- /src/asm/trampoline-i386-cdecl.asm: -------------------------------------------------------------------------------- 1 | ; 2 | ; The MIT License (MIT) 3 | ; 4 | ; Copyright © 2016 Franklin "Snaipe" Mathieu 5 | ; 6 | ; Permission is hereby granted, free of charge, to any person obtaining a copy 7 | ; of this software and associated documentation files (the "Software"), to deal 8 | ; in the Software without restriction, including without limitation the rights 9 | ; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | ; copies of the Software, and to permit persons to whom the Software is 11 | ; furnished to do so, subject to the following conditions: 12 | ; 13 | ; The above copyright notice and this permission notice shall be included in 14 | ; all copies or substantial portions of the Software. 15 | ; 16 | ; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | ; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | ; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | ; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | ; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | ; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | ; THE SOFTWARE. 23 | ; 24 | .386 25 | .MODEL FLAT, C 26 | .CODE 27 | 28 | extern mmk_ctx:dword 29 | 30 | mmk_trampoline label far 31 | call next ; Retrieve IP 32 | next: 33 | pop eax 34 | 35 | and eax, 0fffff000h 36 | push eax 37 | mov eax, dword ptr [eax] ; Setup mock context 38 | push eax ; Call mmk_set_ctx 39 | call dword ptr [eax + 4h] 40 | pop eax 41 | 42 | call dword ptr [eax] ; Check if context was asked 43 | test eax, eax 44 | jnz ret_ctx 45 | 46 | pop eax ; Retrieve offset at 47 | jmp dword ptr [eax + 4h] ; the start of the map 48 | 49 | ret_ctx: ; Return context 50 | pop eax 51 | mov eax, dword ptr [eax] 52 | call dword ptr [eax + 8h] ; Call mmk_ctx 53 | ret 54 | mmk_trampoline_end label far 55 | 56 | public mmk_trampoline 57 | public mmk_trampoline_end 58 | 59 | end 60 | -------------------------------------------------------------------------------- /src/asm/trampoline-x86_64-systemv.S: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright © 2016 Franklin "Snaipe" Mathieu 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | #include "mangling.h" 25 | 26 | # define PUSHDQ(Reg) \ 27 | sub $0x10, %rsp; \ 28 | movdqu Reg, (%rsp) 29 | 30 | # define POPDQ(Reg) \ 31 | movdqu (%rsp), Reg; \ 32 | add $0x10, %rsp 33 | 34 | # if defined(__CET__) 35 | # define mmk_cet endbr64 36 | # else 37 | # define mmk_cet 38 | # endif 39 | 40 | .globl MANGLE(mmk_trampoline) 41 | MANGLE(mmk_trampoline): 42 | start: 43 | mmk_cet 44 | call next // Retrieve IP 45 | next: 46 | mmk_cet 47 | pop %r11 48 | 49 | push %r11 // Setup mock context 50 | mov (start - next - 0x10)(%r11), %r11 51 | 52 | push %rdi // Save caller ctx 53 | push %rsi 54 | push %rdx 55 | push %rcx 56 | push %r8 57 | push %r9 58 | PUSHDQ (%xmm0) 59 | PUSHDQ (%xmm1) 60 | PUSHDQ (%xmm2) 61 | PUSHDQ (%xmm3) 62 | PUSHDQ (%xmm4) 63 | PUSHDQ (%xmm5) 64 | PUSHDQ (%xmm6) 65 | PUSHDQ (%xmm7) 66 | 67 | push %rax 68 | 69 | mov %r11, %rdi // Call mmk_set_ctx 70 | push %r11 71 | mov 0x8(%r11), %r11 72 | call *%r11 73 | pop %r11 74 | 75 | mov (%r11), %r11 // Check if context was asked 76 | call *%r11 77 | 78 | mov %rax, %r11 79 | pop %rax 80 | 81 | POPDQ (%xmm7) // Restore caller ctx 82 | POPDQ (%xmm6) 83 | POPDQ (%xmm5) 84 | POPDQ (%xmm4) 85 | POPDQ (%xmm3) 86 | POPDQ (%xmm2) 87 | POPDQ (%xmm1) 88 | POPDQ (%xmm0) 89 | pop %r9 90 | pop %r8 91 | pop %rcx 92 | pop %rdx 93 | pop %rsi 94 | pop %rdi 95 | 96 | test %r11, %r11 97 | jnz ret_ctx 98 | 99 | pop %r11 100 | mov (start - next - 0x8)(%r11), %r11 // Retrieve offset at 101 | // the start of the map 102 | jmp *%r11 103 | 104 | ret_ctx: // Return context 105 | pop %r11 106 | mov (start - next - 0x10)(%r11), %r11 107 | mov 0x10(%r11), %r11 // Call mmk_ctx 108 | call *%r11 109 | ret 110 | 111 | .globl MANGLE(mmk_trampoline_end) 112 | MANGLE(mmk_trampoline_end): 113 | nop 114 | -------------------------------------------------------------------------------- /src/asm/trampoline-x86_64-win.S: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright © 2016 Franklin "Snaipe" Mathieu 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | # define PUSHDQ(Reg) \ 26 | sub $0x10, %rsp; \ 27 | movdqu Reg, (%rsp) 28 | 29 | # define POPDQ(Reg) \ 30 | movdqu (%rsp), Reg; \ 31 | add $0x10, %rsp 32 | 33 | .globl mmk_trampoline 34 | mmk_trampoline: 35 | start: 36 | call next // Retrieve IP 37 | next: 38 | pop %rax 39 | 40 | push %rax // Setup mock contextscratch registers 41 | mov (start - next - 0x10)(%rax), %rax 42 | 43 | push %rdx // Save caller ctx 44 | push %rcx 45 | push %r8 46 | push %r9 47 | PUSHDQ (%xmm0) 48 | PUSHDQ (%xmm1) 49 | PUSHDQ (%xmm2) 50 | PUSHDQ (%xmm3) 51 | 52 | mov %rax, %rcx // Call mmk_set_ctx 53 | push %rax 54 | mov 0x8(%rax), %rax 55 | call *%rax 56 | pop %rax 57 | 58 | mov (%rax), %rax // Check if context was asked 59 | call *%rax 60 | 61 | POPDQ (%xmm3) // Restore caller ctx 62 | POPDQ (%xmm2) 63 | POPDQ (%xmm1) 64 | POPDQ (%xmm0) 65 | pop %r9 66 | pop %r8 67 | pop %rcx 68 | pop %rdx 69 | 70 | test %rax, %rax 71 | jnz ret_ctx 72 | 73 | pop %rax 74 | mov (start - next - 0x8)(%rax), %rax // Retrieve offset at 75 | // the start of the map 76 | jmp *%rax 77 | 78 | ret_ctx: // Return context 79 | pop %rax 80 | mov (start - next - 0x10)(%rax), %rax 81 | mov 0x10(%rax), %rax // Call mmk_ctx 82 | call *%rax 83 | ret 84 | 85 | .globl mmk_trampoline_end 86 | mmk_trampoline_end: 87 | nop 88 | -------------------------------------------------------------------------------- /src/asm/trampoline-x86_64-win.asm: -------------------------------------------------------------------------------- 1 | ; 2 | ; The MIT License (MIT) 3 | ; 4 | ; Copyright © 2016 Franklin "Snaipe" Mathieu 5 | ; 6 | ; Permission is hereby granted, free of charge, to any person obtaining a copy 7 | ; of this software and associated documentation files (the "Software"), to deal 8 | ; in the Software without restriction, including without limitation the rights 9 | ; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | ; copies of the Software, and to permit persons to whom the Software is 11 | ; furnished to do so, subject to the following conditions: 12 | ; 13 | ; The above copyright notice and this permission notice shall be included in 14 | ; all copies or substantial portions of the Software. 15 | ; 16 | ; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | ; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | ; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | ; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | ; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | ; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | ; THE SOFTWARE. 23 | ; 24 | .CODE 25 | 26 | PUSHDQ MACRO Reg; 27 | sub rsp, 10h 28 | movdqu xmmword ptr [rsp], Reg 29 | ENDM 30 | 31 | POPDQ MACRO Reg; 32 | movdqu Reg, xmmword ptr [rsp] 33 | add rsp, 10h 34 | ENDM 35 | 36 | ; mmk_ctx defined as proc instead of qword to avoid 37 | ; MASM's relative addressing bullshit 38 | extern mmk_ctx:proc 39 | 40 | mmk_trampoline label far 41 | call next ; Retrieve IP 42 | next: 43 | pop rax 44 | 45 | and rax, 0fffffffffffff000h 46 | push rax 47 | mov rax, qword ptr [rax] ; Setup mock context 48 | 49 | push rcx ; Save caller ctx 50 | push rdx 51 | push r8 52 | push r9 53 | PUSHDQ xmm0 54 | PUSHDQ xmm1 55 | PUSHDQ xmm2 56 | PUSHDQ xmm3 57 | 58 | mov rcx, rax ; Call mmk_set_ctx 59 | push rax 60 | call qword ptr [rax + 8h] 61 | pop rax 62 | 63 | call qword ptr [rax] ; Check if context was asked 64 | 65 | POPDQ xmm3 ; Restore caller ctx 66 | POPDQ xmm2 67 | POPDQ xmm1 68 | POPDQ xmm0 69 | pop r9 70 | pop r8 71 | pop rdx 72 | pop rcx 73 | 74 | test rax, rax 75 | jnz ret_ctx 76 | 77 | pop rax ; Retrieve offset at 78 | jmp qword ptr [rax + 8h] ; the start of the map 79 | 80 | ret_ctx: ; Return context 81 | pop rax 82 | mov rax, qword ptr [rax] 83 | ret 84 | mmk_trampoline_end label far 85 | 86 | public mmk_trampoline 87 | public mmk_trampoline_end 88 | 89 | end 90 | -------------------------------------------------------------------------------- /src/common.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright © 2016 Franklin "Snaipe" Mathieu 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | #ifndef COMMON_H_ 25 | # define COMMON_H_ 26 | 27 | # include 28 | 29 | # if __STDC_VERSION__ >= 201112L 30 | # include 31 | # elif defined __GNUC__ 32 | # define mmk_noreturn __attribute__((noreturn)) 33 | # elif defined _MSC_VER 34 | # define mmk_noreturn __declspec(noreturn) 35 | # else 36 | # define mmk_noreturn 37 | # endif 38 | 39 | # if defined __GNUC__ 40 | # define mmk_unreachable() __builtin_unreachable() 41 | # elif defined _MSC_VER 42 | # define mmk_unreachable() __assume(0) 43 | # else 44 | /* Last resort. We *need* to theoritically call a noreturn function after 45 | * mmk_abort, so the function itself can be marked noreturn. 46 | * In reality, this is never called and is just a bunch of wasted opcodes. */ 47 | # define mmk_unreachable() abort() 48 | # endif 49 | 50 | #endif /* !COMMON_H_ */ 51 | -------------------------------------------------------------------------------- /src/config.h.in: -------------------------------------------------------------------------------- 1 | #ifndef CONFIG_H_IN_ 2 | # define CONFIG_H_IN_ 3 | 4 | #cmakedefine MMK_ARCH "@MMK_ARCH@" 5 | #cmakedefine MMK_BITS @MMK_BITS@ 6 | #cmakedefine MMK_MANGLING @MMK_MANGLING@ 7 | 8 | #cmakedefine MMK_ARCH_x86 @MMK_ARCH_x86@ 9 | #cmakedefine MMK_ARCH_x86_64 @MMK_ARCH_x86_64@ 10 | #cmakedefine MMK_ARCH_ARM @MMK_ARCH_ARM@ 11 | #cmakedefine MMK_ARCH_ARM64 @MMK_ARCH_ARM64@ 12 | 13 | #cmakedefine MMK_EXE_FMT_ELF @MMK_EXE_FMT_ELF@ 14 | #cmakedefine MMK_EXE_FMT_PE @MMK_EXE_FMT_PE@ 15 | #cmakedefine MMK_EXE_FMT_MACH_O @MMK_EXE_FMT_MACH_O@ 16 | 17 | #cmakedefine HAVE__R_DEBUG @HAVE__R_DEBUG@ 18 | #cmakedefine HAVE__DYNAMIC @HAVE__DYNAMIC@ 19 | #cmakedefine HAVE_ELF_AUXV_T @HAVE_ELF_AUXV_T@ 20 | #cmakedefine HAVE_ELF_AUXINFO @HAVE_ELF_AUXINFO@ 21 | #cmakedefine HAVE_MMAP @HAVE_MMAP@ 22 | #cmakedefine HAVE_MMAP_MAP_ANONYMOUS @HAVE_MMAP_MAP_ANONYMOUS@ 23 | #cmakedefine HAVE_MMAP_MAP_ANON @HAVE_MMAP_MAP_ANON@ 24 | #cmakedefine HAVE___STDIO_COMMON_VFPRINTF @HAVE___STDIO_COMMON_VFPRINTF@ 25 | 26 | #endif /* !CONFIG_H_IN_ */ 27 | -------------------------------------------------------------------------------- /src/core.c: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright © 2016 Franklin "Snaipe" Mathieu 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | #include 25 | 26 | #include "mimick.h" 27 | #include "mimick/assert.h" 28 | 29 | #include "mock.h" 30 | #include "plt.h" 31 | #include "stub.h" 32 | #include "vitals.h" 33 | 34 | static struct { 35 | int initialized; 36 | plt_ctx plt; 37 | } self; 38 | 39 | void mmk_init(void) 40 | { 41 | if (self.initialized) 42 | return; 43 | 44 | self.plt = plt_init_ctx(); 45 | mmk_assert(self.plt != (void*) -1); 46 | 47 | mmk_init_vital_functions(self.plt); 48 | 49 | self.initialized = 1; 50 | } 51 | 52 | plt_ctx mmk_plt_ctx(void) 53 | { 54 | mmk_assert(self.initialized); 55 | return self.plt; 56 | } 57 | 58 | #undef mmk_reset 59 | void mmk_reset(mmk_fn fn) 60 | { 61 | if (fn == MMK_MOCK_INVALID) 62 | return; 63 | 64 | struct mmk_stub *stub = mmk_ask_ctx(fn); 65 | struct mmk_mock_ctx *mock = mmk_stub_context(stub); 66 | 67 | mmk_mock_destroy_internal(mock); 68 | } 69 | -------------------------------------------------------------------------------- /src/core.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright © 2016 Franklin "Snaipe" Mathieu 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | #ifndef CORE_H_ 25 | # define CORE_H_ 26 | 27 | # include "plt.h" 28 | 29 | void mmk_init(void); 30 | plt_ctx mmk_plt_ctx(void); 31 | 32 | #endif /* !CORE_H_ */ 33 | -------------------------------------------------------------------------------- /src/matcher.c: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright © 2016 Franklin "Snaipe" Mathieu 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | #include 25 | #include 26 | 27 | #include "mimick/assert.h" 28 | #include "mimick/matcher.h" 29 | 30 | #include "threadlocal.h" 31 | #include "vitals.h" 32 | 33 | static MMK_THREAD_LOCAL(struct mmk_matcher *) matcher_ctx; 34 | 35 | void mmk_matcher_init(int kind) 36 | { 37 | struct mmk_matcher *ctx = mmk_malloc(sizeof (struct mmk_matcher)); 38 | *ctx = (struct mmk_matcher) { 39 | .kind = (enum mmk_matcher_kind) kind, 40 | }; 41 | tls_set(struct mmk_matcher *, matcher_ctx, ctx); 42 | } 43 | 44 | void mmk_matcher_term(void) 45 | { 46 | tls_set(struct mmk_matcher *, matcher_ctx, NULL); 47 | } 48 | 49 | struct mmk_matcher *mmk_matcher_ctx(void) 50 | { 51 | return tls_get(struct mmk_matcher *, matcher_ctx); 52 | } 53 | 54 | void mmk_matcher_add_data(enum mmk_matcher_kind kind, int counter, void *data) 55 | { 56 | struct mmk_matcher *out = mmk_malloc(sizeof (struct mmk_matcher)); 57 | struct mmk_matcher *prev = tls_get(struct mmk_matcher *, matcher_ctx); 58 | 59 | *out = (struct mmk_matcher) { 60 | .kind = kind, 61 | .prio = counter, 62 | .data = data, 63 | }; 64 | 65 | for (struct mmk_matcher *m = tls_get(struct mmk_matcher *, matcher_ctx)->next; 66 | m != NULL && m->prio < out->prio; 67 | prev = m, m = m->next) 68 | continue; 69 | out->next = prev->next; 70 | prev->next = out; 71 | } 72 | 73 | void mmk_matcher_add_fn(enum mmk_matcher_kind kind, int counter, void (*fn)(void)) 74 | { 75 | mmk_matcher_add_data(kind, counter, (void *)fn); 76 | } 77 | 78 | void mmk_matcher_add(enum mmk_matcher_kind kind, int counter) 79 | { 80 | mmk_matcher_add_data(kind, counter, NULL); 81 | } 82 | 83 | void (*mmk_matcher_get_predicate(struct mmk_matcher *m))(void) 84 | { 85 | return (void (*)(void)) m->data; 86 | } 87 | -------------------------------------------------------------------------------- /src/mock.c: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright © 2016 Franklin "Snaipe" Mathieu 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | #include "mimick/assert.h" 25 | 26 | #include "core.h" 27 | #include "mimick.h" 28 | #include "mock.h" 29 | #include "threadlocal.h" 30 | #include "vitals.h" 31 | 32 | /* Anti-optimization checks */ 33 | 34 | static MMK_THREAD_LOCAL(unsigned) mock_called; 35 | 36 | void mmk_mock_report_call(void) 37 | { 38 | tls_op(unsigned, mock_called, +, 1); 39 | } 40 | 41 | void mmk_mock_reset_call(const char *file, int line) 42 | { 43 | unsigned val = tls_get(unsigned, mock_called); 44 | if (!val) { 45 | mmk_panic("mimick: %s:%d: mocked function was never called " 46 | "-- was it optimized away?\n", file, line); 47 | } 48 | if (val > 1) { 49 | mmk_panic("mimick: %s:%d: mocked function was called more than once " 50 | "(exactly %u times) in the call expression.\n", 51 | file, line, val); 52 | } 53 | tls_set(unsigned, mock_called, 0); 54 | } 55 | 56 | /* Mock creation */ 57 | 58 | mmk_fn mmk_mock_create_internal(const char *target, mmk_fn fn, 59 | struct mmk_mock_options opts) 60 | { 61 | mmk_init(); 62 | 63 | struct mmk_mock_ctx *ctx = mmk_malloc(sizeof (struct mmk_mock_ctx)); 64 | if (!ctx) { 65 | if (opts.noabort) 66 | return MMK_MOCK_INVALID; 67 | mmk_panic("mimick: Mock allocation for %s failed.\n", target); 68 | } 69 | *ctx = (struct mmk_mock_ctx) { 70 | .params = NULL, 71 | }; 72 | 73 | char *name_end = mmk_strchr(target, '@'); 74 | size_t name_sz; 75 | if (name_end == NULL) 76 | name_sz = mmk_strlen(target); 77 | else 78 | name_sz = (size_t) (name_end - target); 79 | char *name = mmk_malloc(name_sz + 1); 80 | 81 | if (!name) { 82 | if (opts.noabort) { 83 | mmk_free(ctx); 84 | return MMK_MOCK_INVALID; 85 | } 86 | mmk_panic("mimick: Mock allocation for %s failed.\n", target); 87 | } 88 | 89 | mmk_strncpy(name, target, name_sz); 90 | name[name_sz] = '\0'; 91 | 92 | int self = !name_end || mmk_strneq(name_end + 1, "self", 4); 93 | 94 | ctx->stubs = mmk_stub_create(target, fn, ctx); 95 | if (ctx->stubs == MMK_STUB_INVALID) { 96 | if (opts.noabort) { 97 | mmk_free(ctx); 98 | mmk_free(name); 99 | return MMK_MOCK_INVALID; 100 | } 101 | mmk_panic("mimick: Could not find GOT " 102 | "entry for function %s.\n", target); 103 | } 104 | 105 | if (!self) 106 | ctx->stubs->next = mmk_stub_create(name, fn, ctx); 107 | 108 | mmk_free(name); 109 | return (mmk_fn) ctx->stubs->trampoline; 110 | } 111 | 112 | void mmk_mock_destroy_internal(struct mmk_mock_ctx *mock) 113 | { 114 | for (struct mmk_stub *s = mock->stubs; s;) { 115 | struct mmk_stub *next = s->next; 116 | mmk_stub_destroy(s); 117 | s = next; 118 | } 119 | mmk_free(mock->call_data); 120 | for (struct mmk_params *p = mock->params, *pnext; p;) { 121 | pnext = p->next; 122 | for (struct mmk_matcher *m = p->matcher_ctx, *next; m;) { 123 | next = m->next; 124 | mmk_free(m); 125 | m = next; 126 | } 127 | mmk_free(p); 128 | p = pnext; 129 | } 130 | mmk_free(mock); 131 | } 132 | 133 | void *mmk_mock_params_begin(struct mmk_mock_ctx *mock) { 134 | if (!mock->call_data || !mock->call_data_top) 135 | return NULL; 136 | 137 | return mock->call_data + sizeof (size_t); 138 | } 139 | 140 | void *mmk_mock_params_next(struct mmk_mock_ctx *mock, void *prev) { 141 | char *ptr = prev; 142 | size_t sz = *(size_t*) (ptr - sizeof (size_t)); 143 | ptr += sz + sizeof (size_t); 144 | if (ptr >= mock->call_data + mock->call_data_top) 145 | return NULL; 146 | return ptr; 147 | } 148 | 149 | struct mmk_params *mmk_mock_get_params(void) 150 | { 151 | struct mmk_mock_ctx *mock = mmk_stub_context(mmk_ctx()); 152 | return mock->params; 153 | } 154 | -------------------------------------------------------------------------------- /src/mock.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright © 2016 Franklin "Snaipe" Mathieu 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | #ifndef MOCK_H_ 25 | # define MOCK_H_ 26 | 27 | # include "stub.h" 28 | 29 | struct mmk_mock_ctx { 30 | struct mmk_params *params; 31 | struct mmk_stub *stubs; 32 | char *call_data; 33 | size_t call_data_top; 34 | size_t call_data_size; 35 | }; 36 | 37 | mmk_fn mmk_mock_create_internal(const char *target, mmk_fn fn, 38 | struct mmk_mock_options opts); 39 | void mmk_mock_destroy_internal(struct mmk_mock_ctx *mock); 40 | 41 | #endif /* !MOCK_H_ */ 42 | -------------------------------------------------------------------------------- /src/plt-elf.c: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright © 2016 Franklin "Snaipe" Mathieu 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | #include 25 | #include 26 | #include 27 | 28 | #include "mimick/assert.h" 29 | 30 | #include "config.h" 31 | #include "plt.h" 32 | #include "trampoline.h" 33 | #include "vitals.h" 34 | 35 | #include 36 | 37 | #if MMK_BITS == 32 38 | typedef Elf32_Word ElfWord; 39 | typedef Elf32_Sword ElfSWord; 40 | # ifndef ELF_R_SYM 41 | # define ELF_R_SYM(i) ELF32_R_SYM(i) 42 | # endif 43 | #elif MMK_BITS == 64 44 | typedef Elf64_Xword ElfWord; 45 | typedef Elf64_Sxword ElfSWord; 46 | # ifndef ELF_R_SYM 47 | # define ELF_R_SYM(i) ELF64_R_SYM(i) 48 | # endif 49 | #else 50 | # error Unsupported architecture 51 | #endif 52 | 53 | #if defined HAVE_ELF_AUXV_T 54 | typedef ElfW(auxv_t) ElfAux; 55 | #elif defined HAVE_ELF_AUXINFO 56 | typedef ElfW(Auxinfo) ElfAux; 57 | #else 58 | # error Unsupported platform 59 | #endif 60 | 61 | # if defined __clang__ 62 | void __clear_cache(void *, void *); 63 | # endif 64 | 65 | extern char **environ; 66 | 67 | static size_t get_offsets(plt_ctx ctx, plt_lib lib, const char *name, plt_offset ** offsets); 68 | 69 | static void *lib_dt_lookup(plt_lib lib, ElfSWord tag) 70 | { 71 | ElfW(Addr) base =(ElfW(Addr)) lib->l_addr; 72 | for (const ElfW(Dyn) *dyn = lib->l_ld; dyn->d_tag != DT_NULL; ++dyn) { 73 | if (dyn->d_tag == tag) { 74 | if (dyn->d_un.d_ptr >= base 75 | && (dyn->d_un.d_ptr >> (MMK_BITS - 8)) ^ 0xff) 76 | return (void*) dyn->d_un.d_ptr; 77 | else 78 | return (char*) base + dyn->d_un.d_ptr; 79 | } 80 | } 81 | return NULL; 82 | } 83 | 84 | static ElfWord lib_dt_lookup_val(plt_lib lib, ElfSWord tag) 85 | { 86 | for (const ElfW(Dyn) *dyn = lib->l_ld; dyn->d_tag != DT_NULL; ++dyn) { 87 | if (dyn->d_tag == tag) { 88 | return dyn->d_un.d_val; 89 | } 90 | } 91 | return 0; 92 | } 93 | 94 | #if !defined HAVE__R_DEBUG 95 | static int find_dynamic(struct dl_phdr_info *info, size_t size, void *data) 96 | { 97 | ElfAddr *ctx = data; 98 | 99 | for (ElfOff i = 0; i < info->dlpi_phnum; ++i) { 100 | if (info->dlpi_phdr[i].p_type == PT_DYNAMIC) { 101 | *ctx = info->dlpi_addr + info->dlpi_phdr[i].p_vaddr; 102 | return 1; 103 | } 104 | } 105 | return -1; 106 | } 107 | 108 | static struct r_debug *r_debug_from_dynamic(void *dynamic) 109 | { 110 | for (const ElfW(Dyn) *dyn = dynamic; dyn->d_tag != DT_NULL; ++dyn) { 111 | if (dyn->d_tag == DT_DEBUG) 112 | return (struct r_debug *) dyn->d_un.d_ptr; 113 | } 114 | return NULL; 115 | } 116 | #endif 117 | 118 | static struct r_debug *get_r_debug(void) 119 | { 120 | // Find our own r_debug 121 | struct r_debug *dbg = NULL; 122 | 123 | // First use some known shortcuts 124 | #if defined HAVE__R_DEBUG 125 | dbg = &_r_debug; 126 | #elif defined HAVE__DYNAMIC 127 | dbg = r_debug_from_dynamic(_DYNAMIC); 128 | #endif 129 | 130 | #if !defined HAVE__R_DEBUG 131 | // If there are no available shortcuts, we manually query our own phdrs 132 | # if defined HAVE__DYNAMIC 133 | if (!dbg) { 134 | # endif 135 | ElfAddr dynamic; 136 | if (dl_iterate_phdr(find_dynamic, &dynamic) > 0) 137 | dbg = r_debug_from_dynamic((void *) dynamic); 138 | # if defined HAVE__DYNAMIC 139 | } 140 | # endif 141 | #endif 142 | 143 | return dbg; 144 | } 145 | 146 | plt_ctx plt_init_ctx(void) 147 | { 148 | static struct r_debug *dbg = (void*) -1; 149 | if (dbg == (void*) -1) 150 | dbg = get_r_debug(); 151 | return dbg; 152 | } 153 | 154 | static const char *get_lib_name(plt_ctx ctx, plt_lib lib) 155 | { 156 | /* The name of the main shared object is the empty string, 157 | we return something to be consistent with the eglibc weirdity */ 158 | if (lib == ctx->r_map) 159 | return "self"; 160 | 161 | /* Somewhy, eglibc always set l_name to the empty string. */ 162 | if (lib->l_name[0]) 163 | return lib->l_name; 164 | 165 | const char *strtab = lib_dt_lookup(lib, DT_STRTAB); 166 | ElfWord soname_off = lib_dt_lookup_val(lib, DT_SONAME); 167 | if (!strtab || soname_off == (ElfWord) - 1) 168 | return NULL; 169 | 170 | return &strtab[soname_off]; 171 | } 172 | 173 | plt_lib plt_get_lib(plt_ctx ctx, const char *name) 174 | { 175 | if (!name) 176 | return ctx->r_map; 177 | 178 | const char *val = NULL; 179 | enum plt_selector sel = plt_get_selector(name, &val); 180 | size_t val_len = strlen(val); 181 | int libc = !strcmp(val, "c"); 182 | 183 | for (struct link_map *lm = ctx->r_map; lm != NULL; lm = lm->l_next) { 184 | if (sel == PLT_SEL_LIB) { 185 | const char *libname = get_lib_name(ctx, lm); 186 | if (libc) { 187 | if (strstr(libname, "/libc.so") 188 | || strstr(libname, "/musl.so")) 189 | return lm; 190 | } else { 191 | size_t len = val_len + 8; 192 | char pattern[len]; 193 | snprintf(pattern, len, "/lib%s.so", val); 194 | if (strstr(libname, pattern)) 195 | return lm; 196 | } 197 | } else if (sel == PLT_SEL_NONE || sel == PLT_SEL_FILE) { 198 | const char *libname = get_lib_name(ctx, lm); 199 | if (!strcmp(val, libname)) 200 | return lm; 201 | } else if (sel == PLT_SEL_SYM) { 202 | if (get_offsets(ctx, lm, val, NULL) > 0) 203 | return lm; 204 | } 205 | } 206 | return NULL; 207 | } 208 | 209 | struct rel_info { 210 | ElfW(Rel) *tab; 211 | ElfWord size; 212 | ElfWord entry_sz; 213 | }; 214 | 215 | static uintptr_t get_offset(struct rel_info *info, ElfW(Sym) *symtab, 216 | const char *strtab, const char *name) 217 | { 218 | ElfW(Rel) *rel = info->tab; 219 | for (ElfWord i = 0; i < info->size / info->entry_sz; 220 | ++i, rel = (void*)(((char *) rel) + info->entry_sz)) 221 | { 222 | ElfW(Sym) *sym = &symtab[ELF_R_SYM(rel->r_info)]; 223 | 224 | if (!strcmp(strtab + sym->st_name, name)) 225 | return (uintptr_t) rel->r_offset; 226 | } 227 | return 0; 228 | } 229 | 230 | static size_t get_offsets(plt_ctx ctx, plt_lib lib, const char *name, plt_offset ** offsets) 231 | { 232 | ElfW(Sym) *symtab = (ElfW(Sym)*) lib_dt_lookup(lib, DT_SYMTAB); 233 | const char *strtab = (const char*) lib_dt_lookup(lib, DT_STRTAB); 234 | 235 | ElfW(Rel) *jmprel = lib_dt_lookup(lib, DT_JMPREL); 236 | 237 | ElfWord jmprel_sz = lib_dt_lookup_val(lib, DT_PLTRELSZ); 238 | ElfWord relent_sz; 239 | ElfWord rel_sz; 240 | 241 | // For relocation sections try DT_RELA first, then DT_REL. We don't deal with 242 | // addends anyway. 243 | ElfW(Rel) *rel = lib_dt_lookup(lib, DT_RELA); 244 | if (!rel) { 245 | rel = lib_dt_lookup(lib, DT_REL); 246 | if (!rel) 247 | return 0; 248 | rel_sz = lib_dt_lookup_val(lib, DT_RELSZ); 249 | relent_sz = lib_dt_lookup_val(lib, DT_RELENT); 250 | } else { 251 | rel_sz = lib_dt_lookup_val(lib, DT_RELASZ); 252 | relent_sz = lib_dt_lookup_val(lib, DT_RELAENT); 253 | } 254 | 255 | if (!symtab || !strtab || !(rel || jmprel) || !(rel_sz || jmprel_sz) || !relent_sz) 256 | return 0; 257 | 258 | ElfW(Addr) base = (ElfW(Addr)) lib->l_addr; 259 | #ifdef __FreeBSD__ 260 | if (lib == ctx->r_map) 261 | base = 0; 262 | #endif 263 | 264 | size_t n = 0; 265 | if (offsets) { 266 | *offsets = NULL; 267 | } 268 | 269 | struct rel_info info[] = { 270 | { 271 | .tab = rel, 272 | .size = rel_sz, 273 | .entry_sz = relent_sz, 274 | }, 275 | { 276 | .tab = jmprel, 277 | .size = jmprel_sz, 278 | .entry_sz = relent_sz, 279 | } 280 | }; 281 | 282 | for (size_t i = 0; i < sizeof(info) / sizeof(struct rel_info); ++i) { 283 | uintptr_t off = get_offset(&info[i], symtab, strtab, name); 284 | if (off) { 285 | if (offsets) { 286 | *offsets = mmk_realloc(*offsets, (n + 1) * sizeof(plt_offset)); 287 | (*offsets)[n] = (plt_offset) { .offset = (plt_fn **)(base + off) }; 288 | } 289 | ++n; 290 | } 291 | } 292 | 293 | return n; 294 | } 295 | 296 | plt_offset *plt_get_offsets(plt_ctx ctx, plt_lib lib, const char *name, size_t *n) 297 | { 298 | plt_offset *ot = NULL; 299 | *n = get_offsets(ctx, lib, name, &ot); 300 | return ot; 301 | } 302 | 303 | #define align2_down(v, d) ((v) & ~((d) - 1)) 304 | 305 | void plt_set_offsets(plt_offset *offset, size_t nb_off, plt_fn *newval) 306 | { 307 | for (size_t i = 0; i < nb_off; ++i) { 308 | if (!offset[i].oldval) 309 | offset[i].oldval = *offset[i].offset; 310 | void *page_start = (void *)align2_down((uintptr_t)offset[i].offset, 4096); 311 | 312 | /* making a page in a rx segment rwx is usually very bad practice, 313 | but this is a test context, we don't have to care as much. 314 | Implementing this right assumes that we have a way to know 315 | the protection of an existing page, which is not necessarily 316 | available on all unices. */ 317 | mmk_mprotect(page_start, 4096, PROT_READ|PROT_WRITE|PROT_EXEC); 318 | # if defined __clang__ // Check for Clang first, it may set __GNUC__ too. 319 | __clear_cache(page_start, page_start + 4096); 320 | # elif defined __GNUC__ 321 | __builtin___clear_cache((char *)page_start, (char *)(page_start + 4096)); 322 | # endif 323 | *offset[i].offset = newval; 324 | } 325 | } 326 | 327 | void plt_reset_offsets(plt_offset *offset, size_t nb_off) 328 | { 329 | for (size_t i = 0; i < nb_off; ++i) { 330 | *offset[i].offset = offset[i].oldval; 331 | } 332 | } 333 | 334 | static unsigned long elf_hash (const char *s) 335 | { 336 | unsigned long h = 0, high; 337 | while (*s) { 338 | h = (h << 4) + (unsigned char) *s++; 339 | if ((high = h & 0xf0000000)) 340 | h ^= high >> 24; 341 | h &= ~high; 342 | } 343 | return h; 344 | } 345 | 346 | static ElfW(Sym) *elf_hash_find(ElfW(Word) *hash, ElfW(Sym) *symtab, 347 | const char *strtab, const char *name) 348 | { 349 | struct { 350 | ElfW(Word) nb_buckets; 351 | ElfW(Word) nb_chains; 352 | } *h_info = (void*) hash; 353 | 354 | ElfW(Word) *buckets = (ElfW(Word)*) (h_info + 1); 355 | ElfW(Word) *chains = (ElfW(Word)*) (h_info + 1) + h_info->nb_buckets; 356 | 357 | unsigned long idx = elf_hash(name) % h_info->nb_buckets; 358 | 359 | for (ElfW(Word) si = buckets[idx]; si != STN_UNDEF; si = chains[si]) { 360 | if (mmk_streq(&strtab[symtab[si].st_name], name)) 361 | return &symtab[si]; 362 | } 363 | return NULL; 364 | } 365 | 366 | struct gnu_hash_header { 367 | uint32_t nbuckets; 368 | uint32_t symoffset; 369 | uint32_t bloom_size; 370 | uint32_t bloom_shift; 371 | }; 372 | 373 | static uint32_t gnu_hash(const char *s) 374 | { 375 | const uint8_t *name = (const uint8_t *)s; 376 | uint32_t h = 5381; 377 | while (*name) 378 | h = (h << 5) + h + *name++; 379 | return h; 380 | } 381 | 382 | static ElfW(Sym) *gnu_hash_find(struct gnu_hash_header *gnuhash, ElfW(Sym) *symtab, 383 | const char *strtab, const char *name) 384 | { 385 | ElfW(Off) *bloom = (ElfW(Off) *)(gnuhash + 1); 386 | uint32_t *buckets = (uint32_t *)(bloom + gnuhash->bloom_size); 387 | uint32_t *chains = buckets + gnuhash->nbuckets; 388 | 389 | uint32_t symhash = gnu_hash(name); 390 | 391 | // Grab the bloom filter entry, and test both h1 and h2 are present. 392 | ElfW(Off) filter = bloom[(symhash / MMK_BITS) % gnuhash->bloom_size]; 393 | if ((filter & (1UL << symhash % MMK_BITS)) == 0) 394 | return NULL; 395 | if ((filter & (1UL << (symhash >> gnuhash->bloom_shift) % MMK_BITS)) == 0) 396 | return NULL; 397 | 398 | for (uint32_t idx = buckets[symhash % gnuhash->nbuckets];;++idx) { 399 | uint32_t chainhash = chains[idx - gnuhash->symoffset]; 400 | if ((chainhash | 1) == (symhash | 1) && 401 | strcmp(strtab + symtab[idx].st_name, name) == 0) 402 | return &symtab[idx]; 403 | if ((chainhash & 1) != 0) 404 | break; 405 | } 406 | return NULL; 407 | } 408 | 409 | static ElfW(Sym) *sym_lookup_dyn(plt_lib lib, const char *name) 410 | { 411 | ElfW(Sym) *symtab = lib_dt_lookup(lib, DT_SYMTAB); 412 | const char *strtab = lib_dt_lookup(lib, DT_STRTAB); 413 | 414 | if (!symtab || !strtab) 415 | return NULL; 416 | 417 | #ifdef DT_GNU_HASH 418 | // trust GNU hash if we have it. 419 | struct gnu_hash_header *gnu_hash = lib_dt_lookup(lib, DT_GNU_HASH); 420 | if (gnu_hash) 421 | return gnu_hash_find (gnu_hash, symtab, strtab, name); 422 | #endif 423 | 424 | // Look up symbol table using traditional ELF hash. 425 | ElfW(Word) *hash = lib_dt_lookup(lib, DT_HASH); 426 | if (hash) 427 | return elf_hash_find (hash, symtab, strtab, name); 428 | 429 | // XXX: we could do a linear walk of the symbol table here... 430 | return NULL; 431 | 432 | } 433 | 434 | plt_fn *plt_get_real_fn(plt_ctx ctx, const char *name) 435 | { 436 | for (struct link_map *lm = ctx->r_map; lm != NULL; lm = lm->l_next) { 437 | ElfW(Sym) *sym = sym_lookup_dyn(lm, name); 438 | if (sym) { 439 | /* Some compilers (e.g. ICC) put unresolved symbols into the 440 | symbol table with a size of 0. We ignore them to avoid 441 | getting the address of the PLT stub. */ 442 | if (sym->st_size == 0) 443 | continue; 444 | 445 | return (void *) (sym->st_value + lm->l_addr); 446 | } 447 | } 448 | return NULL; 449 | } 450 | -------------------------------------------------------------------------------- /src/plt-elf.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright © 2016 Franklin "Snaipe" Mathieu 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | #ifndef PLT_ELF_H_ 25 | # define PLT_ELF_H_ 26 | 27 | # include 28 | 29 | # ifdef __FreeBSD__ 30 | # include 31 | # define ElfW(type) ElfW_(Elf, type) 32 | # define ElfW_(e,t) ElfW__(e, _##t) 33 | # define ElfW__(e,t) e##t 34 | # endif 35 | 36 | typedef struct link_map *plt_lib; 37 | typedef struct r_debug *plt_ctx; 38 | typedef void *plt_got; 39 | typedef void (plt_fn)(void); 40 | 41 | #endif /* !PLT_ELF_H_ */ 42 | -------------------------------------------------------------------------------- /src/plt-mach-o.c: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright © 2016 Franklin "Snaipe" Mathieu 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #include "mimick/assert.h" 34 | 35 | #include "plt.h" 36 | #include "vitals.h" 37 | 38 | #if MMK_BITS == 32 39 | typedef struct mach_header mach_hdr; 40 | typedef struct nlist sym; 41 | typedef struct section section; 42 | typedef struct segment_command segment_cmd; 43 | #define MMK_LC_SEGMENT LC_SEGMENT 44 | #elif MMK_BITS == 64 45 | typedef struct mach_header_64 mach_hdr; 46 | typedef struct nlist_64 sym; 47 | typedef struct section_64 section; 48 | typedef struct segment_command_64 segment_cmd; 49 | #define MMK_LC_SEGMENT LC_SEGMENT_64 50 | #else 51 | # error Unsupported architecture 52 | #endif 53 | 54 | plt_ctx plt_init_ctx(void) 55 | { 56 | return NULL; 57 | } 58 | 59 | plt_lib plt_get_lib(plt_ctx ctx, const char *name) 60 | { 61 | (void) ctx; 62 | if (!name || !strcmp(name, "self")) 63 | return -1; 64 | 65 | const char *val = NULL; 66 | enum plt_selector sel = plt_get_selector(name, &val); 67 | size_t val_len = strlen(val); 68 | 69 | /* TODO: this is not thread safe, as another thread can load or unload 70 | * images on the fly -- find a way to fix this. */ 71 | 72 | size_t nb_images = _dyld_image_count(); 73 | for (size_t i = 1; i < nb_images; ++i) { 74 | if (sel == PLT_SEL_LIB) { 75 | size_t len = val_len + 8; 76 | char pattern[len]; 77 | snprintf(pattern, len, "/lib%s.dylib", val); 78 | const char *img_name = _dyld_get_image_name(i); 79 | if (img_name && strstr(img_name, pattern)) 80 | return i; 81 | } else if (sel == PLT_SEL_NONE || sel == PLT_SEL_FILE) { 82 | const char *img_name = _dyld_get_image_name(i); 83 | if (img_name && !strcmp(img_name, val)) 84 | return i; 85 | } else if (sel == PLT_SEL_SYM) { 86 | plt_offset *off = plt_get_offsets(ctx, i, val, NULL); 87 | mmk_free(off); 88 | if (off) 89 | return i; 90 | } 91 | } 92 | return 0; 93 | } 94 | 95 | static size_t get_offsets(size_t *off, size_t n, 96 | const sym *symtab, const uint32_t *isymtab, size_t nsyms, 97 | const char *strtab, const char *name) 98 | { 99 | size_t j = 0; 100 | for (size_t i = 0; j < n && i < nsyms; ++i) { 101 | if (isymtab[i] == INDIRECT_SYMBOL_LOCAL || 102 | isymtab[i] == INDIRECT_SYMBOL_ABS) 103 | continue; 104 | 105 | const sym *s = &symtab[isymtab[i]]; 106 | const char *symname = strtab + s->n_un.n_strx; 107 | 108 | if (!strcmp(symname + 1, name)) 109 | off[j++] = i; 110 | } 111 | return j; 112 | } 113 | 114 | static inline void *ptr_add(const void *ptr, size_t off) 115 | { 116 | return (char *) ptr + off; 117 | } 118 | 119 | static void find_tables(const struct mach_header *hdr, 120 | const sym **symtab, const char **strtab, const uint32_t **isymtab, size_t *nisyms) 121 | { 122 | int symtab_found = 0, dysymtab_found = 0; 123 | const struct load_command *lc = ptr_add(hdr, sizeof (mach_hdr)); 124 | for (size_t i = 0; i < hdr->ncmds; ++i, lc = ptr_add(lc, lc->cmdsize)) { 125 | if (lc->cmd == LC_SYMTAB) { 126 | const struct symtab_command *sc = (void *) lc; 127 | *symtab = ptr_add(hdr, sc->symoff); 128 | *strtab = ptr_add(hdr, sc->stroff); 129 | symtab_found = 1; 130 | } else if (lc->cmd == LC_DYSYMTAB) { 131 | const struct dysymtab_command *dsc = (void *) lc; 132 | *isymtab = ptr_add(hdr, dsc->indirectsymoff); 133 | *nisyms = dsc->nindirectsyms; 134 | dysymtab_found = 1; 135 | } 136 | 137 | if (symtab_found && dysymtab_found) 138 | return; 139 | } 140 | } 141 | 142 | static const section *get_section(const struct mach_header *hdr, 143 | const char *segname, const char *sectname) 144 | { 145 | const struct load_command *lc = ptr_add(hdr, sizeof (mach_hdr)); 146 | for (size_t i = 0; i < hdr->ncmds; ++i, lc = ptr_add(lc, lc->cmdsize)) { 147 | if (lc->cmd == MMK_LC_SEGMENT) { 148 | const segment_cmd *sc = (void *) lc; 149 | if (strncmp(segname, sc->segname, 16)) 150 | continue; 151 | const section *s = ptr_add(sc, sizeof (segment_cmd)); 152 | for (size_t j = 0; j < sc->nsects; ++j, ++s) { 153 | if (!strncmp(sectname, s->sectname, 16)) 154 | return s; 155 | } 156 | } 157 | } 158 | return NULL; 159 | } 160 | 161 | static inline plt_fn **find_offset_in(const struct mach_header *hdr, 162 | const section *sec, size_t *indices, size_t n) 163 | { 164 | plt_fn **got = ptr_add(hdr, sec->offset); 165 | 166 | uint32_t idx_start = sec->reserved1; 167 | uint32_t count = sec->size / sizeof (void *); 168 | 169 | for (size_t i = 0; i < n; ++i) { 170 | if (indices[i] >= idx_start && indices[i] < idx_start + count) 171 | return &got[indices[i] - idx_start]; 172 | } 173 | return NULL; 174 | } 175 | 176 | plt_offset *plt_get_offsets(plt_ctx ctx, plt_lib lib, const char *name, size_t *count) 177 | { 178 | mmk_assert(lib); 179 | if (lib == -1) 180 | lib = 0; 181 | 182 | const struct mach_header *hdr = _dyld_get_image_header(lib); 183 | 184 | if (!hdr) 185 | return NULL; 186 | 187 | volatile const char *libname = _dyld_get_image_name(lib); 188 | (void) libname; 189 | 190 | const sym *symtab = NULL; 191 | const char *strtab = NULL; 192 | const uint32_t *isymtab = NULL; 193 | size_t nsyms = 0; 194 | 195 | find_tables(hdr, &symtab, &strtab, &isymtab, &nsyms); 196 | 197 | if (!symtab || !strtab || !isymtab || !nsyms) 198 | return NULL; 199 | 200 | size_t indices[32]; 201 | 202 | size_t n = get_offsets(indices, 32, symtab, isymtab, nsyms, strtab, name); 203 | 204 | if (n == 0) 205 | return NULL; 206 | 207 | if (!count) 208 | return mmk_malloc(1); 209 | 210 | /* First offset is duplicated, so we ignore it by decrementing the count */ 211 | plt_offset *ot = mmk_malloc(sizeof (plt_offset) * (n - 1)); 212 | 213 | static const char *gots[] = { 214 | "__la_symbol_ptr", "__nl_symbol_ptr", "__got" 215 | }; 216 | 217 | size_t offidx = 0; 218 | for (size_t sidx = 0; sidx < sizeof (gots) / sizeof (char *); ++sidx) { 219 | const section *got = get_section(hdr, SEG_DATA, gots[sidx]); 220 | if (!got) 221 | continue; 222 | 223 | plt_fn **off = find_offset_in(hdr, got, indices, n); 224 | if (!off) 225 | continue; 226 | 227 | ot[offidx++] = (plt_offset) { .offset = off }; 228 | } 229 | *count = offidx; 230 | 231 | return ot; 232 | } 233 | 234 | void plt_set_offsets(plt_offset *offset, size_t nb_off, plt_fn *newval) 235 | { 236 | for (size_t i = 0; i < nb_off; ++i) { 237 | if (!offset[i].oldval) 238 | offset[i].oldval = *offset[i].offset; 239 | *offset[i].offset = newval; 240 | } 241 | } 242 | 243 | void plt_reset_offsets(plt_offset *offset, size_t nb_off) 244 | { 245 | for (size_t i = 0; i < nb_off; ++i) { 246 | *offset[i].offset = offset[i].oldval; 247 | } 248 | } 249 | 250 | plt_fn *plt_get_real_fn(plt_ctx ctx, const char *name) 251 | { 252 | (void) ctx; 253 | 254 | /* We could iterate through the images ourselves, but dlsym is more 255 | * convenient here */ 256 | return (plt_fn *) dlsym(RTLD_DEFAULT, name); 257 | } 258 | -------------------------------------------------------------------------------- /src/plt-mach-o.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright © 2016 Franklin "Snaipe" Mathieu 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | #ifndef PLT_MACH_O_H_ 25 | # define PLT_MACH_O_H_ 26 | 27 | typedef int plt_lib; 28 | typedef void *plt_ctx; 29 | typedef void *plt_got; 30 | typedef void (plt_fn)(void); 31 | 32 | #endif /* !PLT_MACH_O_H_ */ 33 | -------------------------------------------------------------------------------- /src/plt-pe.c: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright © 2016 Franklin "Snaipe" Mathieu 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | #include "plt.h" 25 | 26 | #include 27 | #include 28 | #include 29 | 30 | #include "mimick/assert.h" 31 | 32 | #include "vitals.h" 33 | 34 | #define IDIR_IMPORT 1 // Index of the import directory entry 35 | 36 | static plt_fn **plt_get_offset(plt_lib lib, const char *name); 37 | 38 | plt_ctx plt_init_ctx(void) { 39 | return NULL; 40 | } 41 | 42 | static plt_fn **plt_find_offset(const char *name, plt_lib *lib) 43 | { 44 | HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, 45 | GetCurrentProcessId()); 46 | mmk_assert(snap != INVALID_HANDLE_VALUE); 47 | 48 | MODULEENTRY32 mod = { .dwSize = sizeof(MODULEENTRY32) }; 49 | for (BOOL more = Module32First(snap, &mod); more; 50 | more = Module32Next(snap, &mod)) 51 | { 52 | plt_fn **fn = plt_get_offset(mod.hModule, name); 53 | if (fn) { 54 | if (lib) 55 | *lib = mod.hModule; 56 | return fn; 57 | } 58 | } 59 | mmk_assert(GetLastError() == ERROR_NO_MORE_FILES); 60 | return NULL; 61 | } 62 | 63 | plt_lib plt_get_lib(plt_ctx ctx, const char *name) 64 | { 65 | (void) ctx; 66 | if (!name) 67 | name = "self"; 68 | 69 | if (mmk_streq(name, "self")) 70 | return GetModuleHandle(NULL); 71 | 72 | const char *val = NULL; 73 | enum plt_selector sel = plt_get_selector(name, &val); 74 | size_t val_len = mmk_strlen(val); 75 | 76 | if (sel == PLT_SEL_FILE || sel == PLT_SEL_LIB) { 77 | HMODULE m = GetModuleHandle(val); 78 | if (!m) { 79 | size_t sz = val_len + 4; 80 | char *buf = alloca(sz); 81 | snprintf(buf, sz, "lib%s", val); 82 | m = GetModuleHandle(buf); 83 | } 84 | return m; 85 | } else if (sel == PLT_SEL_SYM) { 86 | plt_lib lib; 87 | plt_fn **fn = plt_find_offset(name + 4, &lib); 88 | if (fn) 89 | return lib; 90 | } 91 | return NULL; 92 | } 93 | 94 | static inline PIMAGE_NT_HEADERS nt_header_from_lib(plt_lib lib) 95 | { 96 | PIMAGE_DOS_HEADER dos_hdr = (PIMAGE_DOS_HEADER) lib; 97 | return (PIMAGE_NT_HEADERS) ((char *) dos_hdr + dos_hdr->e_lfanew); 98 | } 99 | 100 | static inline PIMAGE_IMPORT_DESCRIPTOR get_first_import_descriptor(plt_lib lib) 101 | { 102 | PIMAGE_NT_HEADERS nthdr = nt_header_from_lib(lib); 103 | DWORD off = nthdr->OptionalHeader.DataDirectory[IDIR_IMPORT].VirtualAddress; 104 | if (!off) 105 | return NULL; 106 | return (PIMAGE_IMPORT_DESCRIPTOR) ((char *) lib + off); 107 | } 108 | 109 | static plt_fn **plt_get_offset(plt_lib lib, const char *name) 110 | { 111 | char *base = lib; 112 | for (PIMAGE_IMPORT_DESCRIPTOR entry = get_first_import_descriptor(lib); 113 | entry && entry->Name; 114 | entry++) 115 | { 116 | uintptr_t *thunk = (uintptr_t *) (base + entry->FirstThunk); 117 | PIMAGE_THUNK_DATA thunk_data = (PIMAGE_THUNK_DATA) (base 118 | + (entry->OriginalFirstThunk 119 | ? entry->OriginalFirstThunk 120 | : entry->FirstThunk)); 121 | for (; thunk_data->u1.AddressOfData != 0; ++thunk, ++thunk_data) 122 | { 123 | PIMAGE_IMPORT_BY_NAME ibn = (void *) thunk_data->u1.AddressOfData; 124 | if (!strcmp(name, base + (uintptr_t) ibn->Name)) 125 | return (plt_fn **) thunk; 126 | } 127 | } 128 | return NULL; 129 | } 130 | 131 | plt_offset *plt_get_offsets(plt_ctx ctx, plt_lib lib, const char *name, size_t *n) 132 | { 133 | plt_fn **off = plt_get_offset(lib, name); 134 | if (off) { 135 | plt_offset *ot = mmk_malloc(sizeof (plt_offset)); 136 | *n = 1; 137 | *ot = (plt_offset) { .offset = off }; 138 | return ot; 139 | } 140 | return NULL; 141 | } 142 | 143 | static void plt_set_offset(plt_fn **offset, plt_fn *newval) 144 | { 145 | DWORD old; 146 | VirtualProtect(offset, sizeof (void*), PAGE_EXECUTE_READWRITE, &old); 147 | *offset = newval; 148 | VirtualProtect(offset, sizeof (void*), old, &old); 149 | } 150 | 151 | void plt_set_offsets(plt_offset *offset, size_t nb_off, plt_fn *newval) 152 | { 153 | for (size_t i = 0; i < nb_off; ++i) { 154 | if (!offset[i].oldval) 155 | offset[i].oldval = *offset[i].offset; 156 | plt_set_offset(offset[i].offset, newval); 157 | } 158 | } 159 | 160 | void plt_reset_offsets(plt_offset *offset, size_t nb_off) 161 | { 162 | for (size_t i = 0; i < nb_off; ++i) { 163 | plt_set_offset(offset[i].offset, offset[i].oldval); 164 | } 165 | } 166 | 167 | plt_fn *plt_get_real_fn(plt_ctx ctx, const char *name) 168 | { 169 | (void) ctx; 170 | HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, 171 | GetCurrentProcessId()); 172 | mmk_assert(snap != INVALID_HANDLE_VALUE); 173 | 174 | MODULEENTRY32 mod = { .dwSize = sizeof(MODULEENTRY32) }; 175 | for (BOOL more = Module32First(snap, &mod); more; 176 | more = Module32Next(snap, &mod)) 177 | { 178 | FARPROC fn = GetProcAddress(mod.hModule, name); 179 | if (fn != NULL) 180 | return (plt_fn *) fn; 181 | } 182 | mmk_assert(GetLastError() == ERROR_NO_MORE_FILES); 183 | return NULL; 184 | } 185 | -------------------------------------------------------------------------------- /src/plt-pe.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright © 2016 Franklin "Snaipe" Mathieu 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | #ifndef PLT_PE_H_ 25 | # define PLT_PE_H_ 26 | 27 | # include 28 | 29 | typedef void *plt_ctx; 30 | typedef HANDLE plt_lib; 31 | typedef void (plt_fn)(void); 32 | 33 | #endif /* !PLT_PE_H_ */ 34 | -------------------------------------------------------------------------------- /src/plt.c: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright © 2016 Franklin "Snaipe" Mathieu 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | #include "plt.h" 25 | #include "vitals.h" 26 | 27 | enum plt_selector plt_get_selector(const char *target, const char **val) 28 | { 29 | enum plt_selector sel = PLT_SEL_NONE; 30 | if (mmk_strneq(target, "lib:", 4)) 31 | sel = PLT_SEL_LIB; 32 | else if (mmk_strneq(target, "file:", 5)) 33 | sel = PLT_SEL_FILE; 34 | else if (mmk_strneq(target, "sym:", 4)) 35 | sel = PLT_SEL_SYM; 36 | else if (mmk_streq(target, "self")) 37 | target = ""; 38 | else { 39 | 40 | char *end_sel = mmk_strchr(target, ':'); 41 | if (end_sel) { 42 | size_t len = (size_t) (end_sel - target); 43 | mmk_panic("mimick: unknown '%.*s' selector.\n", (int) len, target); 44 | } else { 45 | mmk_panic("mimick: unknown target kind '%s'.\n", target); 46 | } 47 | } 48 | 49 | *val = sel == PLT_SEL_NONE ? target : mmk_strchr(target, ':') + 1; 50 | return sel; 51 | } 52 | -------------------------------------------------------------------------------- /src/plt.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright © 2016 Franklin "Snaipe" Mathieu 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | #ifndef PLT_H_ 25 | # define PLT_H_ 26 | 27 | # include 28 | 29 | # include "config.h" 30 | 31 | # if defined MMK_EXE_FMT_ELF 32 | # include "plt-elf.h" 33 | # elif defined MMK_EXE_FMT_PE 34 | # include "plt-pe.h" 35 | # elif defined MMK_EXE_FMT_MACH_O 36 | # include "plt-mach-o.h" 37 | # endif 38 | 39 | typedef struct plt_offset { 40 | plt_fn **offset; 41 | plt_fn *oldval; 42 | } plt_offset; 43 | 44 | enum plt_selector { 45 | PLT_SEL_NONE, PLT_SEL_LIB, PLT_SEL_FILE, PLT_SEL_SYM 46 | }; 47 | 48 | plt_ctx plt_init_ctx(void); 49 | plt_lib plt_get_lib(plt_ctx ctx, const char *name); 50 | plt_offset *plt_get_offsets(plt_ctx ctx, plt_lib lib, const char *name, size_t *n); 51 | void plt_set_offsets(plt_offset *offset, size_t nb_off, plt_fn *newval); 52 | void plt_reset_offsets(plt_offset *offset, size_t nb_off); 53 | plt_fn *plt_get_real_fn(plt_ctx ctx, const char *name); 54 | enum plt_selector plt_get_selector(const char *target, const char **val); 55 | 56 | #endif /* !PLT_H_ */ 57 | -------------------------------------------------------------------------------- /src/stub.c: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright © 2016 Franklin "Snaipe" Mathieu 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | #include "mimick.h" 26 | #include "mimick/assert.h" 27 | 28 | #include "core.h" 29 | #include "stub.h" 30 | #include "threadlocal.h" 31 | #include "trampoline.h" 32 | #include "vitals.h" 33 | 34 | static MMK_THREAD_LOCAL(int) ask_ctx; 35 | static MMK_THREAD_LOCAL(struct mmk_stub *) mmk_ctx_; 36 | 37 | void *mmk_stub_context(struct mmk_stub *stub) 38 | { 39 | if (stub == MMK_STUB_INVALID) 40 | return NULL; 41 | return stub->ctx; 42 | } 43 | 44 | int mmk_stub_create_static(struct mmk_stub *stub, 45 | const char *target, mmk_fn fn, void *ctx) 46 | { 47 | tls_set(int, ask_ctx, 0); 48 | tls_set(struct mmk_stub *, mmk_ctx_, NULL); 49 | 50 | char *name = mmk_malloc(mmk_strlen(target) + 1); 51 | if (!name) 52 | return -ENOMEM; 53 | 54 | mmk_strcpy(name, target); 55 | 56 | char *path = NULL; 57 | char *delim = mmk_strchr(name, '@'); 58 | if (delim != NULL) { 59 | *delim = 0; 60 | path = delim + 1; 61 | } 62 | if (path && mmk_streq(path, "self")) 63 | path = NULL; 64 | 65 | plt_ctx pctx = mmk_plt_ctx(); 66 | plt_lib lib = plt_get_lib(pctx, path); 67 | if (!lib) 68 | return -ENOENT; 69 | 70 | size_t nb_off = 0; 71 | plt_offset *off = plt_get_offsets(pctx, lib, name, &nb_off); 72 | if (!off || !nb_off) 73 | return -ENOENT; 74 | 75 | *stub = (struct mmk_stub) { 76 | .ctx_asked = mmk_ctx_asked, 77 | .ctx_set = mmk_set_ctx, 78 | .ctx_get = mmk_ctx, 79 | .ctx = ctx, 80 | .name = name, 81 | .path = path, 82 | .offsets = off, 83 | .nb_offsets = nb_off, 84 | }; 85 | stub->trampoline = create_trampoline(stub, (plt_fn *) fn); 86 | plt_set_offsets(off, nb_off, stub->trampoline); 87 | 88 | return 0; 89 | } 90 | 91 | struct mmk_stub *mmk_stub_create(const char *target, mmk_fn fn, void *ctx) 92 | { 93 | mmk_init(); 94 | 95 | struct mmk_stub *stub = mmk_malloc(sizeof (struct mmk_stub)); 96 | int rc = mmk_stub_create_static(stub, target, fn, ctx); 97 | if (rc < 0) { 98 | mmk_free (stub); 99 | errno = rc; 100 | stub = MMK_STUB_INVALID; 101 | } 102 | return stub; 103 | } 104 | 105 | void mmk_stub_destroy_static(struct mmk_stub *stub) 106 | { 107 | plt_reset_offsets(stub->offsets, stub->nb_offsets); 108 | mmk_free(stub->offsets); 109 | destroy_trampoline(stub->trampoline); 110 | mmk_free(stub->name); 111 | } 112 | 113 | void mmk_stub_destroy(struct mmk_stub *stub) 114 | { 115 | mmk_stub_destroy_static(stub); 116 | mmk_free(stub); 117 | } 118 | 119 | struct mmk_stub *mmk_ask_ctx(mmk_fn fn) 120 | { 121 | tls_set(int, ask_ctx, 1); 122 | return ((struct mmk_stub *(*)(void)) fn)(); 123 | } 124 | 125 | int mmk_ctx_asked(void) 126 | { 127 | int asked = tls_get(int, ask_ctx); 128 | tls_set(int, ask_ctx, 0); 129 | return asked; 130 | } 131 | 132 | struct mmk_stub *mmk_ctx(void) 133 | { 134 | return tls_get(struct mmk_stub *, mmk_ctx_); 135 | } 136 | 137 | void mmk_set_ctx(struct mmk_stub *stub) 138 | { 139 | tls_set(struct mmk_stub *, mmk_ctx_, stub); 140 | } 141 | -------------------------------------------------------------------------------- /src/stub.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright © 2016 Franklin "Snaipe" Mathieu 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | #ifndef STUB_H_ 25 | # define STUB_H_ 26 | 27 | # include "plt.h" 28 | 29 | struct mmk_stub { 30 | int (*ctx_asked)(void); 31 | void (*ctx_set)(struct mmk_stub *); 32 | struct mmk_stub *(*ctx_get)(void); 33 | void *ctx; 34 | char *name; 35 | char *path; 36 | plt_offset *offsets; 37 | size_t nb_offsets; 38 | plt_fn *trampoline; 39 | struct mmk_stub *next; 40 | }; 41 | 42 | struct mmk_stub *mmk_ask_ctx(mmk_fn fn); 43 | int mmk_ctx_asked(void); 44 | void mmk_set_ctx(mmk_stub stub); 45 | 46 | #endif /* !STUB_H_ */ 47 | -------------------------------------------------------------------------------- /src/threadlocal.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright © 2016 Franklin "Snaipe" Mathieu 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | #ifndef THREADLOCAL_H_ 25 | # define THREADLOCAL_H_ 26 | 27 | # if __STDC_VERSION__ >= 201112L && !defined __STDC_NO_THREADS__ 28 | # define MMK_THREAD_LOCAL(Type) _Thread_Local Type 29 | # elif defined _MSC_VER 30 | # define MMK_THREAD_LOCAL(Type) __declspec(thread) Type 31 | # elif defined __GNUC__ && defined _WIN32 32 | # define MMK_THREAD_LOCAL(Type) DWORD 33 | # elif defined __GNUC__ 34 | # define MMK_THREAD_LOCAL(Type) __thread Type 35 | # endif 36 | 37 | # if defined __GNUC__ && defined _WIN32 38 | # include 39 | # include "mimick/assert.h" 40 | 41 | # define tls_set(Type, Var, Val) __extension__ ({ \ 42 | __typeof__(Type) *v__ = NULL; \ 43 | if ((Var) == 0) { \ 44 | (Var) = TlsAlloc(); \ 45 | v__ = mmk_malloc(sizeof (*v__)); \ 46 | TlsSetValue((Var), v__); \ 47 | } else { \ 48 | v__ = TlsGetValue(Var); \ 49 | } \ 50 | *v__ = (Val); \ 51 | }) 52 | # define tls_get(Type, Var) __extension__ ({ \ 53 | __typeof__(Type)* v__ = TlsGetValue(Var); \ 54 | mmk_assert(v__ != NULL); \ 55 | *v__; \ 56 | }) 57 | 58 | # define tls_op(Type, Var, Op, Val) __extension__ ({ \ 59 | __typeof__(Type) *v__ = NULL; \ 60 | if ((Var) == 0) { \ 61 | (Var) = TlsAlloc(); \ 62 | v__ = mmk_malloc(sizeof (*v__)); \ 63 | TlsSetValue((Var), v__); \ 64 | *v__ = (__typeof__(Type)) { 0 }; \ 65 | } else { \ 66 | v__ = TlsGetValue(Var); \ 67 | } \ 68 | *v__ = *v__ Op (Val); \ 69 | }) 70 | # else 71 | # define tls_set(Type, Var, Val) ((Var) = (Val)) 72 | # define tls_get(Type, Var) (Var) 73 | # define tls_op(Type, Var, Op, Val) ((Var) = (Var) Op (Val)) 74 | # endif 75 | 76 | #endif /* !THREADLOCAL_H_ */ 77 | -------------------------------------------------------------------------------- /src/trampoline.c: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright © 2016 Franklin "Snaipe" Mathieu 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | #include 25 | 26 | #include "mimick/assert.h" 27 | 28 | #include "trampoline.h" 29 | #include "vitals.h" 30 | 31 | #define PAGE_SIZE 0x1000 32 | 33 | extern void mmk_trampoline(); 34 | extern void mmk_trampoline_end(); 35 | 36 | #if defined HAVE_MMAP 37 | # include 38 | # include 39 | 40 | # ifndef HAVE_MMAP_MAP_ANONYMOUS 41 | # include 42 | # endif 43 | 44 | # if defined __APPLE__ 45 | # include // LLVM __clear_cache seems not working on Mac ARM64 46 | # elif defined __clang__ 47 | void __clear_cache(void *, void *); 48 | # endif 49 | 50 | plt_fn *create_trampoline(void *ctx, plt_fn *routine) 51 | { 52 | uintptr_t trampoline_sz = (uintptr_t) mmk_trampoline_end 53 | - (uintptr_t) mmk_trampoline; 54 | 55 | mmk_assert(trampoline_sz < PAGE_SIZE); 56 | 57 | # if defined HAVE_MMAP_MAP_ANONYMOUS 58 | # if !defined MAP_JIT 59 | # define MAP_JIT 0 60 | # endif 61 | void **map = mmap(NULL, PAGE_SIZE, 62 | PROT_READ | PROT_WRITE, 63 | MAP_PRIVATE | MAP_ANONYMOUS | MAP_JIT, 64 | -1, 0); 65 | # elif defined HAVE_MMAP_MAP_ANON 66 | void **map = mmap(NULL, PAGE_SIZE, 67 | PROT_READ | PROT_WRITE, 68 | MAP_PRIVATE | MAP_ANON, 69 | -1, 0); 70 | # else 71 | int fd = open("/dev/zero", O_RDWR); 72 | mmk_assert(fd != -1); 73 | 74 | void **map = mmap(NULL, PAGE_SIZE, 75 | PROT_READ | PROT_WRITE | PROT_EXEC, 76 | MAP_PRIVATE, 77 | fd, 0); 78 | 79 | mmk_assert(close(fd) != -1); 80 | # endif 81 | 82 | mmk_assert(map != MAP_FAILED); 83 | 84 | *map = ctx; 85 | *(map + 1) = (void *) routine; 86 | memcpy(map + 2, mmk_trampoline, trampoline_sz); 87 | mmk_assert(!mmk_mprotect(map, PAGE_SIZE, PROT_READ | PROT_EXEC)); 88 | # if defined __APPLE__ 89 | sys_icache_invalidate(map, PAGE_SIZE); 90 | # elif defined __clang__ // Check for Clang first, it may set __GNUC__ too. 91 | __clear_cache(map, map + PAGE_SIZE); 92 | # elif defined __GNUC__ 93 | __builtin___clear_cache((char *)map, (char *)(map + PAGE_SIZE)); 94 | # endif 95 | return (plt_fn *) (map + 2); 96 | } 97 | 98 | void destroy_trampoline(plt_fn *trampoline) 99 | { 100 | munmap((void **) trampoline - 2, PAGE_SIZE); 101 | } 102 | #elif defined _WIN32 103 | # include 104 | 105 | plt_fn *create_trampoline(void *ctx, plt_fn *routine) 106 | { 107 | uintptr_t trampoline_sz = (uintptr_t) mmk_trampoline_end 108 | - (uintptr_t) mmk_trampoline; 109 | 110 | mmk_assert(trampoline_sz < PAGE_SIZE); 111 | void **map = VirtualAlloc(NULL, PAGE_SIZE, 112 | MEM_COMMIT, PAGE_EXECUTE_READWRITE); 113 | 114 | *map = ctx; 115 | *(map + 1) = (void *) routine; 116 | memcpy(map + 2, mmk_trampoline, trampoline_sz); 117 | 118 | DWORD old; 119 | VirtualProtect(map, PAGE_SIZE, PAGE_EXECUTE_READ, &old); 120 | return (plt_fn *) (map + 2); 121 | } 122 | 123 | void destroy_trampoline(plt_fn *trampoline) 124 | { 125 | VirtualFree((void **) trampoline - 2, 0, MEM_RELEASE); 126 | } 127 | #else 128 | # error Unsupported platform 129 | #endif 130 | -------------------------------------------------------------------------------- /src/trampoline.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright © 2016 Franklin "Snaipe" Mathieu 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | #ifndef TRAMPOLINE_H_ 25 | # define TRAMPOLINE_H_ 26 | 27 | # include "plt.h" 28 | 29 | plt_fn *create_trampoline(void *ctx, plt_fn *routine); 30 | void destroy_trampoline(plt_fn *trampoline); 31 | 32 | #endif /* !TRAMPOLINE_H_ */ 33 | -------------------------------------------------------------------------------- /src/verify.c: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright © 2016 Franklin "Snaipe" Mathieu 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | #include "mimick.h" 25 | #include "mimick/assert.h" 26 | #include "mimick/verify.h" 27 | 28 | #include "mock.h" 29 | #include "threadlocal.h" 30 | #include "vitals.h" 31 | 32 | static MMK_THREAD_LOCAL(size_t) times; 33 | 34 | void mmk_verify_set_times(size_t t) 35 | { 36 | tls_set(size_t, times, t); 37 | } 38 | 39 | int mmk_verify_times(struct mmk_verify_params *params) 40 | { 41 | size_t t = tls_get(size_t, times); 42 | if (params->never) 43 | return t == 0; 44 | if (params->at_least || params->at_most) { 45 | size_t most = params->at_most ? params->at_most : t; 46 | return t >= params->at_least && t <= most; 47 | } 48 | if (params->matching) 49 | return params->matching(t); 50 | if (params->times > 0) 51 | return params->times == t; 52 | return 1; 53 | } 54 | 55 | static int find_and_inc_call_matching(struct mmk_mock_ctx *mock, 56 | void *params, size_t size) 57 | { 58 | // skip .times field 59 | params = (void*) ((char *) params + sizeof (size_t)); 60 | size -= sizeof (size_t); 61 | 62 | for (void *p = mmk_mock_params_begin(mock); p; 63 | p = mmk_mock_params_next(mock, p)) 64 | { 65 | // compare parameters w/o .times 66 | int res = mmk_memcmp((char *) p + sizeof (size_t), params, size); 67 | if (!res) { 68 | size_t *times = p; 69 | ++*times; 70 | return 1; 71 | } 72 | } 73 | return 0; 74 | } 75 | 76 | void mmk_verify_register_call(void *params, size_t size) 77 | { 78 | struct mmk_mock_ctx *mock = mmk_stub_context(mmk_ctx()); 79 | if (!mock->call_data) { 80 | mock->call_data = mmk_malloc(4096); 81 | mmk_assert(mock->call_data); 82 | mock->call_data_size = 4096; 83 | } 84 | 85 | if (find_and_inc_call_matching(mock, params, size)) 86 | return; 87 | 88 | if (mock->call_data_top + size + sizeof (size_t) >= mock->call_data_size) { 89 | while (mock->call_data_top + size + sizeof (size_t) >= mock->call_data_size) { 90 | mock->call_data_size += 4096; 91 | } 92 | mock->call_data = mmk_realloc(mock->call_data, mock->call_data_size); 93 | mmk_assert(mock->call_data); 94 | } 95 | 96 | mmk_memcpy(mock->call_data + mock->call_data_top, &size, sizeof (size_t)); 97 | size_t *times = mmk_memcpy(mock->call_data + mock->call_data_top 98 | + sizeof (size_t), params, size); 99 | *times = 1; 100 | 101 | mock->call_data_top += size + sizeof (size_t); 102 | } 103 | -------------------------------------------------------------------------------- /src/vitals.c: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright © 2016 Franklin "Snaipe" Mathieu 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | #include 25 | 26 | #include "mimick/assert.h" 27 | #include "mimick/preprocess.h" 28 | 29 | #include "vitals.h" 30 | 31 | int mmk_memcmp(const void *s1, const void *s2, size_t n) 32 | { 33 | for (const char *s1_ = s1, *s2_ = s2; n; ++s1_, ++s2_, --n) 34 | if (*s1_ != *s2_) 35 | return *s1_ < *s2_ ? -1 : 1; 36 | return 0; 37 | } 38 | 39 | void *mmk_memcpy(void *dst, const void *src, size_t n) 40 | { 41 | const char *src_ = src; 42 | for (char *dst_ = dst; n; ++dst_, ++src_, --n) 43 | *dst_ = *src_; 44 | return dst; 45 | } 46 | 47 | int mmk_strneq(const char *src, const char *ref, size_t n) 48 | { 49 | for (; *src && *ref && n; ++src, ++ref, --n) { 50 | if (*src != *ref) 51 | return 0; 52 | } 53 | return n == 0 || !*ref; 54 | } 55 | 56 | int mmk_streq(const char *src, const char *ref) 57 | { 58 | for (; *src && *ref; ++src, ++ref) { 59 | if (*src != *ref) 60 | return 0; 61 | } 62 | return *ref == *src; 63 | } 64 | 65 | char *mmk_strchr(const char *buf, int c) 66 | { 67 | for (; *buf; ++buf) 68 | if (*buf == c) 69 | return (char *) buf; 70 | return NULL; 71 | } 72 | 73 | char *mmk_strcpy(char *dst, const char *src) 74 | { 75 | for (; *src; ++dst, ++src) 76 | *dst = *src; 77 | *dst = '\0'; 78 | return dst; 79 | } 80 | 81 | char *mmk_strncpy(char *dst, const char *src, size_t n) 82 | { 83 | for (; *src && n; ++dst, ++src, --n) 84 | *dst = *src; 85 | *dst = '\0'; 86 | return dst; 87 | } 88 | 89 | size_t mmk_strlen(const char *s) 90 | { 91 | size_t len = 0; 92 | for (; *s; ++len, ++s); 93 | return len; 94 | } 95 | 96 | int mmk_isspace(int c) 97 | { 98 | return c == ' ' || c == '\t' || c == '\r' || c == '\n'; 99 | } 100 | 101 | int (*mmk_mprotect_)(void *, size_t, int); 102 | int mmk_mprotect(void *addr, size_t len, int prot) 103 | { 104 | return mmk_mprotect_(addr, len, prot); 105 | } 106 | 107 | void *(*mmk_malloc_)(size_t); 108 | void *mmk_malloc(size_t size) 109 | { 110 | return mmk_malloc_(size); 111 | } 112 | 113 | void *(*mmk_realloc_)(void *, size_t); 114 | void *mmk_realloc(void *ptr, size_t size) 115 | { 116 | return mmk_realloc_(ptr, size); 117 | } 118 | 119 | void (*mmk_free_)(void *); 120 | void mmk_free(void *ptr) 121 | { 122 | mmk_free_(ptr); 123 | } 124 | 125 | void (*mmk_abort_)(void); 126 | void mmk_abort(void) 127 | { 128 | mmk_abort_(); 129 | } 130 | 131 | void (*mmk_vfprintf_)(FILE *, const char *, va_list); 132 | void mmk_fprintf(FILE *f, const char *str, ...) 133 | { 134 | va_list vl; 135 | va_start(vl, str); 136 | mmk_vfprintf_(f, str, vl); 137 | va_end(vl); 138 | } 139 | 140 | mmk_noreturn void mmk_panic(const char *str, ...) 141 | { 142 | va_list vl; 143 | va_start(vl, str); 144 | mmk_vfprintf_(stderr, str, vl); 145 | va_end(vl); 146 | mmk_abort(); 147 | mmk_unreachable(); 148 | } 149 | 150 | #ifdef HAVE___STDIO_COMMON_VFPRINTF 151 | static int (__cdecl *mmk___stdio_common_vfprintf_)( 152 | unsigned __int64, FILE *, char const *, _locale_t, va_list); 153 | 154 | static int win32_vfprintf_fallback(FILE *f, const char *fmt, va_list vl) 155 | { 156 | return mmk___stdio_common_vfprintf_(_CRT_INTERNAL_LOCAL_PRINTF_OPTIONS, 157 | f, fmt, NULL, vl); 158 | } 159 | #endif 160 | 161 | # define INIT_VITAL_FUNC(Id) do { \ 162 | mmk_ ## Id ## _ = (void *) plt_get_real_fn(ctx, #Id); \ 163 | if (!mmk_ ## Id ## _) \ 164 | mmk_panic("mimick: Initialization error: could not find " \ 165 | "definition for vital function '" #Id "'.\n"); \ 166 | } while (0) 167 | 168 | void mmk_init_vital_functions(plt_ctx ctx) 169 | { 170 | mmk_vfprintf_ = (void *) plt_get_real_fn(ctx, "vfprintf"); 171 | mmk_abort_ = (void *) plt_get_real_fn(ctx, "abort"); 172 | 173 | #ifdef HAVE___STDIO_COMMON_VFPRINTF 174 | /* Windows doesn't always dynlink to msvcstr.dll (when the universal CRT 175 | is used), so we don't have the definition for vfprintf since ucrt 176 | may define it as an inline function that calls 177 | __stdio_common_vfprintf. */ 178 | if (!mmk_vfprintf_) { 179 | mmk___stdio_common_vfprintf_ = (void *) 180 | plt_get_real_fn(ctx, "__stdio_common_vfprintf"); 181 | if (mmk___stdio_common_vfprintf_) 182 | mmk_vfprintf_ = win32_vfprintf_fallback; 183 | } 184 | #endif 185 | 186 | /* Don't use mmk_panic yet, since it depends on both mmk_abort and 187 | mmk_vfprintf. */ 188 | if (!mmk_abort_ || !mmk_vfprintf_) { 189 | fprintf(stderr, "mimick: Initialization error: could not find " 190 | "definitions for vital function(s): %s %s\n", 191 | mmk_abort_ ? "" : "'abort'", 192 | mmk_vfprintf_ ? "" : "'vfprintf'"); 193 | abort(); 194 | } 195 | 196 | INIT_VITAL_FUNC(malloc); 197 | INIT_VITAL_FUNC(realloc); 198 | INIT_VITAL_FUNC(free); 199 | #if defined(MMK_EXE_FMT_ELF) || defined(MMK_EXE_FMT_MACH_O) 200 | INIT_VITAL_FUNC(mprotect); 201 | #endif 202 | } 203 | -------------------------------------------------------------------------------- /src/vitals.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright © 2016 Franklin "Snaipe" Mathieu 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | #ifndef STRING_H_ 25 | # define STRING_H_ 26 | 27 | # include 28 | # include "mimick/unmocked.h" 29 | # include "common.h" 30 | # include "plt.h" 31 | 32 | /* string functions that are notoriously replaced by sse2 variants are 33 | * reimplemented. */ 34 | int mmk_memcmp(const void *s1, const void *s2, size_t n); 35 | void *mmk_memcpy(void *dst, const void *src, size_t n); 36 | char *mmk_strchr(const char *s, int c); 37 | int mmk_strneq(const char *src, const char *ref, size_t n); 38 | int mmk_streq(const char *src, const char *ref); 39 | char *mmk_strcpy(char *dst, const char *src); 40 | char *mmk_strncpy(char *dst, const char *src, size_t n); 41 | size_t mmk_strlen(const char *s); 42 | int mmk_isspace(int c); 43 | int mmk_mprotect(void *addr, size_t len, int prot); 44 | 45 | mmk_noreturn void mmk_panic(const char *, ...); 46 | 47 | void mmk_init_vital_functions(plt_ctx ctx); 48 | 49 | #endif /* !STRING_H_ */ 50 | -------------------------------------------------------------------------------- /src/when.c: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright © 2016 Franklin "Snaipe" Mathieu 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | #include "mimick.h" 25 | #include "mimick/matcher.h" 26 | #include "vitals.h" 27 | #include "threadlocal.h" 28 | #include "mock.h" 29 | 30 | static MMK_THREAD_LOCAL(struct mmk_result *) cur_result; 31 | 32 | void mmk_when_init(struct mmk_result *res) 33 | { 34 | tls_set(struct mmk_result *, cur_result, res); 35 | } 36 | 37 | struct mmk_result *mmk_when_get_result(void) 38 | { 39 | return tls_get(struct mmk_result *, cur_result); 40 | } 41 | 42 | void mmk_when_impl(struct mmk_mock_ctx *mock, void *data) 43 | { 44 | struct mmk_params *params = data; 45 | params->matcher_ctx = mmk_matcher_ctx(); 46 | params->next = mock->params; 47 | mock->params = params; 48 | } 49 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | if (NOT MSVC) 2 | set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pedantic") 3 | endif () 4 | 5 | add_library (foo SHARED libfoo.c) 6 | 7 | add_mimick_test (mmk_test test.c) 8 | -------------------------------------------------------------------------------- /test/libfoo.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "libfoo.h" 3 | 4 | void fn_vv(void) 5 | { 6 | abort(); 7 | } 8 | 9 | void fn_vi(int i) 10 | { 11 | (void) i; 12 | abort(); 13 | } 14 | 15 | int fn_iv(void) 16 | { 17 | abort(); 18 | return 0; 19 | } 20 | 21 | int fn_ii(int i) 22 | { 23 | abort(); 24 | return i; 25 | } 26 | 27 | void fn_vli(long l, int i) 28 | { 29 | (void) l; 30 | (void) i; 31 | abort(); 32 | } 33 | 34 | int fn_ili(long l, int i) 35 | { 36 | (void) l; 37 | abort(); 38 | return i; 39 | } 40 | 41 | void fn_vi_va(int i, ...) 42 | { 43 | (void) i; 44 | abort(); 45 | } 46 | 47 | int fn_ii_va(int i, ...) 48 | { 49 | (void) i; 50 | abort(); 51 | } 52 | -------------------------------------------------------------------------------- /test/libfoo.h: -------------------------------------------------------------------------------- 1 | #ifndef LIBFOO_H_ 2 | # define LIBFOO_H_ 3 | 4 | # if defined _WIN32 || defined __CYGWIN__ 5 | # ifdef foo_EXPORTS 6 | # ifdef __GNUC__ 7 | # define FOO_API __attribute__((dllexport)) 8 | # else 9 | # define FOO_API __declspec(dllexport) 10 | # endif 11 | # else 12 | # ifdef __GNUC__ 13 | # define FOO_API __attribute__((dllimport)) 14 | # else 15 | # define FOO_API __declspec(dllimport) 16 | # endif 17 | # endif 18 | # define FOO_LOCAL 19 | # else 20 | # if __GNUC__ >= 4 21 | # define FOO_API __attribute__((visibility("default"))) 22 | # define FOO_LOCAL __attribute__((visibility("hidden"))) 23 | # else 24 | # define FOO_API 25 | # define FOO_LOCAL 26 | # endif 27 | # endif 28 | 29 | FOO_API void fn_vv(void); 30 | FOO_API void fn_vi(int i); 31 | FOO_API int fn_iv(void); 32 | FOO_API int fn_ii(int i); 33 | FOO_API void fn_vli(long l, int i); 34 | FOO_API int fn_ili(long l, int i); 35 | FOO_API void fn_vi_va(int i, ...); 36 | FOO_API int fn_ii_va(int i, ...); 37 | 38 | #endif /* !LIBFOO_H_ */ 39 | -------------------------------------------------------------------------------- /test/test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "libfoo.h" 7 | 8 | mmk_mock_define (fn_ii_mock, int, int); 9 | mmk_mock_define (fn_vv_mock, void); 10 | 11 | static int MMK_MANGLE(fn_ii_va_mock, serialize_va)(int i, va_list vl, 12 | size_t *sz_out, struct mmk_va_param ***va_out) 13 | { 14 | if (i <= 0) 15 | return 0; 16 | 17 | struct mmk_va_param **va_data = mmk_malloc(i * sizeof (void *)); 18 | 19 | // Expect all va parameters to be int 20 | for (int j = 0; j < i; ++j) { 21 | mmk_make_va_param(va_data[j], vl, int); 22 | } 23 | 24 | *sz_out = i; 25 | *va_out = va_data; 26 | 27 | return 1; 28 | } 29 | 30 | mmk_mock_define (fn_ii_va_mock, int, int, mmk_va_args); 31 | 32 | static int valid; 33 | 34 | void set_valid(void) { 35 | valid = 1; 36 | } 37 | 38 | static int int_eq_called; 39 | static int expected_int; 40 | 41 | int int_eq(int val) { 42 | int_eq_called = 1; 43 | return val == expected_int; 44 | } 45 | 46 | #define check_called_exact(Expr, Times) do { \ 47 | size_t times = (Times); \ 48 | if (times == 0) { \ 49 | mmk_assert(mmk_verify(Expr, .never = 1)); \ 50 | } else { \ 51 | mmk_assert(mmk_verify(Expr, .times = times)); \ 52 | mmk_assert(mmk_verify(Expr, .at_least = times)); \ 53 | mmk_assert(mmk_verify(Expr, .at_most = times)); \ 54 | mmk_assert(mmk_verify(Expr, .at_least = times, .at_most = times)); \ 55 | mmk_assert(!mmk_verify(Expr, .never = 1)); \ 56 | } \ 57 | if (times > 1) { \ 58 | mmk_assert(!mmk_verify(Expr, .times = times - 1)); \ 59 | mmk_assert(mmk_verify(Expr, .at_least = times - 1)); \ 60 | mmk_assert(!mmk_verify(Expr, .at_most = times - 1)); \ 61 | } \ 62 | if (times < (size_t)-1) { \ 63 | mmk_assert(!mmk_verify(Expr, .times = times + 1)); \ 64 | mmk_assert(mmk_verify(Expr, .at_most = times + 1)); \ 65 | mmk_assert(!mmk_verify(Expr, .at_least = times + 1)); \ 66 | } \ 67 | } while (0) 68 | 69 | int main(void) 70 | { 71 | mmk_mock("fn_vv", fn_vv_mock); 72 | mmk_when(fn_vv(), .then_call = (mmk_fn) set_valid); 73 | 74 | check_called_exact(fn_vv(), 0); 75 | fn_vv(); 76 | mmk_assert(valid); 77 | check_called_exact(fn_vv(), 1); 78 | mmk_reset(fn_vv); 79 | 80 | int err = 0xfefefefe; 81 | mmk_mock("fn_ii", fn_ii_mock); 82 | mmk_when(fn_ii(1), .then_return = mmk_val(int, 1), .then_errno = err); 83 | mmk_when(fn_ii(mmk_eq(int, 2)), .then_return = mmk_val(int, 2), .then_errno = err + 1); 84 | mmk_when(fn_ii(mmk_ge(int, 3)), .then_return = mmk_val(int, 3), .then_errno = err + 2); 85 | mmk_when(fn_ii(mmk_gt(int, 4)), .then_return = mmk_val(int, 4), .then_errno = err + 3); 86 | mmk_when(fn_ii(mmk_le(int, -2)), .then_return = mmk_val(int, 5), .then_errno = err + 4); 87 | mmk_when(fn_ii(mmk_lt(int, -3)), .then_return = mmk_val(int, 6), .then_errno = err + 5); 88 | mmk_when(fn_ii(mmk_that(int, int_eq)), .then_return = mmk_val(int, 7), .then_errno = err + 6); 89 | 90 | mmk_assert(fn_ii(1) == 1 && errno == err); 91 | mmk_assert(fn_ii(2) == 2 && errno == err + 1); 92 | mmk_assert(fn_ii(3) == 3 && errno == err + 2); 93 | mmk_assert(fn_ii(4) == 3 && errno == err + 2); 94 | mmk_assert(fn_ii(5) == 4 && errno == err + 3); 95 | mmk_assert(fn_ii(6) == 4 && errno == err + 3); 96 | mmk_assert(fn_ii(INT_MAX) == 4 && errno == err + 3); 97 | mmk_assert(fn_ii(-1) == 0); 98 | mmk_assert(fn_ii(-2) == 5 && errno == err + 4); 99 | mmk_assert(fn_ii(-3) == 5 && errno == err + 4); 100 | mmk_assert(fn_ii(-4) == 6 && errno == err + 5); 101 | mmk_assert(fn_ii(-5) == 6 && errno == err + 5); 102 | mmk_assert(fn_ii(INT_MIN) == 6 && errno == err + 5); 103 | 104 | expected_int = 1; 105 | mmk_assert(fn_ii(1) == 7 && errno == err + 6 && int_eq_called); 106 | int_eq_called = 0; 107 | 108 | check_called_exact(fn_ii(mmk_any(int)), 14); 109 | check_called_exact(fn_ii(1), 2); 110 | check_called_exact(fn_ii(2), 1); 111 | check_called_exact(fn_ii(3), 1); 112 | check_called_exact(fn_ii(4), 1); 113 | check_called_exact(fn_ii(5), 1); 114 | check_called_exact(fn_ii(6), 1); 115 | check_called_exact(fn_ii(INT_MAX), 1); 116 | check_called_exact(fn_ii(-1), 1); 117 | check_called_exact(fn_ii(-2), 1); 118 | check_called_exact(fn_ii(-3), 1); 119 | check_called_exact(fn_ii(-4), 1); 120 | check_called_exact(fn_ii(-5), 1); 121 | check_called_exact(fn_ii(INT_MIN), 1); 122 | check_called_exact(fn_ii(12), 0); 123 | mmk_reset(fn_ii); 124 | 125 | 126 | mmk_mock("fn_ii_va", fn_ii_va_mock); 127 | mmk_when(fn_ii_va(1, 42), .then_return = mmk_val(int, 1)); 128 | 129 | mmk_assert(fn_ii_va(1, 42) == 1); 130 | 131 | mmk_reset(fn_ii_va); 132 | 133 | return 0; 134 | } 135 | --------------------------------------------------------------------------------