IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    用 Trace32 分析死机问题

    Hacper\'s Blog发表于 2021-05-10 13:28:48
    love 0

    出现死机问题的设备是展锐8910,打开Trace32软件,导入设备死机时的dump文件进行分析。如下图:

    先看死机时PC停止的位置,对应的汇编代码是 ldrb r2,[r1,#0x5],结合trace32中寄存器的值来看,可以确定死机原因是访问非法内存, r1 寄存器为0,也就是访问0地址(空指针)导致死机。

    下面分析出现访问空指针问题的具体原因。

    先分析当前的函数调用栈,为了方便查看,把调用栈信息复制出来,如下所示:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    
    ql_open_file_get_info(
        position = ?,
        file_info = 0x80CD343C)
      err = 0
      node = 0x0
      __FUNCTION__ = (113, 108, 95, 111, 112, 101, 110, 95, 102, 105, 108, 101, 95, 103, 101, 116, 95, 1
    
    ql_file_is_opened(
        file_name = 0x80CD3614)
      cnt = 8
      err = -2134035396
      position = 7
      file_info = (
        file_name = (85, 70, 83, 58, 106, 115, 111, 110, 46, 116, 120, 116, 0, 79, 189, 128, 12, 0, 0, 0
        file_open_mode = 522,
        fd = 1033)
    
    ql_fopen(
        file_name = 0x80CD3614,
        mode = 0x6032500C)
      file_real_path = (47, 117, 115, 101, 114, 47, 47, 99, 111, 114, 103, 101, 116, 47, 99, 111, 110, 1
      file_type = 0
      open_mode = 1537
      file_info = (
        file_name = (12, 0, 0, 0, 122, 46, 49, 96, 52, 0, 0, 0, 176, 70, 49, 96, 16, 0, 0, 0, 80, 53, 20
        file_open_mode = 970205137,
        fd = -2134034952)
      err = -2134035396
      __FUNCTION__ = (113, 108, 95, 102, 111, 112, 101, 110, 0)
    
    OEMFile_Open(
        path = 0x6032D8A8,
        oflag = 0x6032500C)
      fd = -2134035396
      _path = (85, 70, 83, 58, 47, 99, 111, 114, 103, 101, 116, 47, 99, 111, 110, 102, 105, 103, 46, 116
      __FUNCTION__ = (79, 69, 77, 70, 105, 108, 101, 95, 79, 112, 101, 110, 0)
    
        }
        else
    CEMConfig::SaveConfig(
        this = 0x80CD4578)
      pFile = 0x80CD343C
      buf = (168, 46, 167, 128, 224, 33, 158, 128, 196, 32, 154, 128, 221, 214, 33, 96, 4, 0, 0, 0, 248,
    
    CSystemEngine::SaveConfig(
        this = 0x80CD48A8)
      buffer = (52, 50, 57, 52, 57, 54, 55, 50, 57, 53, 0, 128, 140, 148, 220, 128, 68, 122, 220, 128, 1
    
    CPocUIManager::HandleATCommand(
        this = 0x80CD3C50,
        aData = 0x80CD42AC,
        aLength = 4)
      mtd = 124
    
    CPocUIManager::HandleATMessage(
        this = 0x80CD3C50,
        pMsg = 0x80C3EAA8)
      param_length = 4
      res = -2134035396
    
    OEM_PendMessage(
        pmsg = 0x80A72D18)
      msg = 0x80A72D18
    
    ql_corget_api_task_worker(
      ?)
      recv_msg = (id = MSG_ID_POC, payload = 0x80A72D18)
      ret = -2134035396
      __FUNCTION__ = (113, 108, 95, 99, 111, 114, 103, 101, 116, 95, 97, 112, 105, 95, 116, 97, 115, 107
    
        g_pcm_play_ctx.pcm_play_handle = ql_pcm_open(&config, flag);
    }
    prvTaskExitError()
    
    end of frame
    

    可以看到,在Trace32 里面不仅可以看到当前出现问题时的调用栈关系,还可以看到函数参数,变量的具体值,对于分析问题的来龙去脉非常有利。

    从调用栈看,当前正在调用接口ql_fopen( file_name = 0x80CD3614, mode = 0x6032500C)打开文件,输入查看变量的指令来查看地址 0x80CD3614,0x6032500C的数据:

    1
    2
    
    v.v %s (char *)0x80CD3614
    v.v %s (char *)0x6032500C
    

    可以看到当前打开的文件是"UFS:/corget/config.txt",打开方式为 “wb”。

    结合源码和调用栈来看,ql_fopen内部调用ql_file_is_opened(file_name = 0x80CD3614) 检查UFS:/corget/config.txt是否在已打开的文件列表内。

    然后ql_file_is_opened 内部会调用 ql_open_file_get_info 遍历打开的文件信息列表,查找文件是否已打开,从dump和源码可以知道,当前总共打开了8个文件,ql_file_is_opened 中 position = 7,也就是当前在获取最后一个文件的信息(从0开始,0~7)。

    最终在ql_open_file_get_info里访问了空指针死机,ql_open_file_get_info里面

    1
    2
    
    err = 0
    node = 0x0 -> NULL
    

    根据代码可以看到是ql_open_file_get_info里面memcpy(file_info, node→data, node→size)这里访问了空指针,可以结合dump和linklist的数据结构定义交叉验证:

    1
    2
    3
    4
    5
    6
    
    typedef struct node
    {
    	struct node * next; // 4 bytes
        datasize_t size; // 1 bytes
        data_t data[1]; // 4 bytes
    } linklist; // 9 bytes
    

    死机的汇编代码位置 ldrb r2,[r1,#0x5],r1为0,偏移5个字节,刚好是memcpy(file_info, node→data, node→size)中的node→data。

    为什么ql_open_file_get_info里会访问空指针?其实是linklist_node_find 这个函数的bug,查看全局变量ql_file_open_list的值和linklist_node_find的源码,可以定位到问题的原因是linklist_node_find的代码逻辑问题,查找链表节点中遍历链表是从(*linklist_handler)->header->next;开始,忽略了第一个节点,如果当前查找的节点是链表的最后一个节点,就会返回空节点。

    linklist_node_find函数的源码如下:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    
    QuecOSStatus linklist_node_find(linklist_handler_t * linklist_handler, uint32_t i, linklist * * dest_node)
    {
    	int 		 j = 0;
    
        if(! linklist_handler)
        {
    		return kGeneralErr;
        }
    
        if(! *linklist_handler)
        {
       	    return kGeneralErr;
        }
    
    	if(! dest_node)
    	{
    		return kGeneralErr;
    	}
    
        linklist * temp_node = (*linklist_handler)->header->next;
    
        if(i >= (*linklist_handler)->node_cnt)
        {
       		return kGeneralErr;
        }
    
        for(j=0; j<=i; j++)
        {
            if(j == i)
            {
                *dest_node = temp_node;
                break;
            }
            temp_node = temp_node->next;
        }
    	return kNoErr;
    }
    

    通过Trace32分析dump文件,定位到了出现问题的源码位置,这个死机问题基本上完成了追根溯源,那么对于如何解决这个问题,你有什么建议?欢迎讨论。



沪ICP备19023445号-2号
友情链接