You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过ctypes获取C共享库中函数的实际内存地址?

获取共享库中get_value函数的实际内存地址

我明白你想拿到get_value函数真正的内存地址,而不是动态链接过程中PLT(过程链接表)的入口地址——这在Linux动态链接环境里确实容易让人困惑,毕竟动态加载的共享库会有一层地址跳转的机制。下面给你几种可行的方案,从简单到进阶:

方法一:直接复用库内部输出的地址

你已经调用了print_pointer_to_get_value(),这个函数在共享库内部打印的&get_value就是函数的实际内存地址——因为共享库内部调用自身函数时,不会走PLT跳转,直接引用的就是函数的真实位置。

你只需要把输出的地址值复制出来,在你的加载程序里转换成函数指针使用就行:

#include <dlfcn.h>
#include <stdio.h>

typedef int (*get_value_t)(void);

int main() {
    void* handle = dlopen("./shared.so", RTLD_NOW);
    if (!handle) {
        fprintf(stderr, "%s\n", dlerror());
        return 1;
    }

    // 调用库函数输出实际地址
    void (*print_ptr)(void) = dlsym(handle, "print_pointer_to_get_value");
    if (print_ptr) print_ptr();

    // 把输出的地址(比如0x7fxxxxxx1234)转换成函数指针
    get_value_t actual_get_value = (get_value_t)0x7fxxxxxx1234;
    printf("实际get_value返回值:%d\n", actual_get_value());

    dlclose(handle);
    return 0;
}

这个方法简单直接,但需要手动复制地址,适合快速测试或者固定环境下使用。

方法二:用dladdr自动解析实际地址

如果你想自动化获取,Linux提供了dladdr()函数,可以从函数的PLT地址反向解析出符号的实际地址。

当你用dlsym()拿到get_value的地址时,这个地址通常是PLT表的入口,而dladdr()能帮你穿透这层跳转,拿到真正的函数地址:

#include <dlfcn.h>
#include <stdio.h>
#include <link.h>

typedef int (*get_value_t)(void);

int main() {
    void* handle = dlopen("./shared.so", RTLD_NOW);
    if (!handle) {
        fprintf(stderr, "%s\n", dlerror());
        return 1;
    }

    // 先拿到PLT地址
    get_value_t plt_addr = dlsym(handle, "get_value");
    if (!plt_addr) {
        fprintf(stderr, "%s\n", dlerror());
        dlclose(handle);
        return 1;
    }

    // 解析实际地址
    Dl_info info;
    if (dladdr(plt_addr, &info)) {
        if (info.dli_sname && !strcmp(info.dli_sname, "get_value")) {
            get_value_t actual_addr = (get_value_t)info.dli_saddr;
            printf("PLT入口地址:%p\n", plt_addr);
            printf("实际get_value地址:%p\n", actual_addr);
            printf("实际get_value返回值:%d\n", actual_addr());
        }
    } else {
        fprintf(stderr, "dladdr解析失败:%s\n", dlerror());
    }

    dlclose(handle);
    return 0;
}

编译时记得链接dl库:gcc your_program.c -ldl

方法三:手动解析ELF符号表(进阶)

如果你想深入底层,也可以直接解析共享库的ELF符号表,通过加载基地址加上符号偏移量得到实际地址。这个方法适合理解动态链接的原理:

#include <dlfcn.h>
#include <stdio.h>
#include <link.h>
#include <elf.h>
#include <string.h>

typedef int (*get_value_t)(void);

static uintptr_t lib_base = 0;

// 遍历已加载的共享库,找到目标库的基地址
int find_target_lib(struct dl_phdr_info *info, size_t size, void *data) {
    const char* target_name = (const char*)data;
    if (strstr(info->dlpi_name, target_name)) {
        lib_base = (uintptr_t)info->dlpi_addr;
        printf("共享库加载基地址:%p\n", (void*)lib_base);
        return 1; // 找到后停止遍历
    }
    return 0;
}

int main() {
    // 查找shared.so的加载基地址
    dl_iterate_phdr(find_target_lib, (void*)"shared.so");
    if (lib_base == 0) {
        fprintf(stderr, "找不到shared.so\n");
        return 1;
    }

    // 解析ELF头部和段表
    Elf64_Ehdr* elf_hdr = (Elf64_Ehdr*)lib_base;
    Elf64_Shdr* section_hdr = (Elf64_Shdr*)(lib_base + elf_hdr->e_shoff);

    const char* str_table = NULL;
    Elf64_Sym* dyn_sym_table = NULL;
    size_t sym_count = 0;

    // 定位动态符号表和字符串表
    for (int i = 0; i < elf_hdr->e_shnum; i++) {
        if (section_hdr[i].sh_type == SHT_DYNSYM) {
            dyn_sym_table = (Elf64_Sym*)(lib_base + section_hdr[i].sh_offset);
            sym_count = section_hdr[i].sh_size / sizeof(Elf64_Sym);
        } else if (section_hdr[i].sh_type == SHT_STRTAB && section_hdr[i].sh_link == elf_hdr->e_shstrndx) {
            str_table = (const char*)(lib_base + section_hdr[i].sh_offset);
        }
    }

    if (!dyn_sym_table || !str_table) {
        fprintf(stderr, "无法找到动态符号表或字符串表\n");
        return 1;
    }

    // 遍历符号表找get_value
    get_value_t actual_addr = NULL;
    for (size_t i = 0; i < sym_count; i++) {
        if ((ELF64_ST_BIND(dyn_sym_table[i].st_info) == STB_GLOBAL) && 
            (ELF64_ST_TYPE(dyn_sym_table[i].st_info) == STT_FUNC) &&
            !strcmp(str_table + dyn_sym_table[i].st_name, "get_value")) {
            actual_addr = (get_value_t)(lib_base + dyn_sym_table[i].st_value);
            printf("从符号表解析的get_value实际地址:%p\n", actual_addr);
            printf("实际get_value返回值:%d\n", actual_addr());
            break;
        }
    }

    if (!actual_addr) {
        fprintf(stderr, "符号表中找不到get_value\n");
    }

    return 0;
}

注意:这个代码是针对64位系统的,如果你用32位系统,需要把所有Elf64前缀换成Elf32。


内容的提问来源于stack exchange,提问作者user6369958

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 08:27:46