代码之家  ›  专栏  ›  技术社区  ›  zoecarver

GEP分段故障LLVM C++ API

  •  1
  • zoecarver  · 技术社区  · 8 年前

    我确信这很简单,但是,我已经想了一个多小时了,我想不出来。

    下面的代码给了我一个分段错误:

    Value *newArray = mBuilder.CreateGEP(alloca, value); // alloca is a `StructType`
    

    但这不是

    Value *newArray = mBuilder.CreateGEP(alloca, ConstantInt::get(mContext, APInt(32, 0)));
    

    价值 value

    %bar1 = load double, double* %bar
    %3 = fptoui double %bar1 to i32
    

    调试

    当我使用lldb调试它时,我得到:

    * thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x0)
        frame #0: 0x00000001000b9e6e a.out`llvm::PointerType::get(llvm::Type*, unsigned int) + 20
    a.out`llvm::PointerType::get:
    ->  0x1000b9e6e <+20>: movq   (%rdi), %rax
    

    问题

    为什么我会出现分割错误,我该如何修复它?

    如何再现问题?

    以下代码再现了问题:

    #include <vector>
    
    #include "llvm/ADT/STLExtras.h"
    #include "llvm/Support/raw_ostream.h"
    #include "llvm/IR/Value.h"
    #include "llvm/ADT/APFloat.h"
    #include "llvm/ADT/APInt.h"
    #include "llvm/IR/Constants.h"
    #include "llvm/IR/DerivedTypes.h"
    #include "llvm/IR/LLVMContext.h"
    #include "llvm/IR/IRBuilder.h"
    #include "llvm/IR/Instructions.h"
    
    using namespace llvm;
    
    static LLVMContext mContext;
    static IRBuilder<> mBuilder(mContext);
    static std::unique_ptr<Module> mModule = make_unique<Module>("example", mContext);
    static Module *M = mModule.get();
    
    static Type *dType = Type::getDoubleTy(mContext);
    static Type *i32 = IntegerType::get(mContext, 32);
    
    // helper functions
    static AllocaInst *entryCreateBlockAllocaType(Function *func, std::string name, Type* type) {
      IRBuilder<> tmpBuilder(&func->getEntryBlock(), func->getEntryBlock().begin());
      return tmpBuilder.CreateAlloca(type, nullptr, name);
    }
    
    static ArrayRef<Value *> PrefixZero (Value *index) {
      std::vector<Value *> out;
      out.push_back(ConstantInt::get(mContext, APInt(32, 0)));
      out.push_back(index);
      return ArrayRef<Value *>(out);
    }
    
    static AllocaInst *createVariable () {
      auto *func = mBuilder.GetInsertBlock()->getParent();
      auto *initValue = ConstantInt::get(mContext, APInt(32, 0));
    
      auto *alloca = entryCreateBlockAllocaType(func, "var", initValue->getType());
      mBuilder.CreateStore(initValue, alloca);
      return alloca; 
    }
    
    static std::vector<Type *> elementTypes (3, dType);
    static AllocaInst *createStruct () {
      auto *func = mBuilder.GetInsertBlock()->getParent();
    
      auto *mStructType = StructType::get(mContext, elementTypes);
      return entryCreateBlockAllocaType(func, "str", mStructType);
    }
    
    int main () {
      // create a main function
      auto *FT = FunctionType::get(i32, std::vector<Type *>(), false);
      auto *f = Function::Create(FT, Function::ExternalLinkage, "main", M);
    
      // set insert point for out below code
      auto *bb = BasicBlock::Create(mContext, "entry", f);
      mBuilder.SetInsertPoint(bb);
    
      // Create a variable
      auto *variable = createVariable();
      // create a struct
      auto *mStruct = createStruct();
    
      // Create a GEP with the loaded index
      auto *loadedVar = mBuilder.CreateLoad(variable, "loaded_index");
    
      // This is where the problem is.
      // If `PrefixZero` is changed to `ConstantInt::get(mContext, APInt(32, 0))` this works
      auto *elementPtr = mBuilder.CreateGEP(mStruct, PrefixZero(loadedVar)); 
    
      mBuilder.CreateRet(ConstantInt::get(mContext, APInt(32, 0))); 
      f->print(errs()); // print out the function
    
      return 1;
    }
    

    The code can also be checked out here.

    1 回复  |  直到 8 年前
        1
  •  1
  •   sepp2k    8 年前

    代码有两个问题:

    1. static ArrayRef<Value *> PrefixZero (Value *index) {
        std::vector<Value *> out;
        out.push_back(ConstantInt::get(mContext, APInt(32, 0)));
        out.push_back(index);
        return ArrayRef<Value *>(out);
      }
      

      documentation of ArrayRef :

      该类不拥有底层数据,它预计将用于数据驻留在其他缓冲区中的情况,该缓冲区的生存期超过arrayref的生存期。

      换句话说,返回 阿雷耶夫 指向局部变量是非法的,就像返回指向局部变量的指针一样。在内部 阿雷耶夫 公正商店 out data 指针和 外面的 超出范围(即 PrefixZero ) 数据 被释放了 阿雷耶夫 现在包含指向已释放内存的指针。

    2. 使用时 getelementptr 在结构上,表示成员访问的索引(即,在您的情况下是第二个索引)必须是常量。如果考虑一下,就不可能用其他方法对指令进行类型检查(请记住,通常结构的成员并不都具有相同的类型)。另外,计算给定非常量索引的指针偏移量基本上需要生成一个完整的查找表,而指针算术指令生成这么多代码则是违反直觉的。可以将结构上的gep视为 the_struct.member_name 在C中,你不能替换 member_name 也有一个变量。

    注意,如果在llvm的构建中启用了断言,第二个问题应该会导致断言失败“类型的getelementptrinst索引无效!”,它虽然不能完全告诉你你需要知道的一切(比如索引是以什么方式无效的),但却比“分段错误”更能指引你正确的方向。因此,如果没有收到该消息,请确保已启用断言,以便下次遇到问题时可以从断言消息中获益。