4

TL;博士;

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

详细问题说明:

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

考虑下面的程序(我的源代码的虚拟),我的目标是使用安全版本的 strcpy_s 重写对 strcpy 函数的所有调用,并在新函数调用中添加一个附加参数,即 - 目标指针最大大小。所以,对于下面的程序,我重构的调用就像 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;
}
4

1 回答 1

5

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

关于源位置的一些信息

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

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

当您正在寻找一种方法来获取替换的原始宏名称时,您只需要获取拼写(即它在原始源代码中的编写方式)并且您不需要携带太多关于宏定义的信息,函数式宏及其参数等。

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

// 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";
}

为片段生成此输出:

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

我希望这些信息对你有用。与 Clang 一起愉快地进行黑客攻击!

于 2019-06-09T09:30:14.240 回答