分类 汇编 下的文章

最近因为项目需要,去看了一下libsodium库的实现,发现里面各种宏定义骚操作真是肯尼迪坐敞篷车,脑洞大开啊。

其中负责加解密一块的逻辑使用了硬件指令集来加速,用到了AES专用指令集一类的。作为专用硬件指令集,调用方法之一自然是在代码里写__asm__了,然而这样会让代码显得很丑,逻辑也不够清晰。于是另一种方法就是封装,反正原理大家都懂,调用的时候做成黑盒就好看多了,于是就有了各种XXXintrin.h,封装了从MMX、SSE直到AVX等SIMD指令集的C语言接口函数。

然而大家似乎并不关心这些函数的命名规则,亦或者说是这个领域的工作者足够细心严谨,所有人都是先读完intel的手册再开始干活的,以至于当我习惯性地试图从函数名推断出这个函数的功能或者数据特征的时候,一脸茫然= =

根据 牙膏厂的官方文档 所说,这些过渡函数的命名是三段式的:

Most intrinsic names use the following notational convention: _mm_<intrin_op>_<suffix>

第一段是固定的_mm,然而intel并没有解释这个mm是什么含义,我个人猜测是同MMX中的Multi-Media,作为一种历史习惯遗留下来;第二段是函数对应汇编指令的功能;而第三段表示的是该汇编指令接收的数据格式与类型,进一步由两部分组成。

以文中提到的_mm_set_pd为例,该函数的作用是从内存中加载两个双精度浮点数据到128位的SIMD寄存器。同样地,以AES-NI指令集的核心指令之一AESENC来说,它的C形式是_mm_aesenc_si128,表示该函数对应的指令为AES一轮加密(_aesenc),传入的数据应该是一个完整(_s)的128位整型数据(i128)。

然而,AVX系列的函数可能并不完全遵循如上规则。比如说涉及数据类型转换的_mm256_castps256_ps128,功能是将一组256位单精度浮点转换为一组128位单精度浮点数(看描述,似乎就是舍弃了高128位?),该函数的第三段_ps128代表的不再是输入数据格式,而是输出数据格式了。

类似地,AVX512的函数前缀就变成了_mm512,应该就是和对应的数据宽度为512相关联吧。

最近看到rpcs3中用到了asmjit,就去其官方项目上看了一下,根据描述是个即时生成目标平台汇编代码的库,但是看了一下样例之后发现有些东西还是没法完全理解。其中之一就是ret指令。在样例代码中是这么写的:

  ...
  x86::Assembler a(&code);
  a.mov(x86::eax, 2);
  a.add(x86::eax, -3);
  a.ret();
  ...

然后就想,既然Assembler模拟的是汇编指令,那么ret指令返回EAX的值会不会是某种约定呢? 然后打算查阅Intel的汇编指令手册,官方名叫Intel® 64 and IA-32 Architectures Software Developer’s Manual,里面有对指令的详细介绍。看到ret指令对应的伪代码有整整几页,有点慌啊。。 其中有这么几句:

IF top 4 bytes of stack not within stack limits
    THEN #SS(0); FI;
EIP ← Pop();

当时就对这个#SS(0);有点迷惑。然后在谷歌上查到了相似的使用方法,但是仍然没看到正式的定义。但是在一个网站上可能看到了类似的定义(http://www.scs.stanford.edu/05au-cs240c/lab/i386/RET.htm,惊了,是斯坦福):

Protected Mode Exceptions #GP, #NP, or #SS, as described under "Operation" above; #PF(fault-code) for a page fault

然后便开始猜测#<abbr>(<val>)的用法为带返回码的异常抛出:其中PF对应page fault的话,根据搜索,SS对应的为stack smashing,即栈溢出;GP对应general peotection,该机制旨在确保程序访问有效的地址区域;但是NP这个not present就有点搞不懂了。。似乎是指什么不存在?

虽然还是有对不上的。。但是大部分意义都是可以对上的,因而认为猜测正确。

总而言之,这个格式的意义是明白了:Intel使用的伪代码中的异常抛出语句。