GitPedia

Cpp stub

Inline hook or detour without trampoline for C++ unit test

From coolxv·Updated June 7, 2026·View on GitHub·

- How to get the original function address (**addr_pri.h**, **addr_any.h**) - How to replace the original function with stub function (**stub.h**) The project is written primarily in C++, distributed under the MIT License license, first published in 2017. Key topics include: arm, cpp, cpp11, dummy, fake.

building and running workflow

中文|English

Principle

  • How to get the original function address (addr_pri.h, addr_any.h)
  • How to replace the original function with stub function (stub.h)

Supported

Description of the unit test

Cannot stub

  • Can't stub the exit function, the compiler has made special optimizations.
  • Can't stub pure virtual functions, pure virtual functions not have the address.
  • Can't stub lambda functions, lambda functions not get the address.(You can try to use addr_any.h api.)
  • Can't stub static functions, static function address is not visible.(You can try to use addr_any.h api.)

Unit test compilation option for linux g++

  • -fno-access-control
  • -fno-inline
  • -Wno-pmf-conversions
  • -Wl,--allow-multiple-definition
  • -no-pie -fno-stack-protector
  • -fprofile-arcs
  • -ftest-coverage

macOS integration

macOS requires every binary that uses cpp-stub to have its __TEXT
segment's maxprot lifted to rwx and be ad-hoc re-signed once, after
link. The library ships helpers that wire this up for you; on Linux and
Windows the same helpers do nothing.

CMake (zero manual steps)

cmake
add_subdirectory(third_party/cpp-stub) add_executable(my_test test.cpp) target_link_libraries(my_test PRIVATE cpp-stub)

The root CMakeLists.txt installs a deferred hook that finds every
executable that links cpp-stub and automatically attaches the
post-build patch step on Apple platforms. For the rare case where an
executable only links cpp-stub transitively through a static library,
call it explicitly:

cmake
cpp_stub_enable(my_test)

Makefile

make
CPP_STUB_DIR := third_party/cpp-stub include $(CPP_STUB_DIR)/mk/cpp-stub.mk my_test: my_test.cpp $(CXX) $(addprefix -I,$(CPP_STUB_INCLUDE)) ... -o $@ $< @$(CPP_STUB_POSTLINK)

$(CPP_STUB_POSTLINK) expands to the enable-stub command on macOS and
to a no-op (:) on Linux/Windows, so the same rule works cross-platform.

Xcode / Bazel / other

Add a run-script / genrule that invokes the helper once per built
executable:

bash
third_party/cpp-stub/tool/macos_enable_stub.sh "$TARGET_BUILD_DIR/$EXECUTABLE_PATH"

Why this step exists

See issue #49 and the
__APPLE__ branch of src/stub.h for the full story: the
kernel will not let mprotect/mach_vm_protect raise __TEXT above
its Mach-O maxprot, and Apple Silicon further enforces W^X at the
page-table level even when maxprot=rwx. tool/macos_enable_stub.sh
raises maxprot so that cpp-stub can obtain a writable alias of
__TEXT at runtime via mach_vm_remap.

Code coverage statistics for linux g++

lcov -d build/ -z
lcov -d build/ -b ../../src1 --no-external -rc lcov_branch_coverage=1 -t ut -c -o ut_1.info
lcov -d build/ -b ../../src2 --no-external -rc lcov_branch_coverage=1 -t ut -c -o ut_2.info
lcov -a ut_1.info -a ut_2.info -o ut.info
genhtml -o report/ --prefix=`pwd` --branch-coverage --function-coverage ut.info

Code coverage statistics for windows

OpenCppCoverage

OpenCppCoverage.exe --sources MySourcePath* -- YourProgram.exe arg1 arg2

Interface description

stub.h

Stub stub
stub.set(addr, addr_stub)
stub.reset(addr)

addr_pri.h

Declaration:
    ACCESS_PRIVATE_FIELD(ClassName, TypeName, FieldName)
    ACCESS_PRIVATE_FUN(ClassName, TypeName, FunName)
    ACCESS_PRIVATE_STATIC_FIELD(ClassName, TypeName, FieldName)
    ACCESS_PRIVATE_STATIC_FUN(ClassName, TypeName, FunName)

Use:
    access_private_field::ClassNameFieldName(object);
    access_private_static_field::ClassName::ClassNameFieldName();
    call_private_fun::ClassNameFunName(object,parameters...);
    call_private_static_fun::ClassName::ClassNameFunName(parameters...);
    get_private_fun::ClassNameFunName();
    get_private_static_fun::ClassName::ClassNameFunName();

addr_any.h(linux)

AddrAny any //for exe
AddrAny any(libname) //for lib

int get_local_func_addr_symtab(std::string func_name_regex_str, std::map<std::string,void*>& result)
int get_global_func_addr_symtab(std::string func_name_regex_str, std::map<std::string,void*>& result)
int get_weak_func_addr_symtab(std::string func_name_regex_str, std::map<std::string,void*>& result)

int get_global_func_addr_dynsym( std::string func_name_regex_str, std::map<std::string,void*>& result)
int get_weak_func_addr_dynsym(std::string func_name_regex_str, std::map<std::string,void*>& result)

addr_any.h(windows)

AddrAny any //for all
int get_func_addr(std::string func_name, std::map<std::string,void*>& result)

addr_any.h(darwin)

not implement

Contributors

Showing top 6 contributors by commit count.

View all contributors on GitHub →

This article is auto-generated from coolxv/cpp-stub via the GitHub API.Last fetched: 6/24/2026