Cpp stub
Inline hook or detour without trampoline for C++ unit test
- 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.
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
- Supported operating systems:
- Windows
- Linux
- MacOS — both Intel (x86-64) and Apple Silicon (arm64). Because macOS
enforces W^X on__TEXTand capsmprotectat each segment'smaxprot,
every binary that uses cpp-stub needs a tiny post-link adjustment
(lift__TEXT.maxprottorwxand ad-hoc re-sign; see
issue #49).
cpp-stub ships build-system helpers that do this automatically, so
downstream users normally do not have to call anything by hand.
See the macOS integration section below.
- Supported hardware platform:
- x86
- x86-64
- arm32
- arm64
- arm thumb
- riscv32
- riscv64
- loongarch64
- mips64
- ppc64 (Generated by chatgpt and requires verification)
- s390x (Generated by chatgpt and requires verification)
- alpha (Generated by chatgpt and requires verification)
- sparc (Generated by chatgpt and requires verification)
- sw_64 (Generated by chatgpt and requires verification)
- Supported compiler:
- msvc
- gcc
- clang
- Support function type:
- normal function
- variadic function
- template function
- overload function
- lambda
- static function(use addr_any.h)
- inline function(use compiler options)
- constructor function
- destructor function
- member function
- static member function
- virtual function(not pure)
- virtual and overload function
- functor
- private member function(use addr_pri.h)
- private member function(cpp17)
- function in the dynamic library(use dlsym())
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)
cmakeadd_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:
cmakecpp_stub_enable(my_test)
Makefile
makeCPP_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:
bashthird_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.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.
