getmodulehandle(nullptr)返回当前进程可执行文件(exe)的加载基址,是windows下最直接可靠的方式;它不分配内存、不增加引用计数,仅查询已加载模块信息,线程安全,失败时返回nullptr并需调用getlasterror()检查。

Windows下用GetModuleHandle(nullptr)获取主模块基地址
在Windows平台,GetModuleHandle(nullptr) 返回当前进程的主模块(即exe)加载基址,这是最直接可靠的方式。它不分配内存、不增加引用计数,只查询已加载模块信息。
常见错误是传入"kernel32.dll"或硬编码模块名——这返回的是DLL基址,不是当前exe;更糟的是传NULL(C风格)而非nullptr,在某些编译器配置下可能触发重载歧义。
-
GetModuleHandle(nullptr)返回HMODULE,本质就是void*,可直接转为uintptr_t做算术运算 - 若返回
nullptr,说明调用失败(极罕见,通常因严重系统错误),应检查GetLastError() - 该函数线程安全,可在任意线程中调用,无需额外同步
Linux下读取/proc/self/maps解析text段起始地址
Linux没有等效API,必须解析/proc/self/maps。关键不是找“r-xp”权限行,而是匹配当前可执行文件路径且含[text]标识的那行——因为ASLR启用时,同一二进制可能被映射多次(如带调试符号的版本)。
容易忽略的是:静态链接程序的[text]段起始即为程序基址,但动态链接程序的基址其实是解释器(如ld-linux-x86-64.so)加载地址,而你的main binary是作为共享对象被ld动态加载的,其起始地址在maps里对应的是./a.out那一行的首列值。
Linux AI 服务器 — 将 Linux 服务器转换为本地 AI 推理集群。使用 systemd、NVIDIA CUDA 实现无头 Linux AI,零 GUI 开销。
- 用
std::ifstream逐行读取/proc/self/maps,用std::string::find定位包含当前argv[0]且含"r-xp"和"[text]"的行 - 行首十六进制数(如
55e123400000-)的前半部分即基地址,用std::stoull(line.substr(0, 16), nullptr, 16)解析 - 注意:若程序被
ptrace或调试器附加,maps格式可能变化,需兼容空格分隔字段数波动
跨平台方案:用__builtin_return_address(0)反推(不推荐但可行)
某些场景下无法依赖系统API(如嵌入式裸机环境),可用__builtin_return_address(0)获取当前函数返回地址,再减去该函数在目标文件中的相对偏移——但这要求你预先知道编译器生成的函数布局,实际几乎不可靠。
真正能稳定用的跨平台技巧,是让链接器导出一个特殊符号(如_start或自定义__image_base__),然后在代码里取其地址。GCC/Clang支持-Wl,--def或__attribute__((section(".data"))) extern char __image_base__;,但需配合链接脚本指定该符号值为SECTIONS { . = ALIGN(0x1000); __image_base__ = .; }。
- 纯C++标准库无此能力,必须依赖编译器扩展或链接器特性
-
__builtin_return_address在优化等级-O2以上可能被内联或消除,导致返回地址指向不可预测位置 - 即使成功,该地址是代码段内某点,不是PE/ELF头起始,与“加载基址”语义不完全等价
为什么不能直接用&main或&_start
&main只是main函数入口地址,不是模块基址——在PIE(位置无关可执行文件)下,它离基址可能差几MB;&_start同理,且在不同libc实现中可能不存在或被重命名(musl vs glibc)。更危险的是,某些构建配置会把_start放在.text段中间,前面还有.init段。
典型错误现象:printf("base: %p\n", &main); 输出值每次运行都变(ASLR开启时),但和GetModuleHandle结果相差固定偏移——这个偏移就是main在二进制里的RVA,不是基址本身。
- 验证方式:用
objdump -d ./a.out | grep "<main>"</main>查看main的RVA,再用readelf -h ./a.out看Entry Point,二者相减即为预期偏移 - Windows上
dumpbin /headers输出的“image base”字段才是理论基址,实际加载时可能被重定位 - 所有基于符号地址的推算,都绕不开链接时生成的重定位表,而运行时这些信息已不可见
GetModuleHandle(nullptr),Linux走/proc/self/maps解析,别试图统一抽象——两套逻辑差异太大,强行封装只会掩盖细节,出问题时更难排查。C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










