LLVM 对函数参数的位码错误检测

Posted

技术标签:

【中文标题】LLVM 对函数参数的位码错误检测【英文标题】:LLVM's bitcode wrong detections of function's parameters 【发布时间】:2017-04-03 17:41:41 【问题描述】:

我正在使用 LLVM api 来解析位码文件。我有以下 sn-p,我正在使用此命令生成位码 $CC -emit-llvm -c -g source.c,其中 CC 设置为 clang 路径。

#include <stdio.h>

struct Point 
    int a;
    int b;
;

int func_0(struct Point p, int x) 
    return 0;

TypeID 应该有一个数值,具体取决于参数的类型。但是,对于整数x 和结构Point,我都获得了10 的值,它被称为TokenTyID。因此,我决定分别使用函数isIntegerTy()isStructTy(),看看是否至少在这种情况下,我得到了正确的结果。此解决方案适用于整数参数x,但不适用于结构。 如何正确识别结构并读取其字段?

为了完整起见,为了解析我使用此代码的位码:

using namespace llvm;

int main(int argc, char** argv) 
    LLVMContext context;
    OwningPtr<MemoryBuffer> mb;
    MemoryBuffer::getFile(FileName, mb);
    Module *m = ParseBitcodeFile(mb.get(), context);

    for (Module::const_iterator i = m->getFunctionList().begin(), e = m->getFunctionList().end(); i != e; ++i) 
        if (i->isDeclaration() || i->getName().str() == "main")
            continue;

        std::cout << i->getName().str() << std::endl;

        Type* ret_type = i->getReturnType();
        std::cout << "\t(ret) " << ret_type->getTypeID() << std::endl;

        Function::const_arg_iterator ai;
        Function::const_arg_iterator ae;

        for (ai = i->arg_begin(), ae = i->arg_end(); ai != ae; ++ai) 
            Type* t = ai->getType();

            std::cout << "\t" << ai->getName().str() << " " << t->getTypeID()
                      << "(" << t->getFunctionNumParams() << ")"
                      << " is struct? " << (t->isStructTy() ? "Y" : "N")
                      << " is int? " << (t->isIntegerTy() ? "Y" : "N")
                      << "\n";
        
    

    return 0;

我读了这篇 Why does Clang coerce struct parameters to ints 的帖子,内容是关于 clang 使用结构体执行的翻译,我很确定这也是我的问题。

【问题讨论】:

尝试使用 clang -S -emit-llvm 转储 LLVM IR? 是的,它也不起作用。如果我用“-S”选项生成IR码,然后用“llvm-as”生成位码,问题是一样的。 我的意思是,尝试查看生成的 IR 并找出你的结构变成了什么。 生成的IR是一样的。数据结构已被替换,就像我在第一篇文章中解释的那样。 【参考方案1】:

由于 clang 更改了 IR 中的函数签名,因此您必须使用调试信息获取该信息。这是一些粗略的代码:

DITypeIdentifierMap TypeIdentifierMap;

DIType* getLowestDINode(DIType* Ty) 
  if (Ty->getTag() == dwarf::DW_TAG_pointer_type ||
      Ty->getTag() == dwarf::DW_TAG_member) 
    DIType *baseTy =
      dyn_cast<DIDerivedType>(Ty)->getBaseType().resolve(TypeIdentifierMap);
    if (!baseTy) 
      errs() << "Type : NULL - Nothing more to do\n";
      return NULL;
    

    //Skip all the DINodes with DW_TAG_typedef tag
    while ((baseTy->getTag() == dwarf::DW_TAG_typedef || baseTy->getTag() == dwarf::DW_TAG_const_type
          || baseTy->getTag() == dwarf::DW_TAG_pointer_type)) 
      if (DITypeRef temp = dyn_cast<DIDerivedType>(baseTy)->getBaseType())
        baseTy = temp.resolve(TypeIdentifierMap);
      else
        break;
     
    return baseTy;
  
  return Ty;


int main(int argc, char** argv) 
  LLVMContext context;
  OwningPtr<MemoryBuffer> mb;
  MemoryBuffer::getFile(FileName, mb);
  Module *m = ParseBitcodeFile(mb.get(), context);
  if (NamedMDNode *CU_Nodes = m.getNamedMetadata("llvm.dbg.cu")) 
    TypeIdentifierMap = generateDITypeIdentifierMap(CU_Nodes);
  
  SmallVector<std::pair<unsigned, MDNode *>, 4> MDs;
  F.getAllMetadata(MDs);
  for (auto &MD : MDs) 
    if (MDNode *N = MD.second) 
      if (auto *subRoutine = dyn_cast<DISubprogram>(N)->getType()) 
        if (!subRoutine->getTypeArray()[0]) 
          errs() << "return type \"void\" for Function : " << F.getName().str()
            << "\n";
        

        const auto &TypeRef = subRoutine->getTypeArray();
        for (int i=0; i<TypeRef.size(); i++) 
          // Resolve the type
          DIType *Ty = ArgTypeRef.resolve(TypeIdentifierMap);
          DIType* baseTy = getLowestDINode(Ty);
          if (!baseTy)
            return;
          // If that pointer is a struct
          if (baseTy->getTag() == dwarf::DW_TAG_structure_type) 
            std::cout << "structure type name: " << baseTy->getName().str() << std::endl();
          
        
      
    
  

我知道它看起来很难看,但使用调试信息并不容易。

【讨论】:

感谢您的回复。你能把前面语句的全部代码给我吗? 当然,这里有一个代码链接:github.com/jiten-thakkar/DataStructureAnalysis/blob/dsa_llvm3.8/…我一直在为其他一些项目做这个

以上是关于LLVM 对函数参数的位码错误检测的主要内容,如果未能解决你的问题,请参考以下文章

iOS:无效的位码版本

如何检索 LLVM 中函数调用期间传递的参数?

使用 xcodebuild 打包 ipa 的位码错误

Xcode 8.2.1 - 错误:无效的位码版本(生产者:'802.0.41.0_0' 读者:'800.0.42.1_0')

归档基于 Xcode 错误的 Unity 应用程序:无效的位码版本(生产者:'802.0.42.0_0' 读者:'800.0.42.1_0')

函数复习