记一次pfring在目标机器上出现Illegal instruction.的修复过程
目录
- 背景
- bug定位
- CPU指令集查看
- 重新编译pfring
- 指令集查看
- 总结
背景
项目中使用了pfring作为流量接收的底层库,遂通过将源码编译成so,并链接可程序程序suricata,并发布到目标机器上进行运行,程序能够正确运行;最近,公司有同事通过虚拟机安装了程序,但是运行后提示了Program received signal SIGILL, Illegal instruction.导致程序无法正常运行,笔者作为底层码农,遂开始进行定位及修复。
bug定位
通过gdb查看程序退出时的调用栈
通过调用栈的最后输出,看上去应该是pfring内部使用了机器不兼容的指令,导致的问题。
CPU指令集查看
因为出现的是Illegal instruction.第一反应应该就是指令集不兼容。所以通过对比编译机器和运行机器的指令集来确认,是否存在指令集不兼容的情况。
通过使用lscpu查看两台机器的指令集情况如下:
编译机器执行情况如下
[root@localhost userland]# lscpu
Architecture: x86_64
CPU op-mode(s): 32-bit, 64-bit
Byte Order: Little Endian
CPU(s): 8
On-line CPU(s) list: 0-7
Thread(s) per core: 1
Core(s) per socket: 2
Socket(s): 4
NUMA node(s): 1
Vendor ID: GenuineIntel
CPU family: 6
Model: 151
Model name: 12th Gen Intel(R) Core(TM) i5-12400F
Stepping: 5
CPU MHz: 2496.001
BogoMIPS: 4992.00
Hypervisor vendor: VMware
Virtualization type: full
L1d cache: 48K
L1i cache: 32K
L2 cache: 1280K
L3 cache: 18432K
NUMA node0 CPU(s): 0-7
Flags: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr ssesse2 ss ht syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon rep_good nopl xtopology tsc_reliable nonstop_tsc eagerfpu pni pclmulqdq ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm abm 3dnowprefetch invpcid_single ssbd ibrs ibpb stibp ibrs_enhanced fsgsbase tsc_adjust bmi1 avx2 smep bmi2 erms invpcid rdseed adx smap clflushopt clwb sha_ni xsaveopt xsavec xgetbv1 arat umip pku ospke gfni vaes vpclmulqdq movdiri movdir64b md_clear spec_ctrl intel_stibp flush_l1d arch_capabilities
运行机器运行情况如下:
[root@localhost bin]# lscpu
Architecture: x86_64
CPU op-mode(s): 32-bit, 64-bit
Byte Order: Little Endian
CPU(s): 16
On-line CPU(s) list: 0-15
Thread(s) per core: 1
Core(s) per socket: 1
座: 16
NUMA 节点: 2
厂商 ID: GenuineIntel
CPU 系列: 6
型号: 62
型号名称: Intel(R) Xeon(R) CPU E5-2660 v2 @ 2.20GHz
步进: 4
CPU MHz: 2200.000
BogoMIPS: 4400.00
超管理器厂商: VMware
虚拟化类型: 完全
L1d 缓存: 32K
L1i 缓存: 32K
L2 缓存: 256K
L3 缓存: 25600K
NUMA 节点0 CPU: 0-7
NUMA 节点1 CPU: 8-15
Flags: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflushdts mmx fxsr sse sse2 ss syscall nx rdtscp lm constant_tsc arch_perfmon pebs bts nopl xtopology tsc_reliable nonstop_tsc eagerfpu pni pclmulqdq ssse3 cx16 pcid sse4_1 sse4_2 x2apic popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm ssbd ibrs ibpb stibp fsgsbase tsc_adjust smep arat md_clear spec_ctrl intel_stibp flush_l1d arch_capabilities
从指令情况来看,其实未能马上发现问题;但是之前编译过程中有接触过avx2指令集,正好发现编译机器上有avx2指令集,而目标机器上没有这个指令集。所以怀疑是因为这个原因引起的。接下来就是验证过程。
重新编译pfring
因为指令集不兼容,那就在目标机器上安装gcc而后重新进行编译,因为目标机器是公司内部虚拟机,可以直接在机器上安装编译器;如果是实际运行环境不能安装编译器(可能需要通过修改编译选项的方式进行);将pfring的源码上传到目标机器,而后进行编译,编译完成之后,替换原来安装pfring.so后,再运行suricata;发现能够正常运行。通过此方法已经顺利解决了pfring运行指令集不兼容的问题;通过重新编译的方式确实解决了问题,但是还未能确定是否确实是因为虚拟机不支持avx2导致的问题;遂考虑通过对比两个机器上的libpfring.so实际的指令的异同来确定是否是因为avx2的指令集的问题。
指令集查看
查看so使用了哪些指令,可以通过命令objdump -d的方式查看指令,命令如下:
objdump -d libpfring.so.7
命令执行结果如下:
图中间这一列即为libpfring.so使用的指令。为了获取libpfring.so中使用的所有指令,首先通过vim块编辑模式,将指令前的地址信息都删除,同时借助awk,grep,sort,uniq等命令,获得libpfring.so使用的所有汇编指令:
编译机器的汇编指令如下:
adcl add addl addq addw and andb andl andn bswap bt callq cld cltd cltq cmova cmovae cmovbe cmove cmovg cmovge cmovl cmovle cmovne cmovns cmovs cmp cmpb cmpl cmpq cmpw cpuid cqto cwtl data16 div divl divq idiv imul inc ja jae jb jbe je jg jge jl jle jmp jmpq jne jns js lea leaveq lfence lzcnt mfence mov movabs movaps movb movbe movdqu movl movq movsbl movsbq movsbw movslq movswl movswq movw movzbl movzwl mul mull mulq neg nop nopl nopw not notl or orb orl pop prefetcht0 push pushq rep repz retq rol ror rorx sar sarx sbb seta setae setb setbe sete setg setge setle setne sfence shl shlx shr shrx sub subl subq subw test testb testw tzcnt vaddsd vbroadcastss vcvtsi2sd vcvttsd2si vextracti128 vinserti128 vmovd vmovdqa vmovdqu vmovsd vmulsd vpaddd vpaddq vpand vperm2i128 vpermq vpinsrd vpmovzxbw vpmovzxwd vpmulld vpor vpshufb vpslld vpsrld vpunpckhqdq vpunpcklqdq vpxor vsubsd vucomisd vxorpd vzeroupper xchg xor
目标机器的汇编指令如下:
adcl add addl addq addsd addw and andb andl bsr bswap bt callq cld cltd cltq cmova cmovae cmovbe cmove cmovg cmovge cmovl cmovle cmovne cmovns cmovs cmp cmpb cmpl cmpq cmpw cpuid cqto cvtsi2sd cvttsd2si cwtl data16 div divl divq idiv imul inc ja jae jb jbe je jg jge jl jle jmp jmpq jne jns js lea lfence mfence mov movabs movapd movaps movb movd movdqa movdqu movl movq movsbl movsbq movsbw movsd movslq movswl movswq movw movzbl movzwl mul mull mulq mulsd neg nop nopl nopw not notl or orb orl paddd paddq pand pmuludq pop por prefetcht0 pshufd pslld psrld psrlq punpckhbw punpckhqdq punpckhwd punpcklbw punpckldq punpcklqdq punpcklwd push pushq pxor rep repz retq rol ror sar sbb seta setae setb setbe sete setg setge setle setne sfence shl shr sub subl subq subsd subw test testb testw tzcnt ucomisd xchg xor xorpd
使用Beyond Compare进行比较
发现多出了很多指令
vaddsd vbroadcastss vcvtsi2sd vcvttsd2si vextracti128 vinserti128 vmovd vmovdqa vmovdqu vmovsd vmulsd vpaddd vpaddq vpand vperm2i128 vpermq vpinsrd vpmovzxbw vpmovzxwd vpmulld vpor vpshufb vpslld vpsrld vpunpckhqdq vpunpcklqdq vpxor vsubsd
挑了其中vextracti128 进行查询,发现其果然为avx2指令集的一员;所以可以断定,之前问题的产生是因为指令集不兼容,同时不兼容的指令集即为avx2.
总结
解决过程其实比较直观,指令集不兼容就通过重新编译,找到适合的指令集即可;不过为了验证是否为avx2指令集的问题,通过学习可通过objdump -d命令获取动态库/可执行程序最终的执行命令,对后续验证应该使用哪个指令集进行编译提供了有效途径。