看懂编译原理:看懂 JIT & AOT

2023-12-08 10:09:46 浏览数 (3)

性质区别

aot在运行前就已经编译成了机器码可以直接运行

而jit由于不是目标机器语言因此需要虚拟机帮忙做转换工作。

目标文件的生成区别

  • 在JIT编译中,由于没有目标文件,所以编译器需要动态得生成目标文件。

在生成目标文件时,编译器会将代码区的内存页保护措施临时关闭,以便将代码写入到代码区并设置可执行文件权限。

  • 在AOT编译中,因为目标文件是预先编译好的,因此不需要在运行时动态生成。

内存页申请内存(提前计算好需要多大内存空间),内存页权限修改为可写代码加载到内存中设置可执行权限,然后关闭内存页写的权限。这样之后使用一个指针指向这块内存地址就可以运行这个动态的代码了。

机器运行效率区别

由于jit可以在运行时拿到具体的目标机器信息,因此优化的内容会更多范围会更广

aot不知道目标机器信息因此这部分得不到很好的机器优化

技术原理区别

AOT

由于aot已经编译成了机器码直接可以运行,所以代码可以直接加载到内存中 的代码区域,操作系统会给这块区域可执行权限让其运行

操作系统会给内存的每个区域都设置权限,对于链接的库来说也会进行编译并生成符号表设置到使用的模块中

使用:模块--》符号表中链接的地址--》真实的内存地址

jit编译后的机器码是在哪存放的?如何运行的?

jit的加载过程和链接过程和aot不同:由于是运行时编译,因此代码区需要动态申请一块内存然后把指令放到这块内存中,在通过一个指针指向这块内存调用这个指针,文末图就行这个过程

如何支持jit编译代码的链接过程?

编译器生成的二进制代码支持重定向,在aot静态编译时由于编译的时候就可以知道引用库里面的符号表信息,因此可以在编译阶段的时候 就进行重定向:重定向指向对应引用库里面的符号信息

对于jit来说,由于是动态加载的代码,所以链接过程也要在运行时动态去做:

引入的库函数中用到的变量,方法的地址放到符号表中运行的时候通过符号表找到模块对应的内存地址

动态链接技术

运行的时候进行重定向使用位置无关pic的技术,让代码加载到不同的进城中,这样不同进城最终引用的是不同的地址。这样为了安全性(固定的地址很容易进行攻击)

为什么要先进行映射到不同地址空间在进行重定向?(安全因素,防止对共享的模块进行攻击)

llvm的动态链接实现(文末图展示流程)

官方定义:

LLVM支持动态链接和地址无关性技术,使得编译生成的代码可以在运行时加载和链接所需的共享库,并解析符号引用。

在LLVM的JIT编译中,所有模块的IR都是先被加载到内存中,然后根据需要动态链接和重定向。 **这种设计可以使得JIT编译的过程更加高效,同时也提供了更好的灵活性,可以在_运行时进行动态的符号解析和链接。**_

每个模块都已经编译成IR存储在内存中,且只需要一次加载统计符号表的过程: **模块_里面的变量方法这些在编译的时候都会存放到符号表中**_。

如果jit生成的代码需要访问这些模块,这些模块会映射到不同的进程空间,然后根据需要在进行映射到对应进城地址空间完成重定向。

利用内存溢出攻击计算机,一段内存是有可执行权限的话,只要黑客写的代码溢出到这块区域,就可以执行这些代码攻击。因此内存w的权限要谨慎设置。

符号表的链接区别

  • 代码需要链接操作,但是在JIT编译中,此时还不知道三方库的符号表信息。所以JIT编译器会使用动态链接器(Dynamic Linker)来进行符号解析和链接:动态链接器可以根据需要生成重定位表(Relocation Table),记录需要进行符号重定位的信息;之后等待所有模块编译完之后再根据重定位表中的信息进行一个个替换为真正的符号链接。
  • AOT编译中,编译器会在编译阶段进行符号表的链接。 **对于_三方库的符号,编译器会在符号表中做特殊标记,并在链接阶段进行符号的重定向和定位。_ **因此,在AOT编译中,不需要在运行时使用动态链接器进行符号解析和链接。

JIT和AOT在编译细节上存在一些区别。JIT需要动态生成目标文件并使用动态链接器进行符号解析和链接,而AOT则是在编译阶段进行符号表的链接,不需要在运行时进行动态生成和链接。

我正在参与2023腾讯技术创作特训营第四期有奖征文,快来和我瓜分大奖!

0 人点赞