libc 中的 strcmp 和 strcmp_sse 函数

strcmp and strcmp_sse functions in libc

我在 libc.so 中看到要调用的 strcmp_sse 的实际类型由函数 strcmp 本身决定。

这是代码:

      strcmp:
 .text:000000000007B9F0                 cmp     cs:__cpu_features.kind, 0
 .text:000000000007B9F7                 jnz     short loc_7B9FE
 .text:000000000007B9F9                 call    __init_cpu_features
 .text:000000000007B9FE
 .text:000000000007B9FE loc_7B9FE:                              ; CODE XREF: .text:000000000007B9F7j
 .text:000000000007B9FE                 lea     rax, __strcmp_sse2_unaligned
 .text:000000000007BA05                 test    cs:__cpu_features.cpuid._eax, 10h
 .text:000000000007BA0F                 jnz     short locret_7BA2B
 .text:000000000007BA11                 lea     rax, __strcmp_ssse3
 .text:000000000007BA18                 test    cs:__cpu_features.cpuid._ecx, 200h
 .text:000000000007BA22                 jnz     short locret_7BA2B
 .text:000000000007BA24                 lea     rax, __strcmp_sse2
 .text:000000000007BA2B
 .text:000000000007BA2B locret_7BA2B:                           ; CODE XREF: .text:000000000007BA0Fj
 .text:000000000007BA2B                                         ; .text:000000000007BA22j
 .text:000000000007BA2B                 retn

我不明白的是strcmp_sse调用函数的地址是放在rax里面的,从来没有真正调用过。因此我想知道:谁会调用 *rax?什么时候?

Linux 动态链接器支持一种名为 STT_GNU_IFUNC 的特殊符号类型。 Strcmp 可能作为 IFUNC 实现。 'Regular' 动态库中的符号只不过是从名称到地址的映射。 IFUNC 比这复杂一点:地址不容易获得,为了获得它,链接器必须从库本身执行一段代码。我们在这里看到了这样一段代码的示例。请注意,在 x86_64 ABI 中,函数 returns RAX 中的结果。

此技术通常用于根据 CPU 特征选择最佳实现。请注意,选择逻辑只运行一次;除了对 strcmp 的第一次调用外,所有调用都很快。