c语言自动插桩,01 - LLVM Pass 实现 C函数 插桩
网上我就搜到这一篇文章介绍使用 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函数 插桩相关推荐
- 基于mysql的应用程序设计[j] 兰旭辉_熊家军_邓刚_高校C语言自动考试系统参考文献...
[1]青宪,叶雪梅,陈钟,刘红. 无纸化考试系统自动组卷策略分析及改进[J]. 计算机应用,2008,28(S2):100-102. [2]王瑞金,段会川,Martin Gogolla. 统一建模语言 ...
- C语言【微项目01】—电话号码管理系统(文件操作实现)【2021-06-29】
C语言[微项目01]-电话号码管理系统(文件操作实现) TelSeaSys.h TelSeaSys.c main.c 运行结果示例 [TDTX] [通过文件进行增.删.查.改, 在文件中增.删.查.改 ...
- Writing an LLVM Pass
目录 1.简介 - 什么是pass? 2.快速入门 - 写下hello world 3.设置构建环境 4.Basic code required 5.Pass类和要求 6.ImmutablePass类 ...
- 【转载】基于LLVM Pass实现控制流平坦化
基于LLVM Pass实现控制流平坦化 文章目录 基于LLVM Pass实现控制流平坦化 0x00. 什么是LLVM和LLVM Pass 0x01. 首先写一个能跑起来的LLVM Pass 0x02. ...
- JS实现聊天接收到消息语言自动提醒(您有新的消息请注意查收)
综述 最近在开发一个网页端的客服系统,需求要求聊天双方接收到消息能有语音提醒,并且客服端如果存在未读消息要求每隔五分钟给客服语音提醒一下.客服聊天系统使用PHP的Workerman框架进行开发,由于语 ...
- windows上的LLVM pass瞎折腾记录
windows上的LLVM pass瞎折腾记录 想了解下LLVM的代码风格和优化过程,就从写LLVM pass入手吧,做个踩坑记录- 文章最后有逐步骤的操作记录,Tips内记录关键点和思路 Tips: ...
- C语言-学习之路-01
C语言学习之路-01 目录 关键字 数据类型 常量 变量 声明和定义 进制 sizeof关键字 整型:int short.int.long.long long 字符型:char ASCII对照表 转义 ...
- c语言自动转化,C语言编程之自动类型转化
咱们在写程序的时候经常会遇到一些不好找的bug,有的并不是很难,只是大家容易忽略,今天咱们就来看一个,关于C语言自动类型转换的bug. 先看一段代码: void getNext(int * next, ...
- gocode+auto-complete搭建emacs的go语言自动补全功能
上篇随笔记录了在emacs中使用go-mode和goflymake搭建了go语言的简单编程环境(推送门),今天来记录一下使用gocode+auto-complete配置emacs中go语言的自动补全功 ...
- C语言学习--01 认识数据类型
C语言学习--01 认识数据类型 #include<stdio.h> int main(){//sizeof 计算类型或者变量所占空间的大小printf("%d\n", ...
最新文章
- 软件开发中部分代码的注解
- 【linux】Shell脚本中调用另外一个脚本的方法
- 下载煎蛋妹子图python代码[自用]
- 使用标定板对相机位姿进行估计
- 评估服务基础性能应该参考那些指标?
- php文件夹转换网页,PHP转换文件夹下所有文件的编码 适合发布网站的其他编码版本...
- python读取数据文件、并把里面的数据变成x的二维坐标_(数据科学学习手札60)用Python实现WGS84、火星坐标系、百度坐标系、web墨卡托四种坐标相互转换...
- Linux cannot remove 'XXX': Is a directory 解决办法
- 财务系统服务器计入什么科目,系统服务费入什么科目
- 微信小程序发布引起轰动
- m3u8手机批量转码_M3U8批量转换器(Android)开发
- 一圆形游泳池如图所示,现在需在其周围建一圆形过道,并在其四周围上栅栏。栅栏价格为35元/米,过道造价为20元/平方米。过道宽度为3米,游泳池半径由键盘输入。要求编程计算并输出过道和栅栏的造价。
- 四十七、Fluent近壁面处理
- css如何选择相同class下的第一个class元素和最后一个元素?
- 机器学习复习:线性回归1
- spry使用视频_Spry Fox如何利用Cloud Build
- 太极创客的项目太乐1号的改造
- Linux打印相关命令
- iview 中 render函数与renderHeader函数
- R语言基础题及答案(一)——数组、数据框、list列表的创建及部分基础问题
热门文章
- ApiPost测试接口获取不到session
- 如何用ICode来学习Python
- 安全龙网络安全攻防实验1.2 全新功能正式上线
- linux合并ts文件吗,UNIX LINUX MACOS shell 下载合并*.ts视频
- 清空mysql数据库所有表数据_清空数据库所有表数据mysql
- euraka resttemplate
- jsp中java代码注释_JSP页面中的三种代码注释方法
- 02.CCNA 200-301 题库_51-100
- CCNA考试题库中英文翻译版及答案3
- oracle数据库卸载过程,oracle数据库卸载教程,Oracle卸载步骤有哪几步?