Linux 禁用valgrind&;的glibc(LD_HWCAP_MASK,/etc/LD.so.nohwcap)中的AVX优化功能;gdb记录
带有glibc的现代x86_64 linux将检测到CPU支持AVX扩展,并将许多字符串函数从通用实现切换到版本(在ifunc dispatchers的帮助下:,) 此功能可以提高性能,但它会阻止valgrind(,before)和gdb的“Linux 禁用valgrind&;的glibc(LD_HWCAP_MASK,/etc/LD.so.nohwcap)中的AVX优化功能;gdb记录,linux,linker,gdb,glibc,avx,Linux,Linker,Gdb,Glibc,Avx,带有glibc的现代x86_64 linux将检测到CPU支持AVX扩展,并将许多字符串函数从通用实现切换到版本(在ifunc dispatchers的帮助下:,) 此功能可以提高性能,但它会阻止valgrind(,before)和gdb的“目标记录””()等工具正常工作(Ubuntu“Z”17.04 beta,gdb 7.12.50.20170207-0ubuntu2,gcc 6.3.0-8ubuntu1 20170221,Ubuntu GLIBC 2.24-7ubuntu2): $cat空调
目标记录”
”()等工具正常工作(Ubuntu“Z”17.04 beta,gdb 7.12.50.20170207-0ubuntu2,gcc 6.3.0-8ubuntu1 20170221,Ubuntu GLIBC 2.24-7ubuntu2):
$cat空调
#包括
#定义N 1000
int main(){
字符src[N],dst[N];
memcpy(dst、src、N);
返回0;
}
$gcc a.c-o a-fno内置
$gdb-q./a
正在从./a..读取符号(未找到调试符号)…已完成。
(gdb)启动
0x724处的临时断点1
启动程序:/home/user/src/a
主()
(gdb)记录
(gdb)c
持续的。
过程记录不支持地址0x7ffff7b60d31处的指令0xc5。
进程记录:无法记录执行日志。
程序停止。
__memmove_avx_unaligned_erms()位于../sysdeps/x86_64/multiarch/memmove vec unaligned erms.S:416
416 VMOVU(%rsi),%VEC(4)
(gdb)x/i$pc
=>0x7ffff7b60d31:vmovdqu(%rsi),%ymm4
gdb实现“目标记录”时出现错误消息“过程记录不支持指令0xc5
”,因为记录/重放引擎不支持AVX指令(有时在\u dl\u runtime\u resolve\u AVX
函数上检测到问题):“进程记录不支持某些AVX指令”
“您可以在运行时重新编译libc(因此ld.so),或hack uu init u cpu u特性和因此uu cpu u特性(参见例如strcmp)”,或设置ld BIND u NOW=1
,但重新编译的glibc仍然有AVX,而ld BIND NOW没有帮助
我听说glibc中有/etc/ld.so.nohwcap
和ld\u HWCAP\u MASK
配置。它们可以用于禁用ifunc分派到glibc中的AVX优化字符串函数吗
glibc(rtld?)如何使用cpuid
、/proc/cpuinfo
(可能不是)或HWCAP(LD\u SHOW\u AUXV=1/bin/echo | grep HWCAP
命令给出的AT\u HWCAP:bfebfbff
)来检测AVX
我听说glibc中有/etc/ld.so.nohwcap
和ld\u HWCAP\u MASK
配置。它们可以用于禁用ifunc分派到glibc中的AVX优化字符串函数吗
是:设置LD\u HWCAP\u MASK=0将使GLIBC假装没有可用的CPU功能
将掩码设置为0可能会触发错误,您可能需要找出控制AVX的精确位,并仅屏蔽该位。不是最佳或完整的解决方案,只是一个最小的位编辑kludge以允许valgrind和gdb记录“我的任务”
:
如何在不重新编译glibc的情况下屏蔽AVX/SSE
我完全重建了未修改的glibc,这在debian和ubuntu中相当容易:只需sudo-apt-get-source-glibc
,sudo-apt-get-build-dep-glibc
和cd-glibc-*/;dpkg-buildpackage-us-uc
(以获取ld.so,而无需剥离调试信息)
然后,我对输出ld.so文件进行了二进制(位)修补,该文件位于\uuu get\u cpu\u features
使用的函数中。目标函数是以get\u common\u index.constprop.1
的名称编译而成的(它位于二进制代码中\uu get\u cpu\u features
之后)。它有几个CPUID,第一个是;然后检查“jle 0x6”并跳转代码以获取AVX2状态。有一个代码被编译成此逻辑:
get_common_indeces (struct cpu_features *cpu_features,
unsigned int *family, unsigned int *model,
unsigned int *extended_model, unsigned int *stepping)
{ ...
if (cpu_features->max_cpuid >= 7)
__cpuid_count (7, 0,
cpu_features->cpuid[COMMON_CPUID_INDEX_7].eax,
cpu_features->cpuid[COMMON_CPUID_INDEX_7].ebx,
cpu_features->cpuid[COMMON_CPUID_INDEX_7].ecx,
cpu_features->cpuid[COMMON_CPUID_INDEX_7].edx);
cpu\u功能->max\u cpuid
是在cpu\u cpuid(0,cpu\u功能->max\u cpuid,ebx,ecx,edx);
行中填写的。通过将cmp0x6
之后的jle
替换为jg
(字节0x7e到0x7f),禁用if
语句更容易。(实际上,这个二进制补丁是手动重新应用于real systemld linux.so.2的功能的\uGet\uCPU\功能的。所以.2
-在mov 7 eax;xor ecx,ecx;cpuid
之前的第一个jle更改为jg。)
系统中未安装重新编译的包和修改的ld.so;我使用了ld.so./my_程序
(或mv ld.so/some/short/path.so
和patchelf--set解释器./my_程序
)的命令行语法
其他可能的解决办法:
- 尝试使用更新的valgrind和gdb记录工具
- 尝试使用旧的glibc
- 在gdb记录中实现缺失指令模拟(如果未完成)
- 在glibc中对if(cpu\u功能->max\u cpuid>=7)
进行源代码修补并重新编译
在glibc中对启用avx2的字符串函数进行源代码修补并重新编译
似乎没有一种简单的运行时方法来修补功能检测。这种检测在动态链接器(ld.so)的早期就发生了
对链接器进行二进制修补似乎是目前最简单的方法。@osgx覆盖跳转的一种方法。另一种方法只是伪造cpuid结果。通常cpuid(eax=0)
返回eax
中支持的最高函数,而制造商ID位于寄存器ebx、ecx和edx中。我们在glibc 2.25sysdeps/x86/cpu features.c
中有此代码段:
\uuuCPUID(0,cpu功能->最大cpuid,ebx,ecx,edx);
/*这意味着“真正的英特尔”*/
如果(ebx==0x756e6547&&ecx==0x6c65746e&&edx==0x49656e69)
{
/*各种Intel CPU的功能检测*/
}
/*AMD的另一个案例*/
其他的
{
种类=拱门、种类或其他;
获取公共索引(cpu特性,NULL,NULL,NULL,NULL);
}
\uuu cpuid
行
get_common_indeces (struct cpu_features *cpu_features,
unsigned int *family, unsigned int *model,
unsigned int *extended_model, unsigned int *stepping)
{ ...
if (cpu_features->max_cpuid >= 7)
__cpuid_count (7, 0,
cpu_features->cpuid[COMMON_CPUID_INDEX_7].eax,
cpu_features->cpuid[COMMON_CPUID_INDEX_7].ebx,
cpu_features->cpuid[COMMON_CPUID_INDEX_7].ecx,
cpu_features->cpuid[COMMON_CPUID_INDEX_7].edx);
172a8: 31 c0 xor eax,eax
172aa: c7 44 24 38 00 00 00 mov DWORD PTR [rsp+0x38],0x0
172b1: 00
172b2: c7 44 24 3c 00 00 00 mov DWORD PTR [rsp+0x3c],0x0
172b9: 00
172ba: 0f a2 cpuid
$ gdb -q -ex "set exec-wrapper ./ld-linux-x86-64-patched.so.2" -ex start ./a
Reading symbols from ./a...done.
Temporary breakpoint 1 at 0x400502: file a.c, line 5.
Starting program: /tmp/a
During startup program exited normally.
(gdb) quit
$ gdb -q -ex start --args ./ld-linux-x86-64-patched.so.2 ./a
Reading symbols from ./ld-linux-x86-64-patched.so.2...(no debugging symbols found)...done.
Function "main" not defined.
Temporary breakpoint 1 (main) pending.
Starting program: /tmp/ld-linux-x86-64-patched.so.2 ./a
[Inferior 1 (process 27418) exited normally]
(gdb) quit
(gdb) info shared
From To Syms Read Shared Object Library
0x00007ffff7fd3090 0x00007ffff7ff3130 Yes /lib64/ld-linux-x86-64.so.2
0x00007ffff76366b0 0x00007ffff766b52e Yes /usr/lib/x86_64-linux-gnu/libubsan.so.1
0x00007ffff746a320 0x00007ffff75d9cab Yes /lib/x86_64-linux-gnu/libc.so.6
...
(gdb) disassemble 0x7ffff75c65d4
Dump of assembler code for function __strcmp_avx2:
0x00007ffff75c65d0 <+0>: mov %edi,%eax
0x00007ffff75c65d2 <+2>: xor %edx,%edx
=> 0x00007ffff75c65d4 <+4>: vpxor %ymm7,%ymm7,%ymm7
IFUNC_IMPL (i, name, strcmp,
IFUNC_IMPL_ADD (array, i, strcmp,
HAS_ARCH_FEATURE (AVX2_Usable),
__strcmp_avx2)
IFUNC_IMPL_ADD (array, i, strcmp, HAS_CPU_FEATURE (SSE4_2),
__strcmp_sse42)
IFUNC_IMPL_ADD (array, i, strcmp, HAS_CPU_FEATURE (SSSE3),
__strcmp_ssse3)
IFUNC_IMPL_ADD (array, i, strcmp, 1, __strcmp_sse2_unaligned)
IFUNC_IMPL_ADD (array, i, strcmp, 1, __strcmp_sse2))