1001_记录一次运行结果无法解释的C代码

发布时间 2023-07-15 00:07:06作者: 明天再取个名字

环境

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 点,是不是有个进程结束了,没有干扰了。。。
以后能复现了,继续添加,待续。