JIT 即时编译的原理

什么是 JIT

名如其特点,JIT —— just in time,即时编译。

什么?这就是你要告诉大家伙的?这不是人人都知道的嘛?而且网上一搜也全都是 JIT = just in time 了事。好吧好吧,我知错啦。那就认真的定义一下JIT:

一个程序在它运行的时候创建并且运行了全新的代码,而并非那些最初作为这个程序的一部分保存在硬盘上的固有的代码。就叫 JIT。

几个点:

  1. 程序需要运行
  2. 生成的代码是新的代码,并非作为原始程序的一部分被存在磁盘上的那些代码
  3. 不光生成代码,还要运行。

需要提醒的是第三点,也就是 JIT不光是生成新的代码,它还会运行新生成的代码。

模拟一下JIT的过程

JIT这么好,那它是如何实现既生成新代码,又能运行新代码的呢?

编译器如何生成代码很多文章都有涉及,我就不多在此着墨了。下面我就着重和各位聊聊,如何运行新生成的代码。

首先我们要知道生成的所谓机器码到底是神马东西。一行看上去只是处理几个数字的代码,蕴含着的就是机器码。

unsigned char[] macCode = {0x48, 0x8b, 0x07};

macCode对应的汇编指令就是:

mov    (%rdi),%rax

其实可以看出机器码就是比特流,所以将它加载进内存并不困难。而问题是应该如何执行。

好啦。下面我们就模拟一下执行新生成的机器码的过程。假设JIT已经为我们编译出了新的机器码,是一个求和函数的机器码:

//求和函数
long add(long num) {   return num + 1; }  

//对应的机器码
0x48, 0x83, 0xc0, 0x01, 0xc3 

首先,动态的在内存上创建函数之前,我们需要在内存上分配空间。具体到模拟动态创建函数,其实就是将对应的机器码映射到内存空间中。这里我们使用c语言做实验,利用 mmap函数 来实现这一点。

头文件:
#include <unistd.h> #include <sys/mman.h>
定义函数:
void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offsize)
函数说明:
mmap()用来将某个文件内容映射到内存中,对该内存区域的存取即是直接对该文件内容的读写。

因为我们想要把已经是 比特流的“求和函数”在内存中创建出来,同时还要运行它。所以mmap有几个参数需要注意一下。

代表映射区域的保护方式,有下列组合:

  • PROT_EXEC 映射区域可被执行;
  • PROT_READ 映射区域可被读取;
  • PROT_WRITE 映射区域可被写入;
#include<stdio.h>                                                                                            
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/mman.h>

//分配内存
void* create_space(size_t size) {
    void* ptr = mmap(0, size,
            PROT_READ | PROT_WRITE | PROT_EXEC,
            MAP_PRIVATE | MAP_ANON,
            -1, 0);   
    return ptr;
}

这样我们就获得了一块分配给我们存放代码的空间。下一步就是实现一个方法将机器码,也就是比特流拷贝到分配给我们的那块空间上去。使用 memcpy 即可。

//在内存中创建函数
void copy_code_2_space(unsigned char* m) {
    unsigned char macCode[] = {
        0x48, 0x83, 0xc0, 0x01,
        c3 
    };
    memcpy(m, macCode, sizeof(macCode));
}

然后我们在写一个main函数来处理整个逻辑:

#include<stdio.h>                                                                                            
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/mman.h>

//分配内存
void* create_space(size_t size) {
    void* ptr = mmap(0, size,
            PROT_READ | PROT_WRITE | PROT_EXEC,
            MAP_PRIVATE | MAP_ANON,
            -1, 0);   
    return ptr;
}

//在内存中创建函数
void copy_code_2_space(unsigned char* addr) {
    unsigned char macCode[] = {
        0x48, 0x83, 0xc0, 0x01,
        0xc3 
    };
    memcpy(addr, macCode, sizeof(macCode));
}

//main 声明一个函数指针TestFun用来指向我们的求和函数在内存中的地址
int main(int argc, char** argv) {                                                                                              
    const size_t SIZE = 1024;
    typedef long (*TestFun)(long);
    void* addr = create_space(SIZE);
    copy_code_2_space(addr);
    TestFun test = addr;
    int result = test(1);
    printf("result = %d\n", result); 
    return 0;
}

编译运行一下看下结果:

//编译
gcc testFun.c
//运行
./a.out 1 
image

为什么iOS不能使用JIT?

OK,到此为止。这个例子模拟了动态代码在内存上的生成,和之后的运行。似乎没有什么问题呀?可不知道各位是否忽略了一个前提?那就是我们为这块区域设置的保护模式可是:可读,可写,可执行的啊!如果没有内存可读写可执行的权限,我们的实验还能成功吗?

让我们把create_space函数中的“可执行”PROT_EXEC权限去掉,看看结果会是怎样的一番景象。

修改代码,同时将刚才生成的可执行文件a.out删除重新生成运行。

rm a.out vim testFun.c gcc testFun.c ./a.out 1

结果。。。报错了!

image

所以,IOS并非把JIT禁止了。或者换个句式讲,IOS封了内存(或者堆)的可执行权限,相当于变相的封锁了JIT这种编译方式。

原因呢?为什么iOS要这么做?且听下回分解~~~~~IOS和安全漏洞的赌注

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,384评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,845评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,148评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,640评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,731评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,712评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,703评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,473评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,915评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,227评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,384评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,063评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,706评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,302评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,531评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,321评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,248评论 2 352

推荐阅读更多精彩内容

  • 原文:Hello, JIT World: The Joy of Simple JITs标签(空格分隔):jit 编...
    prostory阅读 2,394评论 0 5
  • 如果你看完书中的所有例子,你很可能已经做完你的实验和在已经越狱的iPhone上的研究。因为和许多人一样,几乎所有的...
    fishmai0阅读 15,956评论 2 42
  • (一) 一山榴红满城香, 金秋挂果丰收忙。 八方游客聚福地, 人人喜摘石榴王。 《榴园赏景》 (二) 青罗...
    相山雨晨阅读 376评论 0 13
  • 世界不会听你说话。
    狄金森阅读 182评论 0 0
  • 12歲的時候覺得14歲是個很「老」的年紀,那時我是初中。現在18歲了,大學並不是老師口中的象牙塔,我很懷念12歲,...
    Sweeky阅读 203评论 2 0