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

如何通过LLVM或Clang在运行时获取所有全局变量的地址与大小

基于LLVM模块Pass的全局变量运行时追踪实现方案

可行性说明

完全可以通过LLVM模块Pass实现该需求。LLVM模块Pass能够遍历目标程序的所有模块,访问每个全局变量的元信息(地址、类型、大小等),并在编译阶段自动插入运行时代码,完成全局变量边界信息的收集。

核心实现步骤

1. 定义追踪数据结构

先在代码中(或通过Pass自动插入)定义存储全局变量边界的结构体和列表操作逻辑:

#include <stdlib.h>

typedef struct {
    void* start;
    void* end;
} LIST_ITEM;

typedef struct Node {
    LIST_ITEM data;
    struct Node* next;
} ListNode;

ListNode* global_list = NULL;

void add_global_var(void* start, size_t size) {
    ListNode* new_node = (ListNode*)malloc(sizeof(ListNode));
    new_node->data.start = start;
    new_node->data.end = (char*)start + size;
    new_node->next = global_list;
    global_list = new_node;
}

2. 编写LLVM模块Pass

用LLVM C++ API编写模块Pass,遍历所有全局变量,计算其实际占用大小,并生成调用add_global_var的代码,插入到程序初始化阶段(比如main函数开头)。

示例Pass核心代码:

#include "llvm/IR/Module.h"
#include "llvm/Passes/PassPlugin.h"
#include "llvm/Passes/PassManager.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/Type.h"
#include "llvm/IR/DataLayout.h"

using namespace llvm;

struct GlobalVarTrackPass : public ModulePass {
    static char ID;
    GlobalVarTrackPass() : ModulePass(ID) {}

    bool runOnModule(Module &M) override {
        const DataLayout &DL = M.getDataLayout();
        // 查找或声明add_global_var函数
        FunctionCallee addFunc = M.getOrInsertFunction(
            "add_global_var",
            Type::getVoidTy(M.getContext()),
            Type::getInt8PtrTy(M.getContext()),
            Type::getSizeTy(M.getContext())
        );

        Function *MainFunc = M.getFunction("main");
        if (!MainFunc) return false;

        // 在main函数入口插入收集逻辑
        IRBuilder<> Builder(&*MainFunc->getEntryBlock().getFirstInsertionPt());

        // 遍历模块内所有全局变量
        for (GlobalVariable &GV : M.globals()) {
            if (GV.isDeclaration() || !GV.hasInitializer()) continue;

            Type *GVType = GV.getValueType();
            uint64_t Size = DL.getTypeAllocSize(GVType);

            Value *GVAddr = Builder.CreatePointerCast(&GV, Type::getInt8PtrTy(M.getContext()));
            Value *SizeVal = ConstantInt::get(Type::getSizeTy(M.getContext()), Size);

            Builder.CreateCall(addFunc, {GVAddr, SizeVal});
        }

        return true;
    }
};

char GlobalVarTrackPass::ID = 0;

// 注册Pass插件
extern "C" LLVM_ATTRIBUTE_WEAK PassPluginLibraryInfo llvmGetPassPluginInfo() {
    return {
        LLVM_PLUGIN_API_VERSION, "GlobalVarTrackPass", "v0.1",
        [](PassBuilder &PB) {
            PB.registerPipelineParsingCallback(
                [](StringRef Name, ModulePassManager &MPM, ArrayRef<PassBuilder::PipelineElement>) {
                    if (Name == "global-var-track") {
                        MPM.addPass(GlobalVarTrackPass());
                        return true;
                    }
                    return false;
                }
            );
        }
    };
}

3. 编译与应用Pass

  1. 将Pass代码编译成LLVM插件:
clang++ -fPIC -shared GlobalVarTrackPass.cpp -o libGlobalVarTrackPass.so `llvm-config --cxxflags --ldflags --system-libs --libs core passes`
  1. 编译目标程序时加载该Pass:
clang your_program.cc -fpass-plugin=./libGlobalVarTrackPass.so -o your_program

关键细节注意

  • 外部全局变量:Pass仅处理当前模块定义的全局变量,若需追踪动态库中的全局变量,需对动态库也应用该Pass。
  • 字符串字面量:C/C++中的字符串字面量会被识别为全局常量字符数组,Pass会自动捕获,其大小包含末尾的\0。
  • 内存对齐:使用DataLayout::getTypeAllocSize计算的大小包含内存对齐所需空间,能准确反映全局变量的实际内存占用范围。
  • 无main场景适配:若程序没有main函数,可通过LLVM生成全局构造函数(程序启动时自动执行)来完成收集逻辑。

运行效果

编译后的程序启动时,会自动收集所有全局变量的起始地址和结束地址(start + size)并加入global_list,后续可在程序任意位置访问该列表,用于内存越界检测等场景。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 17:40:46