网上我就搜到这一篇文章介绍使用 LLVM Pass 的方式,来实现函数插桩。

但是按照他文章的一步步坐下来,发现各种报错,要不就是 pass 无法被 xcode 加载,反正遇到了不少的问题。

而且该作者很多核心的部分都没有写出来,不知道是因为太简单,还是不想让别人知道 ~

经历了两周多左右的研究,以 LLVM 小白的身份,终于跑起来了 ~ 不容易 ~

注意:如下内容,最好需要你已经有 LLVM 环境!!!

02. C 函数 手动插桩

long _ly_fun_b()

{

struct timeval star;

gettimeofday(&star, NULL);

long b = star.tv_sec * 1000000 + star.tv_usec;

return b;

}

void _ly_fun_e(char *name, long b)

{

struct timeval end;

gettimeofday(&end, NULL);

long e = end.tv_sec * 1000000 + end.tv_usec;

long t = e - b;

printf("%s %ld us\n",name, t);

}

int main()

{

long b = _ly_fun_b();

printf("hello world!");

_ly_fun_e("main", b);

return 0;

}

手动插桩实际上就是将预先写好的开始和结束两个函数的调用,手动写入到 main() 执行前 和 执行后。

那么自动插桩了?

就是不需要手动在 main() 中进行修改,而是在编译的过程中,自动将上面2个函数,插入到 main() 执行前 和 执行后。

那么如何在编译的时候,在 main() 插入其他的 函数调用 了?

03. 通过 LLVM Pass 自动插桩的过程

001.jpg

这里涉及一些 LLVM IR 的东西,在这里就不列举了,可以自己去学习下。

总之,我们可以在如上 橘黄色 Optimizer (优化器)这部分,来完成 自动插桩 的目的。

而 LLVM 留给开发者在 Optimizer (优化器)阶段 读写 IR 的方式也有很多,比如:Pass、ClangPlugin ..

至于 Pass 和 ClangPlugin 对比,可以自己去看下,本文通过 Pass 来实现。

04. 实现 函数插桩 Pass

04-01. $LLVM_SOURCE/lib/Transforms/MyPlacement/MyPlacementPass.cpp

#include

#include

#include

#include "llvm/Support/raw_ostream.h"

#include "llvm/Pass.h"

#include "llvm/IR/Module.h"

#include "llvm/IR/Function.h"

#include "llvm/IR/Instructions.h"

#include "llvm/IR/LegacyPassManager.h"

#include "llvm/IR/IRBuilder.h"

//#include "llvm/IR/BasicBlock.h"

//#include "llvm/IR/Constants.h"

//#include "llvm/IR/LLVMContext.h"

//#include "llvm/IR/Type.h"

#include "llvm/Transforms/IPO/PassManagerBuilder.h"

using namespace llvm;

struct MyPlacementPass : public FunctionPass

{

static char ID;

MyPlacementPass() : FunctionPass(ID){}

bool runOnFunction(Function &F) override

{

printf("------------- runOnFunction --------------\n");

if (F.getName().startswith("_ly_fun"))

{

return false;

}

LLVMContext &context = F.getParent()->getContext();

BasicBlock &bb = F.getEntryBlock();

Instruction *beginInst = dyn_cast(bb.begin());

FunctionType *type = FunctionType::get(Type::getInt64Ty(context), {}, false);

Constant *beginFun = F.getParent()->getOrInsertFunction("_ly_fun_b", type);

Value *beginTime = nullptr;

if (Function *fun = dyn_cast(beginFun))

{

CallInst *inst = CallInst::Create(fun);

inst->insertBefore(beginInst);

beginTime = inst;

}

for (Function::iterator I = F.begin(), E = F.end(); I != E; ++I)

{

BasicBlock &BB = *I;

for (BasicBlock::iterator I = BB.begin(), E = BB.end(); I != E; ++I)

{

ReturnInst *IST = dyn_cast(I);

if (IST)

{

FunctionType *type = FunctionType::get(Type::getVoidTy(context), {Type::getInt8PtrTy(context),Type::getInt64Ty(context)}, false);

Constant *s = BB.getModule()->getOrInsertFunction("_ly_fun_e", type);

if (Function *fun = dyn_cast(s))

{

IRBuilder<> builder(&BB);

CallInst *inst = CallInst::Create(fun, {builder.CreateGlobalStringPtr(BB.getParent()->getName()), beginTime});

inst->insertBefore(IST);

}

}

}

}

return false;

}

};

char MyPlacementPass::ID = 0;

// Automatically enable the pass.

// http://adriansampson.net/blog/clangpass.html

static void registerSkeletonPass(const PassManagerBuilder &, legacy::PassManagerBase &PM)

{

PM.add(new MyPlacementPass());

}

static RegisterStandardPasses RegisterMyPass(PassManagerBuilder::EP_EarlyAsPossible, registerSkeletonPass);

如上代码基本与前面的博客文章中的差不太多,但是底部的几句代码,那篇博客中并没有贴出来,这也是我经历了一两周摸索 ~

不加上这几句,编译出来的 dylib ,总是无法被 clang 加载执行。

并且注意如上代码是用的 LLVM 8.0.1 版本的代码,对应 LLVM github 源码仓库中的是 release_80 分支的代码。

我当时使用的是 LLVM github 源码仓库中的是 release_90 分支代码,如上代码一直编译报错,发现很多的 API 都已经变了,搜搜了很多文档才找到新的 API 使用。

这是 Pass 的毛病,因为依赖的是 libclang 库,而这个库 API 是不稳定的,经常会发生变化。

04-02. $LLVM_SOURCE/lib/Transforms/MyPlacement/CMakeLists.txt

add_llvm_library( MyPlacementPass MODULE

MyPlacementPass.cpp

)

04-03. 构建生成 Pass dylib

image.png

╭─xiongzenghui at xiongzenghui的MacBook Pro in ~/llvm-8.0.1.src/build/Debug/lib using ‹ruby-2.4.1›

╰─○ ll | grep -i 'MyPlacementPass'

-rwxr-xr-x 1 xiongzenghui staff 149K 10 10 22:52 MyPlacementPass.dylib

MyPlacementPass.dylib 这个动态库就是要使用到的 LLVM Pass。

05. 新建一个 Mac App 项目, 使用 Pass

05-01. Build Settings 中新增 CC、CXX 两项用户定义设置, 指向自己编译的 clang

image.png

05-02. Build Settings 修改 OTHER_CFLAGS 加载 pass dylib

image.png

05-03. 如果编译报错: unknown argument index ...

image.png

Build Settings 栏目中搜索 index , 然后将 Enable Index-Wihle-Building Functionality 默认值 Default 改为 NO

image.png

05-04. 运行 mac app

image.png

c语言自动插桩,01 - LLVM Pass 实现 C函数 插桩相关推荐

  1. 基于mysql的应用程序设计[j] 兰旭辉_熊家军_邓刚_高校C语言自动考试系统参考文献...

    [1]青宪,叶雪梅,陈钟,刘红. 无纸化考试系统自动组卷策略分析及改进[J]. 计算机应用,2008,28(S2):100-102. [2]王瑞金,段会川,Martin Gogolla. 统一建模语言 ...

  2. C语言【微项目01】—电话号码管理系统(文件操作实现)【2021-06-29】

    C语言[微项目01]-电话号码管理系统(文件操作实现) TelSeaSys.h TelSeaSys.c main.c 运行结果示例 [TDTX] [通过文件进行增.删.查.改, 在文件中增.删.查.改 ...

  3. Writing an LLVM Pass

    目录 1.简介 - 什么是pass? 2.快速入门 - 写下hello world 3.设置构建环境 4.Basic code required 5.Pass类和要求 6.ImmutablePass类 ...

  4. 【转载】基于LLVM Pass实现控制流平坦化

    基于LLVM Pass实现控制流平坦化 文章目录 基于LLVM Pass实现控制流平坦化 0x00. 什么是LLVM和LLVM Pass 0x01. 首先写一个能跑起来的LLVM Pass 0x02. ...

  5. JS实现聊天接收到消息语言自动提醒(您有新的消息请注意查收)

    综述 最近在开发一个网页端的客服系统,需求要求聊天双方接收到消息能有语音提醒,并且客服端如果存在未读消息要求每隔五分钟给客服语音提醒一下.客服聊天系统使用PHP的Workerman框架进行开发,由于语 ...

  6. windows上的LLVM pass瞎折腾记录

    windows上的LLVM pass瞎折腾记录 想了解下LLVM的代码风格和优化过程,就从写LLVM pass入手吧,做个踩坑记录- 文章最后有逐步骤的操作记录,Tips内记录关键点和思路 Tips: ...

  7. C语言-学习之路-01

    C语言学习之路-01 目录 关键字 数据类型 常量 变量 声明和定义 进制 sizeof关键字 整型:int short.int.long.long long 字符型:char ASCII对照表 转义 ...

  8. c语言自动转化,C语言编程之自动类型转化

    咱们在写程序的时候经常会遇到一些不好找的bug,有的并不是很难,只是大家容易忽略,今天咱们就来看一个,关于C语言自动类型转换的bug. 先看一段代码: void getNext(int * next, ...

  9. gocode+auto-complete搭建emacs的go语言自动补全功能

    上篇随笔记录了在emacs中使用go-mode和goflymake搭建了go语言的简单编程环境(推送门),今天来记录一下使用gocode+auto-complete配置emacs中go语言的自动补全功 ...

  10. C语言学习--01 认识数据类型

    C语言学习--01 认识数据类型 #include<stdio.h> int main(){//sizeof 计算类型或者变量所占空间的大小printf("%d\n", ...

最新文章

  1. 软件开发中部分代码的注解
  2. 【linux】Shell脚本中调用另外一个脚本的方法
  3. 下载煎蛋妹子图python代码[自用]
  4. 使用标定板对相机位姿进行估计
  5. 评估服务基础性能应该参考那些指标?
  6. php文件夹转换网页,PHP转换文件夹下所有文件的编码 适合发布网站的其他编码版本...
  7. python读取数据文件、并把里面的数据变成x的二维坐标_(数据科学学习手札60)用Python实现WGS84、火星坐标系、百度坐标系、web墨卡托四种坐标相互转换...
  8. Linux cannot remove 'XXX': Is a directory 解决办法
  9. 财务系统服务器计入什么科目,系统服务费入什么科目
  10. 微信小程序发布引起轰动
  11. m3u8手机批量转码_M3U8批量转换器(Android)开发
  12. 一圆形游泳池如图所示,现在需在其周围建一圆形过道,并在其四周围上栅栏。栅栏价格为35元/米,过道造价为20元/平方米。过道宽度为3米,游泳池半径由键盘输入。要求编程计算并输出过道和栅栏的造价。
  13. 四十七、Fluent近壁面处理
  14. css如何选择相同class下的第一个class元素和最后一个元素?
  15. 机器学习复习:线性回归1
  16. spry使用视频_Spry Fox如何利用Cloud Build
  17. 太极创客的项目太乐1号的改造
  18. Linux打印相关命令
  19. iview 中 render函数与renderHeader函数
  20. R语言基础题及答案(一)——数组、数据框、list列表的创建及部分基础问题

热门文章

  1. ApiPost测试接口获取不到session
  2. 如何用ICode来学习Python
  3. 安全龙网络安全攻防实验1.2 全新功能正式上线
  4. linux合并ts文件吗,UNIX LINUX MACOS shell 下载合并*.ts视频
  5. 清空mysql数据库所有表数据_清空数据库所有表数据mysql
  6. euraka resttemplate
  7. jsp中java代码注释_JSP页面中的三种代码注释方法
  8. 02.CCNA 200-301 题库_51-100
  9. CCNA考试题库中英文翻译版及答案3
  10. oracle数据库卸载过程,oracle数据库卸载教程,Oracle卸载步骤有哪几步?