环境
MinGW32-gcc-g++(6.3.0-1),系统 = win10专业版, AMD Ryzen 3 2200G with Radeon Vega Graphics 3.50 GHz
C代码
就是这段代码,用来计算一个数组里面、有效编码的长度,这个编码以 bit1 结尾。
例如这串编码的长度为74: 00011010101010010010110000000010000110000000011001001100000011000000110111000000000000000000000,最后一个 bit1 后面的所有的 bit0 都不是编码的组成部分。
int HuffmanCode_stream_len(unsigned int HufCode[], int HufBufLen)
{
int codeLen = 0;
char typeLen = 0;
unsigned int data;
if(HufCode == NULL) { return 0; }
// 找到最后一个不为 0 的字节,这就是编码的最后一个字节
while(HufCode[--HufBufLen] == 0)
{
continue;
}
codeLen = HufBufLen * sizeof(int) * 8;
// 得到最后一个字节的编码长度
data = HufCode[HufBufLen];
while(data != 0)
{
codeLen++;
data <<= 1;
}
return codeLen;
}
void main(void)
{
unsigned int code[5] = { 0x1aa92c02, 0x18064c06, 0xdc00000 };
int len = stream_len(code, 5);
printf("Huffman Code len = %d\r\n", len);
}
正常情况
平时运行的结果都正常、输出 74 :
不论是全速运行、还是单步进入函数内部运行,结果都是 74 。
异常情况
今天白天运行也是好的,晚上就出现了异常结果,得到 46 。
于是我单步进入这个函数,发现传入参数都不对了:
每单步一次, HufBufLen 的值都会减 1 ,非常奇怪。
特意去看了数组里面多余的两个字节,都是 0 ,没问题:
继续看、发现 HufBufLen = 2 时、codeLen = HufBufLen * sizeof(int) * 8; 的计算结果居然是 32 ,而不是 64 (这个忘记截图了)。
原因应该就是每单步一次、 HufBufLen 的值都会减 1 。
反汇编
在运行异常的时候, debug 过程中查看了反汇编、发现除了后半部分有不解,并没有发现变量有被异常修改的地方。
[New Thread 12940.0x14c8]
=memory-changed,thread-group="i1",addr="0x0062fef4",len="0x4"
-exec disassemble /m
Dump of assembler code for function stream_len:
19 {
0x00401460 <+0>: push %ebp ; 将上一个函数的栈帧指针压栈
0x00401461 <+1>: mov %esp,%ebp ; esp -> ebp ,ebp 是栈顶指针(表明当前栈帧的起始位置),这里将这个函数(线程)的栈顶指针赋值为当前的栈指针、也就是这个函数的栈从最新的栈指针位置开始
0x00401463 <+3>: sub $0x10,%esp ; esp = esp - 16, 栈指针下移 16 字节
20 int codeLen = 0;
=> 0x00401466 <+6>: movl $0x0,-0x4(%ebp) ; 第一个变量定义在 ebp - 4 的位置
21 char typeLen = 0;
0x0040146d <+13>: movb $0x0,-0x9(%ebp) ; 第二个变量定义在 ebp - 9 的位置
22 unsigned int data;
23 if(HufCode == NULL) { return 0; }
0x00401471 <+17>: cmpl $0x0,0x8(%ebp) ; 0x8(%ebp) = 0x8(%ebp) - 0 ,HufCode @ 0x8(%ebp)
0x00401475 <+21>: jne 0x40147f <stream_len+31> ; 非零跳转
0x00401477 <+23>: mov $0x0,%eax
0x0040147c <+28>: jmp 0x4014c7 <stream_len+103> ; 跳转到返回代码
24
25 // 找到最后一个不为 0 的字节,这就是编码的最后一个字节
26 while(HufCode[--HufBufLen] == 0)
0x0040147f <+31>: subl $0x1,0xc(%ebp) ; 0xc(%ebp) -= 1 , HufBufLen -= 1, HufBufLen @ 0xc(%ebp)
0x00401483 <+35>: mov 0xc(%ebp),%eax ; %eax = HufBufLen
0x00401486 <+38>: lea 0x0(,%eax,4),%edx ; %edx = 0 + %eax * 4 = HufBufLen * 4 , (disp(base,index,scale)格式,得到 base + scale * index + disp)
0x0040148d <+45>: mov 0x8(%ebp),%eax ; %eax = 0x8(%ebp) = HufCode
0x00401490 <+48>: add %edx,%eax ; %eax = %eax + %edx ,这里得到 &HufCode[HufBufLen] ,即 HufCode[HufBufLen] 的地址
0x00401492 <+50>: mov (%eax),%eax ; %eax = (%eax) = HufCode[HufBufLen] ,这是间接寻址
0x00401494 <+52>: test %eax,%eax ; 按位与,即 %eax & %eax
0x00401496 <+54>: je 0x40147e <stream_len+30> ; 和上一条 test 指令联合使用, test 的结果为 0 则跳转,相当于 while(HufCode[--HufBufLen] == 0) 循环继续
27 {
28 continue;
0x0040147e <+30>: nop
29 }
30 codeLen = HufBufLen * sizeof(int) * 8;
0x00401498 <+56>: mov 0xc(%ebp),%eax ; %eax = 0xc(%ebp) = HufBufLen
0x0040149b <+59>: shl $0x5,%eax ; %eax <<= 5 ,相当于 %eax *= 32
0x0040149e <+62>: mov %eax,-0x4(%ebp) ; -0x4(%ebp) = %eax ,即 codeLen = %eax
31 // 得到最后一个字节的编码长度
32 data = HufCode[HufBufLen];
0x004014a1 <+65>: mov 0xc(%ebp),%eax ; %eax = 0xc(%ebp) = HufBufLen
0x004014a4 <+68>: lea 0x0(,%eax,4),%edx ;
0x004014ab <+75>: mov 0x8(%ebp),%eax ;
0x004014ae <+78>: add %edx,%eax ;
0x004014b0 <+80>: mov (%eax),%eax ; %eax = (%eax) = HufCode[HufBufLen] ,这是间接寻址
0x004014b2 <+82>: mov %eax,-0x8(%ebp) ; -0x8(%ebp) = %eax ,相当于 data = HufCode[HufBufLen] , data @ -0x8(%ebp)
33 while(data != 0)
0x004014b5 <+85>: jmp 0x4014be <stream_len+94>
0x004014be <+94>: cmpl $0x0,-0x8(%ebp) ; -0x8(%ebp) - 0 , 即 data != 0 比较
0x004014c2 <+98>: jne 0x4014b7 <stream_len+87> ; 非零跳转
34 {
35 codeLen++;
0x004014b7 <+87>: addl $0x1,-0x4(%ebp) ; codeLen++
36 data <<= 1;
0x004014bb <+91>: shll -0x8(%ebp) ; data <<= 1
37 } ; 执行完这两条语句之后、不跳转到 0x4014be 继续判断吗???
38 return codeLen;
0x004014c4 <+100>: mov -0x4(%ebp),%eax ; %eax = codeLen
39 }
0x004014c7 <+103>: leave ; 为返回准备好栈,为ret准备好栈,主要是弹出函数内的栈使用及%ebp
0x004014c8 <+104>: ret
End of assembler dump.
我觉得不解的地方在于这里,这个 while 循环在执行了循环体之后并没有返回循环:
33 while(data != 0)
0x004014b5 <+85>: jmp 0x4014be <stream_len+94>
0x004014be <+94>: cmpl $0x0,-0x8(%ebp) ; -0x8(%ebp) - 0 , 即 data != 0 比较
0x004014c2 <+98>: jne 0x4014b7 <stream_len+87> ; 非零跳转
34 {
35 codeLen++;
0x004014b7 <+87>: addl $0x1,-0x4(%ebp) ; codeLen++
36 data <<= 1;
0x004014bb <+91>: shll -0x8(%ebp) ; data <<= 1
37 } ; 执行完这两条语句之后、不跳转到 0x4014be 继续判断吗???
38 return codeLen; ; 没有跳转指令返回 while 循环的话、应该就是执行这一句代码了
0x004014c4 <+100>: mov -0x4(%ebp),%eax ; %eax = codeLen
39 }
0x004014c7 <+103>: leave ; 为返回准备好栈,为ret准备好栈,主要是弹出函数内的栈使用及%ebp
0x004014c8 <+104>: ret
但这也和本文遇到的异常无关。
看来反汇编没有发现之后,重新再来调试,再次进去函数,发现变量又对了,输出结果也对了:
(特意又看了反汇编代码、并没有变化)
此后、怎么重复都不能复现这个异常现象了。
==>| 此时时间刚过 23 点,是不是有个进程结束了,没有干扰了。。。
以后能复现了,继续添加,待续。