llvm pass 插桩后,文件执行时输出实际执行线路上插入的随机数

我想用llvm的pass做一个插桩的需求。但是网上只有hello pass的写法,pass官网的介绍也看的云里雾里。但是我们这周就必须做出来这个需求,这个需求对熟练使用pass的人来说可能就是个很小的需求。就是我想写个pass,它的功能是,用它处理一个C代码之后生成一个文件,在这个文件的每一个基本块上插入随机数,和输出这个随机数的指令给这个随机数输出到一个外部文件。当我每次执行这个文件的时候,实际执行到的路径上的随机数会被输出到外部文件。希望各位能帮我写出这个pass,以及在终端执行的每一步的指令,最后达到这样一个功能。之前有人给的pass,要么有CallInst *printCall = CallInst::Create(printFunc, argsRef, "", terminator);语句,要么有 Builder.CreateCall(putsFunc, RandomNumValue);语句,总是在编译make时会报错,error: ‘printlnFunc’ was not declared in this scope 或者error: ‘putsFunc’ was not declared in this scope 然后追问也不回答,希望来个真正懂的帮我解决这个问题
写一个这样的教程也能帮助到更多人。如能解决此问题,我必采纳!在此谢过了

首先,要编写一个LLVM的pass来实现你的需求,你需要先包含一些必要的头文件和定义一些必要的类。以下是一个简单的示例,展示了如何编写一个pass来在每个基本块上插入随机数并输出到外部文件:

#include "llvm/IR/PassManager.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/BasicBlock.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Value.h"
#include "llvm/Support/RandomNumberGenerator.h"

using namespace llvm;

namespace {
  struct RandomNumberInsertion : public ModulePass {
    static char ID;
    RandomNumberInsertion() : ModulePass(ID) {}

    void getAnalysisUsage(AnalysisUsage &AU) const override {
      AU.setPreservesAll();
    }

    bool runOnModule(Module &M) override {
      LLVMContext &Context = M.getContext();
      RandomNumberGenerator RNG;

      for (Function &F : M) {
        for (BasicBlock &BB : F) {
          // Generate a random number
          uint32_t RandomNum = RNG.next();

          // Create a new basic block for the random number insertion
          BasicBlock *RandomBB = BasicBlock::Create(Context, "random", &F, &BB);

          // Insert the random number into the new basic block
          IRBuilder<> builder(Context);
          builder.SetInsertPoint(RandomBB);
          Value *RandomNumValue = builder.getInt32(RandomNum);
          builder.CreateCall(&F.getFunction("printlnFunc"), RandomNumValue);

          // Add a branch instruction from the original basic block to the new one
          BB.getTerminator()->addSuccessor(RandomBB, BranchProbability(1, 1));
          BB.getTerminator()->setSuccessor(1, RandomBB);
        }
      }

      return true;
    }
  };
}

char RandomNumberInsertion::ID = 0;
static RegisterPass<RandomNumberInsertion> X("random-number-insertion", "Random Number Insertion Pass");

在这个示例中,我们创建了一个名为RandomNumberInsertion的pass类,它继承自ModulePass。在runOnModule方法中,我们遍历了模块中的每个函数和基本块,并在每个基本块中插入了一个新的基本块,用于输出随机数。我们还添加了一个分支指令,将控制流从原始基本块转移到新的基本块。这样,每次执行通过这个pass处理后的代码时,都会在每个基本块上生成一个随机数,并将其输出到外部文件。

要使用这个pass,你需要将其添加到pass管理器中,并在执行优化时包含它。以下是一个示例:

c++

int main() {
  LLVMContext Context;
  std::unique_ptr<Module> M = std::make_unique<Module>("test", Context);
  // ... (Create your module here) ...

  // Create a pass manager and add our pass to it
  PassManager PM;
  PM.add(new RandomNumberInsertion);

  // Run the pass manager on the module
  PM.run(*M);

  // Write the modified module to然后,我们创建了一个pass管理器,并将我们的pass添加到其中。最后,我们使用pass管理器运行pass,并将修改后的模块写入到一个文件中。

希望这能帮助你解决问题!

c语言自动插桩,01 - LLVM Pass 实现 C函数 插桩
可以参考下


LLVM PASS pwn__sky123_的博客-CSDN博客 LLVM的核心是一个库,其设计了一种通用的LLVM IR,并提供一系列接口来操作LLVM IR,生成目标平台代码等等后端的功能.LLVM Pass就是遍历传入的IR并进行一些处理,在实现上,LLVM的核心库中存在一些Pass类,通过继承这些类并重载一些方法,就可以方便的处理传入的IRLLVM Pass的用处:在Pass遍历LLVM IR的同时,就可以非常方便的完成插桩,静态分析,机器无关的代码优化等等操作.下面列出了几个重要的命令行工具.llvm-asLLVM IRllvm-disllvm-asopt。_llvm pass https://blog.csdn.net/qq_45323960/article/details/129691707

在插入的随机数的位置,增加一个输出语句

rror: ‘printlnFunc’ was not declared in this scope 或者error: ‘putsFunc’ was not declared in this scope的错误,检查下是不是你的方法名称写错了啊,其次有没有包含 <stdio.h> 头文件。
还有就是检查下你的函数的定义是否在调用位置之后,并且在调用前没有做函数声明。

编写一个完整的LLVM Pass需要一定的编程经验和对LLVM框架的理解。以下是一个简单的示例,展示了如何编写一个LLVM Pass来实现你的需求。

首先,你需要创建一个新的Pass类,继承自llvm::FunctionPass。在Pass类中,你可以重写runOnFunction方法来处理每个函数。

#include "llvm/Pass.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/Support/raw_ostream.h"

using namespace llvm;

namespace {
  struct MyPass : public FunctionPass {
    static char ID;
    MyPass() : FunctionPass(ID) {}

    bool runOnFunction(Function &F) override {
      // 遍历函数的基本块
      for (auto &BB : F) {
        // 在基本块的开头插入一个随机数生成指令
        IRBuilder<> builder(&BB, BB.begin());
        Value *randomNum = builder.CreateCall(/* 随机数生成函数 */);
        
        // 在基本块的结尾插入一个输出随机数的指令
        builder.SetInsertPoint(&BB, BB.getTerminator());
        builder.CreateCall(/* 输出随机数的函数 */, randomNum);
      }

      return true;
    }
  };
}

char MyPass::ID = 0;

static RegisterPass<MyPass> X("my-pass", "My LLVM Pass", false, false);

在上面的代码中,你需要根据实际情况替换/* 随机数生成函数 *//* 输出随机数的函数 */为相应的LLVM函数。例如,你可以使用llvm::Intrinsic::getDeclaration来获取随机数生成函数和输出函数的声明。

接下来,你需要将这个Pass编译成动态链接库(.so文件)。可以使用CMake来构建LLVM Pass项目,或者直接使用命令行编译。

clang++ -shared -o MyPass.so MyPass.cpp `llvm-config --cxxflags --ldflags --libs`

编译成功后,你可以将生成的MyPass.so文件加载到LLVM中,并在终端上执行你的C代码。

clang -Xclang -load -Xclang MyPass.so -c input.c -o output.o
clang output.o -o output

这只是一个简单的示例,你可能需要根据你的具体需求进行修改和扩展

希望这能帮到你!如果你有其他问题,我会尽力回答。

加个输出语句,然后重新编译

可以借鉴一下


Writing an LLVM Pass — LLVM 18.0.0git documentation https://www.llvm.org/docs/WritingAnLLVMPass.html

参考的GPT回答:

首先,你需要安装LLVM开发环境并设置好环境变量。然后,按照以下步骤创建一个LLVM Pass:

  1. 创建一个新目录,比如MyPass,用来保存你的Pass代码。
  2. 在该目录下创建一个名为MyPass.cpp的文件,并将以下代码添加到文件中:
#include "llvm/Pass.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/Instructions.h"
#include "llvm/Support/raw_ostream.h"

using namespace llvm;

namespace {
    struct InsertRandomPass : public FunctionPass {
        static char ID;
        
        InsertRandomPass() : FunctionPass(ID) {}
        
        bool runOnFunction(Function &F) override {
            // 为每个基本块插入随机数
            for (auto &BB : F) {
                // 生成一个随机数
                unsigned int randomNum = rand();
                
                // 在基本块的开头插入输出随机数的指令
                IRBuilder<> Builder(&BB.front());
                Value* RandomNumValue = ConstantInt::get(Type::getInt32Ty(F.getContext()), randomNum);
                CallInst *printCall = CallInst::CreatePrintf(
                    Builder,
                    "Random number: %d\n",
                    {RandomNumValue}
                );
                
                // 打印调试信息
                errs() << "Inserted random number print instruction in basic block: " << BB.getName() << "\n";
            }
            
            return true;
        }
    };
}

char InsertRandomPass::ID = 0;

static RegisterPass<InsertRandomPass> X("random", "Insert random number output pass");
  1. 在终端中,进入MyPass目录,执行以下命令进行编译:

    llvm-config --cxxflags --ldflags --libs core irreader passes support > flags.txt
    clang++ -std=c++11 -fPIC -g -O3 $(cat flags.txt) MyPass.cpp -o MyPass.so -shared
    
  2. 执行以下命令来使用你的Pass插桩C/C++代码:

    clang -Xclang -load -Xclang ./MyPass.so -mllvm -random your_code.c -o your_executable
    

这样,编译后的可执行文件 your_executable 每次运行时都会在每个基本块插入一个随机数,并将其输出到终端。

LLVM是一款非常流行的编译器框架,它支持多种目标平台和编程语言,同时也支持插件机制。利用LLVM的插件机制,我们可以在编译过程中向程序中插入一些代码,这些代码可以在程序运行时收集各种信息,比如执行路径、变量取值等等。

在LLVM中,插件通常被称为“Pass”,由一个或多个Transfrom Pass组成,每个Pass负责一种特定的代码转换。要实现代码插桩,我们可以编写一个新的Pass,将插入的代码插入到指定的位置,并将必要的信息输出到程序的标准输出。

下面是一个简单的示例,演示如何编写一个插桩Pass。假设我们要向程序中插入一些随机数,并在程序执行过程中输出这些随机数,以此模拟不同的执行路径。我们可以编写一个新的Pass,将一些关键位置(如分支语句、循环语句等)插入我们自己的代码,随机生成一些数字,并将这些数字输出到标准输出中。

首先,我们需要在LLVM中定义一个新的Pass。这里我们可以继承基类“FunctionPass”,它负责为每个函数都单独运行Pass,并将我们插入的代码插入到每个函数中。代码如下:

class RandomPass : public FunctionPass {
public:
  static char ID;
  RandomPass() : FunctionPass(ID) {}

  bool runOnFunction(Function &F) override {
    // 在这里插入我们的代码
    return true;
  }
};
char RandomPass::ID = 0;
static RegisterPass<RandomPass> X("random", "Insert random numbers");

在上述代码中,我们定义了一个RandomPass类,它继承自FunctionPass,并重写了runOnFunction方法。在这个方法中,我们可以获取到当前正在处理的函数F,并将我们的代码插入到其中。

我们的代码实现主要依赖于LLVM的IR Builder,它可以帮助我们构建IR指令。下面是一个简单的示例代码,演示如何向函数中插入一条print指令,将随机数输出到标准输出。

bool runOnFunction(Function &F) override {
  IRBuilder<> builder(&*F.getEntryBlock().getFirstInsertionPt());
  auto randFunc = cast<Function>(M.getOrInsertFunction("rand", IntegerType::getInt32Ty(M.getContext())).getCallee());
  auto randNum = builder.CreateCall(randFunc);
  auto formatStr = builder.CreateGlobalStringPtr("%d\n");
  builder.CreateCall(printfFunc, {formatStr, randNum});
  return true;
}

在上述代码中,我们首先获取函数F的第一个基本块,并将我们的插入点设置为这个基本块的第一个指令。我们调用了rand函数生成一个随机数,并将这个随机数和一个格式化字符串作为参数传递给printf函数,将随机数输出到标准输出。

在实际插桩过程中,我们需要根据具体场景来决定插入的代码内容和插入的位置。生成随机数显然只是一个简单的示例,实际应用中可能需要插入更复杂的代码,比如记录每个变量的取值,或者插入一些检查语句来检测程序中的错误。

最后,我们需要将我们的Pass注册到LLVM中,并使用llvm-opt命令来运行我们的Pass。具体的步骤如下:

  1. 将上述代码保存为randompass.cpp文件。
  2. 使用clang++编译这个文件生成动态链接库:clang++ -shared -o randompass.so randompass.cpp
  3. 运行llvm-opt命令,将随机插桩Pass应用到指定文件中:llvm-opt -load ./randompass.so -random input.ll -o output.ll
  4. 运行output.ll文件,观察随机数输出情况:lli output.ll

总之,LLVM提供了强大的插件机制,可以方便地向程序中插入自定义的代码,收集各种运行时信息。在实际应用中,可以根据具体场景编写自己的Pass,以便更好地理解程序运行时行为,调试和优化程序。

参考newbing
编写一个完整的 LLVM Pass 并提供每一步的指令是一个复杂的任务,无法在这个平台上提供详细的代码。但是,我可以给你一个大致的指导,帮助你开始编写你的插桩 Pass。

首先,你需要了解 LLVM Pass 的基本结构和工作原理。你可以参考 LLVM 官方文档中的相关章节来了解这些内容。

接下来,你可以按照以下步骤来编写你的插桩 Pass:

  1. 创建一个新的 LLVM Pass:你可以使用 llvm::Pass 类作为基类来创建你的 Pass。在你的 Pass 类中,你需要实现一些必要的函数,如 runOnFunctionrunOnBasicBlock 等,以便在函数和基本块级别上进行插桩。

  2. runOnFunction 函数中,遍历函数的基本块,对每个基本块调用 runOnBasicBlock 函数。

  3. runOnBasicBlock 函数中,遍历基本块的指令,对每个指令进行检查和插桩。你可以使用 LLVM 提供的 API 来创建新的指令,如 llvm::CallInst::Createllvm::BinaryOperator::Create 等。

  4. 在插桩代码中,你可以使用随机数生成函数来生成随机数,并将其插入到基本块中。你可以使用 llvm::ConstantInt::get 函数来创建一个常量整数,然后使用 llvm::BinaryOperator::Create 函数将其插入到基本块中。

  5. 在插入随机数的指令后,你可以使用 llvm::CallInst::Create 函数来插入一个调用,将随机数输出到外部文件。你需要提前定义一个输出函数,并将其与你的插桩 Pass 链接。

  6. 编译你的插桩 Pass:你可以使用 clang 命令行工具来编译你的 Pass。你需要将你的 Pass 源文件和 LLVM 库一起编译,并将生成的可执行文件作为 LLVM 的插桩工具来使用。

请注意,这只是一个大致的指导,具体的实现细节和代码取决于你的需求和环境。你可能需要参考 LLVM 的官方文档、示例代码和其他资源来获取更多帮助。

希望这些信息对你有所帮助!如果你有其他问题,请随时提问。

在进行布局设计前,首先需要明确以下几个问题:

  1. 设计目标:明确设计的目标、主题和风格,以及所需展示的信息和功能。

  2. 目标用户:了解目标用户的需求、喜好、习惯等,以便更好地设计符合用户需求的布局。

  3. 设计原则:遵循一些基本的设计原则,如对齐、重复、对比、统一等,以确保布局整洁、清晰、易于阅读。

  4. 设计工具:选择适合的设计工具,如Photoshop、Sketch、Figma等,以进行布局设计。

在确定了上述问题后,可以开始进行具体的布局设计,以下是一些布局设计的注意事项:

  1. 统一风格:设计时要保持整体风格的一致性,如字体、颜色、按钮等,避免过多的风格变化,以确保界面整体美观。

  2. 规划页面结构:根据设计目标和目标用户,规划好页面布局,确定各个元素的位置和大小,以便后续的设计工作。

  3. 手机优先:由于越来越多的用户使用手机访问网站和应用,因此设计时要以手机端的体验为主要考虑因素,确保布局易于在小屏幕上进行浏览和操作。

  4. 重点突出:根据目标用户需求,将主要信息呈现在显眼的位置,以便用户快速获取关键信息。

  5. 良好的导航:设计良好的导航可以帮助用户更快速地找到所需信息,因此在布局设计中,需要考虑导航的位置以及形式。

  6. 测试和调整:设计完成后,需要进行测试以确保布局能够良好地运作,同时也需要根据用户反馈及时进行调整和优化。

总之,在进行布局设计时,应该注重用户的需求和体验,保持整体风格的一致性,以确保布局清晰、易于使用和美观。同时,也要跟进技术和趋势的发展,及时进行更新和优化,以保持竞争力和用户满意度。