【线上问题定位】Llvm库ARM环境崩溃问题

本文分析了一本地编写的JIT demo在高并发下运行时遇到的崩溃问题,源于LLVM在处理ARM的.eh_frame段重定向时的断言失败。通过问题定位,作者揭示了小代码模型导致的地址计算限制,并提供了调整编译参数和修改LLVM源码的解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

问题现象

问题分解思维导图

工具说明

问题定位

源码


问题现象

本地编写的demo, 在多线程组装和运行JIT函数的过程中产生崩溃问题。崩溃信息为

/xxx/llvm6.0/llvm/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldELF.cpp:383: void llvm::RuntimeDyldELF::resolveAArch64Relocation(const llvm::SectionEntry&, uint64_t, uint64_t, uint32_t, int64_t): Assertion `static_cast<int64_t>(Result) >= (-2147483647-1) && static_cast<int64_t>(Result) <= (4294967295U)' failed.

Llvm库编译方式为: Release+Assert断言版本

 

问题分解思维导图

 

工具说明

 

问题定位

问题是否必现?

跑产品集成测试环境,问题是必现的。每一次的崩溃问题堆栈都相同。

 

问题复现的场景是什么?

跑产品集成测试环境是1000并发,每一个并发线程组装和运行JIT函数100次。

 

是否可以提取成demo, 降低复杂度?

把JIT相关组装和使用的简化逻辑提取出来编写成demo,并且模拟环境1000并发,组装JIT函数100次的场景。问题得到复现。

 

崩溃的位置,处理的是什么逻辑?

处理ARM环境下,JIT重定向功能中 .eh_frame段的重定向功能。".eh_frame"段是用于异常处理,包含异常处理过程需要的帧信息。

 

出现问题的代码,影响的因素有哪些?

".eh_frame段"的重定向功能影响因素是编译器的大小代码模型。

 

问题原因分析

问题一、当前编译器默认使用小代码模型,在jit的重定位模块resolveAArch64Relocation函数case ELF_AARCH64_PREL32分支中会计算代码段和.eh_frame段的重定位地址之前的差距,相差超过+/- 2G会触发断言,需要使用大代码模型解除该限制。如下图所示

 

问题二、当编译器使用大代码模型后,.eh_frame段的地址还是使用4bytes地址,是LLVM源码的bug,导致大代码模型无法生效。

 

解决问题的方案是什么?

问题一的解决方案分两种

<1> 编译的bc文件需要重新定义bc文件中的大代码模型。

clang++ -c -emit-llvm xx.cpp -o xx.bc -mcmodel=large

PS:

-mcmodel=large  //是设置编译器使用大代码模型

 

<2> 如果程序中手动组装JIT函数则需要设置

EngineBuilder类EngineBuilder engineBuilder(xxx)engineBuilder.setCodeModel(CodeModel::Large) //是设置编译器使用大代码模型

 

问题二的解决方案

在生成目标机器码的接口中,添加AArch64分支,通过判断代码模型分别对应使用4/8字节重定位地址。这样使用大代码模型时生成的.eh_frame重定位类型不会有2G的限制,且该修改只针对.eh_frame段。

修改lib/MC/MCObjectFileInfo.cpp文件,如下图所示

 

源码

程序不保证能够运行,看个样子就行,代码仅仅是示意作用。可以自己本地参考编写。

#include "llvm_header.h"#include <sstream>typedef llvm::IRBuilder<> LlvmIRBuilder;class LlvmEntry {public:    LlvmEntry() {        m_module           = NULL;        m_llvm_main_func   = NULL;        m_llvm_context     = NULL;        m_execution_engine = NULL;    }    ~LlvmEntry() {}    void initialize(const std::string& name,                 std::unique_ptr<llvm::Module> mod,                 llvm::LLVMContext * context) {        m_llvm_context = context;        m_module = mod.get();        std::string errorStr;        llvm::EngineBuilder engineBuilder(std::move(mod));        engineBuilder.setEngineKind(llvm::EngineKind::JIT);        engineBuilder.setErrorStr(&errorStr);        engineBuilder.setOptLevel(llvm::CodeGenOpt::Aggressive);        // engineBuilder.setCodeModel(CodeModel::Large); // 设置大代码模型        m_execution_engine = engineBuilder.create();        if (NULL == m_execution_engine) {            std::stringstream sstr;            sstr << "[LlvmEntry::initialize] LLVM ExecutionEngine is null"                << ", because [" << errorStr << "]";            std::cout << sstr.str().c_str() << std::endl;            return;        }        m_builder = new LlvmIRBuilder(*m_llvm_context);    }    void release() {        if (m_llvm_context) {            delete m_llvm_context;            m_llvm_context = NULL;        }        if (m_execution_engine) {            delete m_execution_engine;            m_execution_engine = NULL;        }        if (m_builder) {            delete m_builder;            m_builder = NULL;        }    }    llvm::LLVMContext* getLlvmContext() const {        return m_llvm_context;    }    llvm::Module* getLlvmModule() const {        return m_module;    }    void setLlvmModule(llvm::Module* mod) {        m_module = mod;    }    llvm::ExecutionEngine* getLlvmExecutionEngine() const {        return m_execution_engine;    }    void setLlvmExecutionEngine(llvm::ExecutionEngine* engine) {        m_execution_engine = engine;    }    LlvmIRBuilder* getLlvmBuilder() const {        return m_builder;    }private:    llvm::LLVMContext* m_llvm_context;    llvm::Module* m_module;    llvm::ExecutionEngine* m_execution_engine;    llvm::Function* m_llvm_main_func;    LlvmIRBuilder* m_builder;};
#include "llvm_header.h"#include "llvm_entry.h"#include <sstream>#include "llvm/Support/Debug.h"#include "type.h"std::unique_ptr<llvm::Module> loadBC(llvm::LLVMContext& context) {    SMDiagnostic Err;    std::unique_ptr<llvm::Module> main_module = parseIRFile("static_lib.bc", Err, context);    if (!main_module) {        Err.print("[parseBitcodeFile] fail ! -> ", errs());    }    return main_module;}void createJitFunctionDefine(llvm::IRBuilder<> *ir_builder, llvm::Module* mod, LLVMContext& context, int64_t idx) {    std::vector<Type*>FuncTy_0_args;    FunctionType* FuncTy_0 = FunctionType::get(      /*Result=*/Type::getVoidTy(context),      /*Params=*/FuncTy_0_args,      /*isVarArg=*/false);    std::ostringstream ss;    ss << "my_jit_func" << idx;    llvm::Function* fn = llvm::Function::Create(            FuncTy_0, llvm::GlobalValue::ExternalLinkage, ss.str().c_str(), mod);    if (ir_builder != NULL) {      llvm::BasicBlock* entry_block =          llvm::BasicBlock::Create(context, "entry", fn);      ir_builder->SetInsertPoint(entry_block);    }    fn->addAttribute(~0U, llvm::Attribute::AlwaysInline);}void createJitFunctionBody(llvm::IRBuilder<> *ir_builder, llvm::Module* mod, LLVMContext& context) {    llvm::Type *i1T  = mod->getTypeByName("struct.IntVal");    llvm::Value* i1  = ir_builder->CreateAlloca(i1T);    llvm::Type *i2T  = mod->getTypeByName("struct.IntVal");    llvm::Value* i2  = ir_builder->CreateAlloca(i2T);    llvm::Type *retT = mod->getTypeByName("struct.BoolVal");    llvm::Value* ret = ir_builder->CreateAlloca(retT);    std::vector<Value*> params;    params.push_back(i1);    params.push_back(i2);    params.push_back(ret);    Function* eq_func = mod->getFunction("_Z16intobj_eq_intobjR6IntValS0_R7BoolVal");    ir_builder->CreateCall(eq_func, params);    ir_builder->CreateRetVoid();}void* OneThread(void* args) {    typedef void (*jitFuncPtr)();    int64_t thread_id = 1;    llvm::LLVMContext *context = new llvm::LLVMContext();    std::unique_ptr<llvm::Module> mod = loadBC(*context);    std::string module_name = "moudule";    LlvmEntry *entry = new LlvmEntry();    entry->initialize(module_name, std::move(mod), context);    llvm::Module *module = entry->getLlvmModule();    llvm::ExecutionEngine* execution_engine = entry->getLlvmExecutionEngine();    llvm::IRBuilder<> *ir_builder = entry->getLlvmBuilder();    module->setDataLayout(execution_engine->getDataLayout());    createJitFunctionDefine(ir_builder, module, *context, thread_id);    createJitFunctionBody(ir_builder, module, *context);    std::ostringstream ss;    ss << "my_jit_func" << thread_id;    //call main function    void* irFunc = reinterpret_cast<void*>(execution_engine->getFunctionAddress(ss.str().c_str()));    if (irFunc == NULL) {        std::cout << "myJitFunc is Null !" << std::endl;    }    jitFuncPtr jitFunc = reinterpret_cast<jitFuncPtr>(irFunc);    jitFunc();    delete entry;    return NULL;}void createThread() {    int NUM_THREADS = 1;    pthread_t tids[NUM_THREADS];    for(int i = 0; i < NUM_THREADS; ++i)    {        int ret = pthread_create(&tids[i], NULL, OneThread, NULL);        if (ret != 0)        {           std::cout << "pthread_create error: error_code=" << ret << std::endl;        }    }    for(int i = 0; i < NUM_THREADS; ++i) {        pthread_join(tids[i], NULL);    }}int main(int argc, char**argv) {    llvm::InitializeNativeTarget();    llvm::InitializeNativeTargetAsmPrinter();    llvm::InitializeNativeTargetAsmParser();    llvm::InitializeNativeTargetDisassembler();    //llvm::DebugFlag = true;    createThread();    llvm_shutdown();    std::cout << "main function fininsh !" << std::endl;    return 0;}

 

参考资料

//.eh_frame段的描述信息

https://refspecs.linuxbase.org/LSB_3.0.0/LSB-PDA/LSB-PDA/specialsections.html

// 关于编译器mcmodel选项的说明:

// -mcmodel=small [默认值]程序和它的符号必须位于2GB以下的地址空间。

// -mcmodel=large 对地址空间没有任何限制。

https://gcc.gnu.org/onlinedocs/gcc/x86-Options.html

// 查看LLVM最新代码,在PowerPC也解决过类似问题

https://www.mail-archive.com/llvm-bugs@lists.llvm.org/msg22257.html

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值