是否存在与Linux内核的alternative()宏等效的用户空间?

时间:2017-08-30 21:44:16

标签: linux performance x86 instrumentation

Linux内核有alternative() macro,允许开发人员为一系列代码指定多个实现,并在运行时选择使用的特定替代方法(在引导过程的早期在内核alternative()宏的情况下。)

是否有合理的方法为用户模式应用程序实现类似的功能?特别是,记录备用位置和运行时修补代码。

对于半静态检测可能很有用:可以在运行时启用或禁用的检测,但仍然可以编译进入"仅限于特定地点。

1 个答案:

答案 0 :(得分:3)

如果您使用的是针对Linux的英特尔icc编译器,则可以使用__notify_intrinsic功能来允许添加修补。也就是说,它是一种允许您安全地挂钩现有函数并添加其他行为的方法 - 与alternative()略有不同,后者侧重于在两个序列之间进行选择。

__notify_intrinsic确保在您放置内在函数的代码中的位置存在探测就绪指令序列。这允许您注入"探测" (即使代码正在执行时),以安全的方式在那一点(实际上可以是任意代码)。

原则上,这可能只有零开销:探测就绪序列只是一个6个指令字节 1 的序列,您可以安全地重新定位到其他地方:主要是指6个字节在指令边界上均匀对齐,不具有位置相关代码 2 ,并且不会跳转到该范围内。

这个想法是你可以使用这个探测就绪序列来修改运行时的行为,方法是将字节复制到别处,然后复制新代码,然后用jmp指令修改探测序列到你的外面在线"探测" (但探测器可以是任何东西,真的)。

实际上,icc版本13到17似乎只是插入一个6字节的NOP而不是实际尝试使用现有的指令。作为example,以下C代码:

int add(int a, int b) {
  if (a < b) {
    __notify_intrinsic("a lt b", 0);
    a *= b;
    return a * b;
  } else {
    __notify_intrinsic("a gt b", 0);
    return a << b;
  }
}

生成此程序集:

add:
        cmp       edi, esi                                      #2.11
        jge       ..B1.4        # Prob 50%                      #2.11
        xor       edx, edx                                      #3.5
        .byte     102                                           #
        .byte     15                                            #
        .byte     31                                            #
        .byte     68                                            #
        .byte     0                                             #
        .byte     0                                             #
        imul      edi, esi                                      #4.5
        imul      esi, edi                                      #5.16
        mov       eax, esi                                      #5.16
        ret                                                     #5.16
..B1.4:                         # Preds ..B1.1
        xor       edx, edx                                      #7.5
        .byte     102                                           #
        .byte     15                                            #
        .byte     31                                            #
        .byte     68                                            #
        .byte     0                                             #
        .byte     0                                             #
        mov       ecx, esi                                      #8.17
        shl       edi, cl                                       #8.17
        mov       eax, edi                                      #8.17
        ret   

6个.byte指令系列产生一个6字节长的nop指令。

编译器在ELF二进制文件的特殊__notify_intrinsic部分中记录每个.itt_not_tab的位置和其他信息,例如注释的值,您可以在运行时检查它。

您可以找到有关此方法的更多详细信息in the documentation,但不幸的是,它目前仅限于英特尔的专有icc编译器。

1 它在32位x86上有5个字节。

2 例如,相对跳跃或rip - 相对寻址。