【问题标题】:Clang: How to get the macro name used for size of a constant size array declarationClang:如何获取用于常量大小数组声明大小的宏名称
【发布时间】:2019-10-24 00:11:03
【问题描述】:

TL;DR;

如何从 callExpr -> arg_0 -> DeclRefExpr 中获取用于常量大小数组声明大小的宏名称。

详细问题说明:

最近我开始研究一个需要源到源转换工具来修改的挑战 带有附加参数的特定函数调用。研究我可以实现的方法介绍了我 到这个惊人的工具集 Clang。我一直在学习如何使用 libtooling 中提供的不同工具 实现我的目标。但是现在我遇到了一个问题,请在这里寻求您的帮助。

考虑下面的程序(我的源代码的虚拟),我的目标是重写所有对 strcpy 的调用 具有安全版本的 strcpy_s 的函数,并在新函数调用中添加附加参数 即 - 目标指针最大大小。所以,对于下面的程序,我重构的调用就像 strcpy_s(inStr, STR_MAX, argv[1]);

我编写了一个 RecursiveVisitor 类并检查了 VisitCallExpr 方法中的所有函数调用,以获得最大大小 dest arg 我得到了第一个 agrument 的 VarDecl 并试图获取大小(ConstArrayType)。自从 源文件已经过预处理,我看到 2049 的大小,但我需要的是宏 STR_MAX 这个案例。我怎么能得到那个? (使用此信息创建替换,然后使用 RefactoringTool 替换它们)

#include <stdio.h>
#include <string.h>
#include <stdlib.h> 

#define STR_MAX 2049

int main(int argc, char **argv){
  char inStr[STR_MAX];

  if(argc>1){
    //Clang tool required to transaform the below call into strncpy_s(inStr, STR_MAX, argv[1], strlen(argv[1]));
    strcpy(inStr, argv[1]);
  } else {
    printf("\n not enough args");
    return -1;
  }

  printf("got [%s]", inStr);

  return 0;
}

【问题讨论】:

    标签: clang clang++ llvm-clang clang-static-analyzer libtooling


    【解决方案1】:

    正如您正确注意到的那样,源代码已经过预处理,并且所有宏都已扩展。因此,AST 将简单地使用一个整数表达式作为数组的大小。

    关于源位置的一点信息

    注意:您可以跳过它并直接进入下面的解决方案

    关于扩展宏的信息包含在 AST 节点的源位置中,通常可以使用 Lexer 检索(Clang 的词法分析器和预处理器紧密相连,甚至可以视为一个实体)。这是最低限度的,使用起来不是很明显,但它就是这样。

    当您正在寻找一种方法来获取原始宏名称以进行替换时,您只需要获取 拼写(即它在原始源代码中的编写方式)并且您不需要不需要过多介绍宏定义、函数式宏及其参数等。

    Clang 有两种不同的位置:SourceLocationCharSourceLocation。通过 AST,几乎可以在任何地方找到第一个。它指的是 tokens 方面的位置。这解释了为什么 beginend 位置可能有点违反直觉:

    // clang::DeclRefExpr
    //
    //  ┌─ begin location
    foo(VeryLongButDescriptiveVariableName);
    //  └─ end location
    
    // clang::BinaryOperator
    //
    //           ┌─ begin location
    int Result = LHS + RHS;
    //                 └─ end location
    

    如您所见,这种类型的源位置指向相应标记的开头。另一方面,CharSourceLocation 直接指向字符

    所以,为了得到表达式的原始文本,我们需要将SourceLocation的转换为CharSourceLocation的,并从源中获取对应的文本。

    解决方案

    我已经修改了您的示例以显示宏扩展的其他情况:

    #define STR_MAX 2049
    #define BAR(X) X
    
    int main() {
      char inStrDef[STR_MAX];
      char inStrFunc[BAR(2049)];
      char inStrFuncNested[BAR(BAR(STR_MAX))];
    }
    

    以下代码:

    // clang::VarDecl *VD;
    // clang::ASTContext *Context;
    auto &SM = Context->getSourceManager();
    auto &LO = Context->getLangOpts();
    auto DeclarationType = VD->getTypeSourceInfo()->getTypeLoc();
    
    if (auto ArrayType = DeclarationType.getAs<ConstantArrayTypeLoc>()) {
      auto *Size = ArrayType.getSizeExpr();
    
      auto CharRange = Lexer::getAsCharRange(Size->getSourceRange(), SM, LO);
      // Lexer gets text for [start, end) and we want him to grab the end as well
      CharRange.setEnd(CharRange.getEnd().getLocWithOffset(1));
    
      auto StringRep = Lexer::getSourceText(CharRange, SM, LO);
      llvm::errs() << StringRep << "\n";
    }
    

    为 sn-p 生成此输出:

    STR_MAX
    BAR(2049)
    BAR(BAR(STR_MAX))
    

    我希望这些信息对您有所帮助。愉快地使用 Clang 进行黑客攻击!

    【讨论】:

    • 太棒了,你拯救了我的一天。非常感谢您提供如此清晰的解释和解决方案代码。
    猜你喜欢
    • 2011-02-21
    • 1970-01-01
    • 2020-07-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多