Draft
[WIP]【PR】: LD_PRELOAD问题定位及测试 #4594
李力_LeonAdd on Aug 29
[WIP]【PR】: LD_PRELOAD问题定位及测试 #4594
Draft
Merged fromLeon0930/runtimedocs/api-hook-preload-docintocann/runtimemaster
Total: 2 files changed+414-0
| @@ -0,0 +1,135 @@ | |||
| 1 | +# API Hook LD_PRELOAD 劫持方案说明 | ||
| 2 | + | ||
| 3 | +## 问题背景 | ||
| 4 | + | ||
| 5 | +### Hook 需求前的状态:LD_PRELOAD 劫持可以生效 | ||
| 6 | + | ||
| 7 | +在做 API Hook 需求之前,`libacl_rt.so` 中 `aclrtGetSocName` 等接口通过 `ACL_RT_CPP` 宏直接转发:`ret name sig { return name##Impl args; }`。这个 `name##Impl` 调用是 `libacl_rt.so` 对 `libruntime.so` 中 `*Impl` 函数的**外部符号引用**(`U` 符号)。 | ||
| 8 | + | ||
| 9 | +由于 `libacl_rt.so` 虽然也编译了 `-Bsymbolic`,但 `*Impl` 在 `libacl_rt.so` 中是未定义(`U`)的,`-Bsymbolic` 只影响本地有定义的符号。因此动态链接器在解析这个 `U` 符号时走全局搜索顺序,`LD_PRELOAD` 库优先命中,劫持可以生效。 | ||
| 10 | + | ||
| 11 | +### Hook 需求引入后:劫持失效 | ||
| 12 | + | ||
| 13 | +API Hook 机制通过 `ACL_HOOK_DEF` 宏定义 `g_hook_*` 全局变量,存储 `*Impl` 函数指针(`originalFunc` 和 `currentFunc`)。`ACL_RT_CPP_HOOKABLE` 转发函数通过 `g_hook_*.currentFunc` 间接调用,实现 Hook 替换。 | ||
| 14 | + | ||
| 15 | +原始实现中,`g_hook_*` 定义在 `dfx.cpp`,编译进 `libruntime.so`。此时 `&name##Impl` 的取地址操作发生在 `libruntime.so` **内部**——而 `*Impl` 函数也在 `libruntime.so` 中定义(`T` 符号),且 `libruntime.so` 编译了 `-Bsymbolic`,导致符号解析绑定到本地实现,`LD_PRELOAD` 劫持失效。 | ||
| 16 | + | ||
| 17 | +## 原因分析 | ||
| 18 | + | ||
| 19 | +### `-Bsymbolic` 机制详解 | ||
| 20 | + | ||
| 21 | +`-Wl,-Bsymbolic` 是链接器选项,作用是在生成的 SO 的 `.dynamic` 段中设置 `DT_SYMBOLIC` 标志。该标志改变动态链接器解析该 SO 内部符号引用的顺序。 | ||
| 22 | + | ||
| 23 | +**默认行为(无 `-Bsymbolic`)**:SO 内部引用一个全局可见符号时,动态链接器按**全局搜索顺序**解析。全局搜索顺序为:可执行文件 → `LD_PRELOAD` 库(按顺序)→ 已加载的 SO(按加载顺序)。第一个提供该符号定义的对象胜出。 | ||
| 24 | + | ||
| 25 | +**`-Bsymbolic` 行为**:动态链接器在解析该 SO 内部的符号引用时,**优先搜索自身的符号表**。如果该 SO 本身有该符号的定义(`T`/`D`/`B` 等),直接绑定到本地定义,**不会继续搜索全局作用域**。只有本地找不到(`U` 符号)时,才回退到全局搜索顺序。 | ||
| 26 | + | ||
| 27 | +**对 `LD_PRELOAD` 的影响**:`LD_PRELOAD` 的劫持能力依赖全局搜索顺序——劫持库的符号定义排在被劫持 SO 之前。但 `-Bsymbolic` 使 SO 内部的符号引用绑定到本地,跳过了全局搜索,`LD_PRELOAD` 库的符号定义被忽略。 | ||
| 28 | + | ||
| 29 | +**对 `U` 符号的影响**:`-Bsymbolic` 只影响 SO **本地有定义**的符号。对于 SO 中未定义(`U`)的符号引用,本地搜索找不到,动态链接器正常走全局搜索顺序,`LD_PRELOAD` 仍然可以劫持。 | ||
| 30 | + | ||
| 31 | +**使用原因**:`-Bsymbolic` 的主要好处是减少符号解析开销(避免运行时全局搜索)和提高符号隔离性(防止同一进程中被其他 SO 的同名符号意外覆盖)。`libruntime.so` 和 `libacl_rt.so` 都使用了此选项。 | ||
| 32 | + | ||
| 33 | +### `-Bsymbolic` 阻止 LD_PRELOAD 劫持 | ||
| 34 | + | ||
| 35 | +`libruntime.so` 编译时使用了 `-Wl,-Bsymbolic`(`runtime.cmake:684`),设置 `DT_SYMBOLIC` 标志。 | ||
| 36 | + | ||
| 37 | +`-Bsymbolic` 的效果:动态链接器在解析该 SO 内部的符号引用时,**优先搜索自身的符号表**。如果本地有定义,直接绑定,不会继续搜索全局作用域(包括 LD_PRELOAD 库)。 | ||
| 38 | + | ||
| 39 | +### 原始方案的符号流向 | ||
| 40 | + | ||
| 41 | +``` | ||
| 42 | +libruntime.so: | ||
| 43 | + - 定义 g_hook_* (ACL_HOOK_DEF 在 dfx.cpp 中展开) | ||
| 44 | + - 定义 *Impl 函数 (aclrtGetSocNameImpl 等,ACL_RT_IMPL_HEADER 声明) | ||
| 45 | + - 编译选项: -Bsymbolic | ||
| 46 | + | ||
| 47 | +libacl_rt.so: | ||
| 48 | + - 引用 g_hook_* (ACL_RT_CPP_HOOKABLE 转发函数引用) | ||
| 49 | + - 引用 *Impl 函数 (通过 g_hook_*.currentFunc 间接调用) | ||
| 50 | +``` | ||
| 51 | + | ||
| 52 | +`g_hook_*` 在 `libruntime.so` 内部静态初始化时,`&name##Impl` 解析过程: | ||
| 53 | + | ||
| 54 | +1. `-Bsymbolic` 生效 → 优先搜索 `libruntime.so` 自身符号表 | ||
| 55 | +2. 找到本地 `aclrtGetSocNameImpl` 定义 → 直接绑定 | ||
| 56 | +3. **不会搜索全局作用域** → LD_PRELOAD 的劫持函数被跳过 | ||
| 57 | + | ||
| 58 | +结果:`g_hook_*.originalFunc` 和 `currentFunc` 始终指向 `libruntime.so` 本地的 `*Impl`,`aclrtGetSocName()` 调用走的是原始实现,劫持无效。 | ||
| 59 | + | ||
| 60 | +### `LD_DEBUG=bindings` 验证 | ||
| 61 | + | ||
| 62 | +``` | ||
| 63 | +binding file libruntime.so [0] to libruntime.so [0]: normal symbol 'aclrtGetSocNameImpl' ← 静态初始化绑定到本地 | ||
| 64 | +binding file ./test_real [0] to ./libhijack_real.so [0]: normal symbol 'aclrtGetSocNameImpl' ← 测试程序的dlsym绑定到劫持库 | ||
| 65 | +``` | ||
| 66 | + | ||
| 67 | +两个绑定地址不同,证实 `-Bsymbolic` 导致 `libruntime.so` 内部的符号解析未命中 LD_PRELOAD。 | ||
| 68 | + | ||
| 69 | +## 修改方案 | ||
| 70 | + | ||
| 71 | +### 核心思路 | ||
| 72 | + | ||
| 73 | +将 `g_hook_*` 定义从 `libruntime.so`(有 `-Bsymbolic` 且定义 `*Impl`)移到 `libacl_rt.so`(有 `-Bsymbolic` 但**不定义** `*Impl`)。 | ||
| 74 | + | ||
| 75 | +`libacl_rt.so` 中 `*Impl` 是 `U`(未定义引用),`-Bsymbolic` 只影响本地有定义的符号,对 `U` 符号无效。符号解析回退到全局作用域,LD_PRELOAD 优先命中。 | ||
| 76 | + | ||
| 77 | +### 修改后的符号流向 | ||
| 78 | + | ||
| 79 | +``` | ||
| 80 | +libacl_rt.so: | ||
| 81 | + - 定义 g_hook_* (ACL_HOOK_DEF 移到 acl_rt.cpp,弱定义 V) | ||
| 82 | + - 引用 *Impl 函数 (*Impl 是 U 符号,-Bsymbolic 不影响) | ||
| 83 | + - 编译选项: -Bsymbolic(对 U 符号无效) | ||
| 84 | + | ||
| 85 | +libruntime.so: | ||
| 86 | + - 弱引用 g_hook_* (ACL_HOOK_DECL 加 __attribute__((weak)),w 符号) | ||
| 87 | + - 定义 *Impl 函数 (不变) | ||
| 88 | + - 编译选项: -Bsymbolic(不影响,g_hook_* 不在此定义) | ||
| 89 | +``` | ||
| 90 | + | ||
| 91 | +`g_hook_*` 在 `libacl_rt.so` 内部静态初始化时,`&name##Impl` 解析过程: | ||
| 92 | + | ||
| 93 | +1. `-Bsymbolic` 生效 → 搜索 `libacl_rt.so` 自身符号表 | ||
| 94 | +2. `*Impl` 是 `U`(未定义)→ **本地找不到** | ||
| 95 | +3. 回退到全局作用域搜索 | ||
| 96 | +4. LD_PRELOAD 库优先 → 命中劫持函数 | ||
| 97 | + | ||
| 98 | +### 弱符号处理 | ||
| 99 | + | ||
| 100 | +`dfx.cpp` 中的 `g_aclrtApiLookup[]` 查找表引用 `&g_hook_*`。`g_hook_*` 定义移到 `libacl_rt.so` 后,`libruntime.so` 中的这些引用变为未定义符号。如果直接用 `extern` 强引用,`libruntime.so` 独立加载(`dlopen`)时会因找不到 `g_hook_*` 而失败。 | ||
| 101 | + | ||
| 102 | +通过将 `ACL_HOOK_DECL` 的 `extern` 声明加 `__attribute__((weak))`,使 `libruntime.so` 中的 `g_hook_*` 引用变为弱引用: | ||
| 103 | + | ||
| 104 | +- **`libacl_rt.so` 在场时**(正常使用场景):弱引用自动解析到 `libacl_rt.so` 中的强定义,hook 功能正常。 | ||
| 105 | +- **`libruntime.so` 独立加载时**:弱引用解析为 NULL,`FindHookEntryByName` 跳过 NULL entry 返回 nullptr,`aclrtApiInjectionSetFunc/GetFunc` 报参数错误。hook 功能不可用,但 `rtGetSocVersion` 等原生接口正常工作。 | ||
| 106 | + | ||
| 107 | +### 具体改动 | ||
| 108 | + | ||
| 109 | +共修改 3 个文件: | ||
| 110 | + | ||
| 111 | +1. **`src/acl/aclrt/acl_rt.cpp`**:新增 `ACL_HOOK_DEF` 宏展开,使 `g_hook_*` 定义编译进 `libacl_rt.so`。 | ||
| 112 | + | ||
| 113 | +2. **`src/acl/aclrt_impl/dfx.cpp`**:删除 `ACL_HOOK_DEF` 宏展开,`g_hook_*` 不再在 `libruntime.so` 中定义。`FindHookEntryByName` 遍历时跳过 `entry == nullptr` 的项(弱符号未解析时)。 | ||
| 114 | + | ||
| 115 | +3. **`src/acl/aclrt_impl/acl_rt_wrapper.h`**: | ||
| 116 | + - `ACL_HOOK_DECL` 加 `__attribute__((weak))`,使 `libruntime.so` 中的 `g_hook_*` 引用为弱引用,支持独立加载。 | ||
| 117 | + - `ACL_HOOK_DEF` 宏恢复使用 `&name##Impl` 直接取地址(之前的 `dlsym` 方案不再需要),并删除不再使用的 `#include <dlfcn.h>`。 | ||
| 118 | + | ||
| 119 | +### 不需要修改的文件 | ||
| 120 | + | ||
| 121 | +| 文件 | 原因 | | ||
| 122 | +|------|------| | ||
| 123 | +| `runtime.cmake` / `tiny.cmake` / `cmodel.cmake` | `dfx.cpp` 仍在源列表中,只是代码减少。`ACL_RT_API_HOOK_ENABLE` 保留无害(`extern` 声明不产生引用) | | ||
| 124 | +| `arch5162.cmake` | arch5162 不启用 `ACL_RT_API_HOOK_ENABLE`,`dfx.cpp` 的 hook 代码被 `#ifdef` 排除,搬走后效果一致 | | ||
| 125 | + | ||
| 126 | +## 验证 | ||
| 127 | + | ||
| 128 | +整包编译后 `nm -D` 确认符号分布: | ||
| 129 | + | ||
| 130 | +``` | ||
| 131 | +libacl_rt.so: 0000000000030458 V g_hook_aclrtGetSocName ← 弱定义在 libacl_rt.so | ||
| 132 | +libruntime.so: w g_hook_aclrtGetSocName ← 弱引用 | ||
| 133 | +``` | ||
| 134 | + | ||
| 135 | +`LD_PRELOAD` 劫持测试通过,`dlopen("libruntime.so")` 独立加载也正常,详见 `docs/api_hook_preload_test.md`。 | ||
| @@ -0,0 +1,279 @@ | |||
| 1 | +# LD_PRELOAD 劫持测试报告 | ||
| 2 | + | ||
| 3 | +## 测试目标 | ||
| 4 | + | ||
| 5 | +验证 `ACL_HOOK_DEF` 中 `g_hook_*` 定义移至 `libacl_rt.so` 后,`LD_PRELOAD` 劫持 `*Impl` 函数是否生效。 | ||
| 6 | + | ||
| 7 | +## 测试环境 | ||
| 8 | + | ||
| 9 | +- 平台:aarch64-linux | ||
| 10 | +- 编译命令:`bash build.sh --pkg -j8`(整包编译) | ||
| 11 | +- 安装路径:`/tmp/opencode/preload_test/real_runtime5/cann` | ||
| 12 | + | ||
| 13 | +## 测试文件 | ||
| 14 | + | ||
| 15 | +### 劫持库 `libhijack_real.so` | ||
| 16 | + | ||
| 17 | +源文件 `libhijack_real.c`: | ||
| 18 | + | ||
| 19 | +```c | ||
| 20 | +#include <stdio.h> | ||
| 21 | +#include <string.h> | ||
| 22 | + | ||
| 23 | +const char *aclrtGetSocNameImpl(void) | ||
| 24 | +{ | ||
| 25 | + printf(" [PRELOAD] aclrtGetSocNameImpl hijacked!\n"); | ||
| 26 | + return "Ascend-HIJACKED-BY-PRELOAD"; | ||
| 27 | +} | ||
| 28 | +``` | ||
| 29 | + | ||
| 30 | +编译命令: | ||
| 31 | + | ||
| 32 | +```bash | ||
| 33 | +gcc -shared -fPIC -o libhijack_real.so libhijack_real.c | ||
| 34 | +``` | ||
| 35 | + | ||
| 36 | +### 测试程序 `test_real` | ||
| 37 | + | ||
| 38 | +源文件 `test_real.c`: | ||
| 39 | + | ||
| 40 | +```c | ||
| 41 | +#include <stdio.h> | ||
| 42 | +#include <dlfcn.h> | ||
| 43 | +#include <string.h> | ||
| 44 | +#include "acl/acl_base_rt.h" | ||
| 45 | + | ||
| 46 | +typedef const char *(*getSocName_t)(void); | ||
| 47 | + | ||
| 48 | +typedef struct { | ||
| 49 | + void *originalFunc; | ||
| 50 | + void *currentFunc; | ||
| 51 | +} apiEntry_t; | ||
| 52 | + | ||
| 53 | +int main(void) | ||
| 54 | +{ | ||
| 55 | + printf("=== 真实Runtime LD_PRELOAD劫持测试 ===\n\n"); | ||
| 56 | + | ||
| 57 | + // 1. 检查 dlsym 能否找到 Impl 函数 | ||
| 58 | + printf("1. dlsym 查找 aclrtGetSocNameImpl:\n"); | ||
| 59 | + dlerror(); | ||
| 60 | + void *sym = dlsym(RTLD_DEFAULT, "aclrtGetSocNameImpl"); | ||
| 61 | + const char *err = dlerror(); | ||
| 62 | + if (err != NULL) { | ||
| 63 | + printf(" dlsym error: %s\n", err); | ||
| 64 | + } else { | ||
| 65 | + printf(" dlsym result = %p\n", sym); | ||
| 66 | + } | ||
| 67 | + | ||
| 68 | + // 2. 查找 g_hook_aclrtGetSocName 结构体 | ||
| 69 | + printf("\n2. dlsym 查找 g_hook_aclrtGetSocName:\n"); | ||
| 70 | + dlerror(); | ||
| 71 | + apiEntry_t *hook = (apiEntry_t *)dlsym(RTLD_DEFAULT, "g_hook_aclrtGetSocName"); | ||
| 72 | + err = dlerror(); | ||
| 73 | + if (err != NULL) { | ||
| 74 | + printf(" dlsym error: %s\n", err); | ||
| 75 | + } else { | ||
| 76 | + printf(" g_hook_aclrtGetSocName = %p\n", (void *)hook); | ||
| 77 | + printf(" originalFunc = %p\n", hook->originalFunc); | ||
| 78 | + printf(" currentFunc = %p\n", hook->currentFunc); | ||
| 79 | + if (hook->originalFunc == sym) { | ||
| 80 | + printf(" -> originalFunc 与 dlsym 结果一致\n"); | ||
| 81 | + } else { | ||
| 82 | + printf(" -> originalFunc 与 dlsym 结果不同!\n"); | ||
| 83 | + } | ||
| 84 | + } | ||
| 85 | + | ||
| 86 | + // 3. 直接调用 aclrtGetSocName(走 libacl_rt.so 的 ACL_RT_CPP_HOOKABLE 转发函数) | ||
| 87 | + printf("\n3. 直接调用 aclrtGetSocName()(走libacl_rt.so转发):\n"); | ||
| 88 | + const char *result = aclrtGetSocName(); | ||
| 89 | + printf(" 返回值: \"%s\"\n", result); | ||
| 90 | + | ||
| 91 | + if (strcmp(result, "Ascend-HIJACKED-BY-PRELOAD") == 0) { | ||
| 92 | + printf(" -> LD_PRELOAD 劫持成功! dlsym方式生效!\n"); | ||
| 93 | + } else { | ||
| 94 | + printf(" -> 未被劫持,返回真实值\n"); | ||
| 95 | + } | ||
| 96 | + | ||
| 97 | + // 4. 对比:直接 dlsym 调用 Impl | ||
| 98 | + printf("\n4. 直接调用 aclrtGetSocNameImpl()(通过dlsym):\n"); | ||
| 99 | + if (sym != NULL) { | ||
| 100 | + getSocName_t implFn = (getSocName_t)sym; | ||
| 101 | + const char *result2 = implFn(); | ||
| 102 | + printf(" 返回值: \"%s\"\n", result2); | ||
| 103 | + if (strcmp(result2, "Ascend-HIJACKED-BY-PRELOAD") == 0) { | ||
| 104 | + printf(" -> dlsym 直接调用也被劫持!\n"); | ||
| 105 | + } else { | ||
| 106 | + printf(" -> dlsym 直接调用未被劫持\n"); | ||
| 107 | + } | ||
| 108 | + } | ||
| 109 | + | ||
| 110 | + printf("\n=== 测试结束 ===\n"); | ||
| 111 | + return 0; | ||
| 112 | +} | ||
| 113 | +``` | ||
| 114 | + | ||
| 115 | +编译命令: | ||
| 116 | + | ||
| 117 | +```bash | ||
| 118 | +RT_LIB=/tmp/opencode/preload_test/real_runtime5/cann/lib64 | ||
| 119 | +gcc -o test_real test_real.c -ldl \ | ||
| 120 | + -I/tmp/opencode/preload_test/real_runtime5/cann/include \ | ||
| 121 | + -L$RT_LIB -lacl_rt \ | ||
| 122 | + -Wl,-rpath,$RT_LIB -Wl,--no-as-needed | ||
| 123 | +``` | ||
| 124 | + | ||
| 125 | +## 测试步骤 | ||
| 126 | + | ||
| 127 | +### 步骤 1:整包编译 | ||
| 128 | + | ||
| 129 | +```bash | ||
| 130 | +bash build.sh --pkg -j8 | ||
| 131 | +``` | ||
| 132 | + | ||
| 133 | +### 步骤 2:安装到临时目录 | ||
| 134 | + | ||
| 135 | +```bash | ||
| 136 | +mkdir -p /tmp/opencode/preload_test/real_runtime5 | ||
| 137 | +bash build_out/cann-npu-runtime_9.2.0_linux-aarch64.run \ | ||
| 138 | + --install-path=/tmp/opencode/preload_test/real_runtime5 --full --quiet | ||
| 139 | +``` | ||
| 140 | + | ||
| 141 | +### 步骤 3:编译劫持库 | ||
| 142 | + | ||
| 143 | +```bash | ||
| 144 | +gcc -shared -fPIC -o libhijack_real.so libhijack_real.c | ||
| 145 | +``` | ||
| 146 | + | ||
| 147 | +### 步骤 4:编译测试程序 | ||
| 148 | + | ||
| 149 | +```bash | ||
| 150 | +RT_LIB=/tmp/opencode/preload_test/real_runtime5/cann/lib64 | ||
| 151 | +gcc -o test_real test_real.c -ldl \ | ||
| 152 | + -I/tmp/opencode/preload_test/real_runtime5/cann/include \ | ||
| 153 | + -L$RT_LIB -lacl_rt \ | ||
| 154 | + -Wl,-rpath,$RT_LIB -Wl,--no-as-needed | ||
| 155 | +``` | ||
| 156 | + | ||
| 157 | +### 步骤 5:检查符号分布 | ||
| 158 | + | ||
| 159 | +```bash | ||
| 160 | +RT_LIB=/tmp/opencode/preload_test/real_runtime5/cann/lib64 | ||
| 161 | +nm -D $RT_LIB/libacl_rt.so | grep g_hook_aclrtGetSocName | ||
| 162 | +nm -D $RT_LIB/libruntime.so | grep g_hook_aclrtGetSocName | ||
| 163 | +``` | ||
| 164 | + | ||
| 165 | +### 步骤 6:不带 LD_PRELOAD 运行 | ||
| 166 | + | ||
| 167 | +```bash | ||
| 168 | +RT_LIB=/tmp/opencode/preload_test/real_runtime5/cann/lib64 | ||
| 169 | +LD_LIBRARY_PATH=$RT_LIB:$LD_LIBRARY_PATH ./test_real | ||
| 170 | +``` | ||
| 171 | + | ||
| 172 | +### 步骤 7:带 LD_PRELOAD 运行 | ||
| 173 | + | ||
| 174 | +```bash | ||
| 175 | +RT_LIB=/tmp/opencode/preload_test/real_runtime5/cann/lib64 | ||
| 176 | +LD_PRELOAD=./libhijack_real.so LD_LIBRARY_PATH=$RT_LIB:$LD_LIBRARY_PATH ./test_real | ||
| 177 | +``` | ||
| 178 | + | ||
| 179 | +## 测试结果 | ||
| 180 | + | ||
| 181 | +### 符号分布 | ||
| 182 | + | ||
| 183 | +``` | ||
| 184 | +libacl_rt.so: 0000000000030458 V g_hook_aclrtGetSocName ← 弱定义(Data 段) | ||
| 185 | +libruntime.so: w g_hook_aclrtGetSocName ← 弱引用 | ||
| 186 | +``` | ||
| 187 | + | ||
| 188 | +### 不带 LD_PRELOAD | ||
| 189 | + | ||
| 190 | +``` | ||
| 191 | +=== 真实Runtime LD_PRELOAD劫持测试 === | ||
| 192 | + | ||
| 193 | +1. dlsym 查找 aclrtGetSocNameImpl: | ||
| 194 | + dlsym result = 0xffff8f36e284 | ||
| 195 | + | ||
| 196 | +2. dlsym 查找 g_hook_aclrtGetSocName: | ||
| 197 | + g_hook_aclrtGetSocName = 0xffff8fa70458 | ||
| 198 | + originalFunc = 0xffff8f36e284 | ||
| 199 | + currentFunc = 0xffff8f36e284 | ||
| 200 | + -> originalFunc 与 dlsym 结果一致 | ||
| 201 | + | ||
| 202 | +3. 直接调用 aclrtGetSocName()(走libacl_rt.so转发): | ||
| 203 | + 返回值: "Ascend910_9362" | ||
| 204 | + -> 未被劫持,返回真实值 | ||
| 205 | + | ||
| 206 | +4. 直接调用 aclrtGetSocNameImpl()(通过dlsym): | ||
| 207 | + 返回值: "Ascend910_9362" | ||
| 208 | + -> dlsym 直接调用未被劫持 | ||
| 209 | + | ||
| 210 | +=== 测试结束 === | ||
| 211 | +``` | ||
| 212 | + | ||
| 213 | +### 带 LD_PRELOAD | ||
| 214 | + | ||
| 215 | +``` | ||
| 216 | +=== 真实Runtime LD_PRELOAD劫持测试 === | ||
| 217 | + | ||
| 218 | +1. dlsym 查找 aclrtGetSocNameImpl: | ||
| 219 | + dlsym result = 0xffff899305e8 | ||
| 220 | + | ||
| 221 | +2. dlsym 查找 g_hook_aclrtGetSocName: | ||
| 222 | + g_hook_aclrtGetSocName = 0xffff89920458 | ||
| 223 | + originalFunc = 0xffff899305e8 | ||
| 224 | + currentFunc = 0xffff899305e8 | ||
| 225 | + -> originalFunc 与 dlsym 结果一致 | ||
| 226 | + | ||
| 227 | +3. 直接调用 aclrtGetSocName()(走libacl_rt.so转发): | ||
| 228 | + [PRELOAD] aclrtGetSocNameImpl hijacked! | ||
| 229 | + 返回值: "Ascend-HIJACKED-BY-PRELOAD" | ||
| 230 | + -> LD_PRELOAD 劫持成功! dlsym方式生效! | ||
| 231 | + | ||
| 232 | +4. 直接调用 aclrtGetSocNameImpl()(通过dlsym): | ||
| 233 | + [PRELOAD] aclrtGetSocNameImpl hijacked! | ||
| 234 | + 返回值: "Ascend-HIJACKED-BY-PRELOAD" | ||
| 235 | + -> dlsym 直接调用也被劫持! | ||
| 236 | + | ||
| 237 | +=== 测试结束 === | ||
| 238 | +``` | ||
| 239 | + | ||
| 240 | +### 结果汇总 | ||
| 241 | + | ||
| 242 | +| 场景 | `g_hook` 定义位置 | `originalFunc` 指向 | `aclrtGetSocName()` 返回值 | 劫持是否生效 | | ||
| 243 | +|------|-------------------|---------------------|---------------------------|--------------| | ||
| 244 | +| 不带 LD_PRELOAD | `libacl_rt.so` | `libruntime.so` 的真实实现 | `"Ascend910_9362"` | N/A | | ||
| 245 | +| 带 LD_PRELOAD | `libacl_rt.so` | `libhijack_real.so` 的劫持实现 | `"Ascend-HIJACKED-BY-PRELOAD"` | **是** | | ||
| 246 | + | ||
| 247 | +### `dlopen("libruntime.so")` 独立加载测试 | ||
| 248 | + | ||
| 249 | +验证 `libruntime.so` 不依赖 `libacl_rt.so` 也能独立加载(弱符号场景): | ||
| 250 | + | ||
| 251 | +```c | ||
| 252 | +void *h = dlopen("libruntime.so", RTLD_NOW); | ||
| 253 | +rtError_t (*fn)(char *, const uint32_t) = dlsym(h, "rtGetSocVersion"); | ||
| 254 | +char ver[256] = {0}; | ||
| 255 | +fn(ver, sizeof(ver)); | ||
| 256 | +``` | ||
| 257 | + | ||
| 258 | +结果: | ||
| 259 | + | ||
| 260 | +``` | ||
| 261 | +rtGetSocVersion() ret=0 ver="Ascend910_9362" | ||
| 262 | +``` | ||
| 263 | + | ||
| 264 | +`libruntime.so` 独立加载成功,`rtGetSocVersion` 正常返回。 | ||
| 265 | + | ||
| 266 | +### 多 API 劫持测试 | ||
| 267 | + | ||
| 268 | +同时劫持 `aclrtGetSocNameImpl` 和 `aclrtGetVersionImpl`: | ||
| 269 | + | ||
| 270 | +| API | 不带 LD_PRELOAD | 带 LD_PRELOAD | | ||
| 271 | +|-----|----------------|--------------| | ||
| 272 | +| `aclrtGetSocName()` | `"Ascend910_9362"` | `"Ascend-HIJACKED-BY-PRELOAD"` | | ||
| 273 | +| `aclrtGetVersion()` | `1.17.0` | `999.999.999` | | ||
| 274 | + | ||
| 275 | +不劫持时返回真实值,劫持时两个 `*Impl` 均被替换。 | ||
| 276 | + | ||
| 277 | +## 结论 | ||
| 278 | + | ||
| 279 | +`g_hook_*` 定义移至 `libacl_rt.so` 后,`LD_PRELOAD` 劫持 `*Impl` 函数成功生效。通过弱符号处理,`libruntime.so` 独立加载也正常工作。测试覆盖单 API 劫持、多 API 劫持、独立加载三个场景,均在真实 Runtime 包上验证通过。 | ||