博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
gcc的 “-fpack-struct” 编译选项导致程序core dump的分析
阅读量:6881 次
发布时间:2019-06-27

本文共 4846 字,大约阅读时间需要 16 分钟。

感谢网友【】的投稿

最近team引入gcov来做代码分析。编译好的程序在Solaris上运行的好好的,结果在Linux上一运行就会产生core dump文件。这篇文章就介绍整个分析过程。

首先用gdb分析core文件,显示是strlen调用出了问题:

(gdb) #0  0x00000034e433386f in __strlen_sse42 () from /lib64/libc.so.6#1  0x000000000053c57a in __gcov_init ()#2  0x000000000053c4b9 in _GLOBAL__I_65535_0_g_cmd_param () at source/rerun/aicent_ara_rerun.c:963#3  0x000000000053dc26 in __do_global_ctors_aux ()#4  0x0000000000403743 in _init ()#5  0x00007fff6d6b3ce8 in ?? ()#6  0x000000000053db55 in __libc_csu_init ()#7  0x00000034e421ecb0 in __libc_start_main () from /lib64/libc.so.6#8  0x0000000000404449 in _start ()

由于我们使用的gcc是用安装包形式安装的,没有源码。所以就从github上找了相应版本的gcc源代码,希望能有所帮助。以下是__gcov_init函数的代码():

void__gcov_init (struct gcov_info *info){  if (!info->version)    return;  if (gcov_version (info, info->version, 0))    {      const char *ptr = info->filename;      gcov_unsigned_t crc32 = gcov_crc32;      size_t filename_length =  strlen(info->filename);      /* Refresh the longest file name information */      if (filename_length > gcov_max_filename)        gcov_max_filename = filename_length;      do    {      unsigned ix;      gcov_unsigned_t value = *ptr << 24;      for (ix = 8; ix--; value <<= 1)        {          gcov_unsigned_t feedback;          feedback = (value ^ crc32) & 0x80000000 ? 0x04c11db7 : 0;          crc32 <<= 1;          crc32 ^= feedback;        }    }      while (*ptr++);      gcov_crc32 = crc32;      if (!gcov_list)    atexit (gcov_exit);      info->next = gcov_list;      gcov_list = info;    }  info->version = 0;}

结合源代码和core文件可以看出,应该是“size_t filename_length = strlen(info->filename);”这一行出了问题。再结合汇编程序:

(gdb) disassemble __strlen_sse42Dump of assembler code for function __strlen_sse42:   0x00000034e4333860 <+0>:     pxor   %xmm1,%xmm1   0x00000034e4333864 <+4>:     mov    %edi,%ecx   0x00000034e4333866 <+6>:     mov    %rdi,%r8   0x00000034e4333869 <+9>:     and    $0xfffffffffffffff0,%rdi   0x00000034e433386d <+13>:    xor    %edi,%ecx=> 0x00000034e433386f <+15>:    pcmpeqb (%rdi),%xmm1

是访问rdi寄存器出了问题,而rdi保存的应该是strlen的参数,也就是“info->filename”。试着访问一下rdi寄存器保存的地址:

(gdb) i registers rdirdi            0x57c4ac00000000 24704565987246080(gdb) x/16xb 0x57c4ac000000000x57c4ac00000000:       Cannot access memory at address 0x57c4ac00000000

可以看到rdi寄存器保存的地址的确是个无效的地址。

接下来,就要分析一下为什么传入__gcov_initinfo结构体的filename是一个无效指针。首先看一下gcov_info结构体的定义():

/* Information about a single object file.  */struct gcov_info{  gcov_unsigned_t version;  /* expected version number */  struct gcov_info *next;   /* link to next, used by libgcov */  gcov_unsigned_t stamp;    /* uniquifying time stamp */  const char *filename;     /* output file name */  unsigned n_functions;     /* number of functions */  const struct gcov_fn_info *functions; /* table of functions */  unsigned ctr_mask;        /* mask of counters instrumented.  */  struct gcov_ctr_info counts[0]; /* count data. The number of bits                     set in the ctr_mask field                     determines how big this array                     is.  */};

查看调用__gcov_init_GLOBAL__I_65535_0_g_cmd_param函数的汇编代码:

(gdb) disassemble _GLOBAL__I_65535_0_g_cmd_paramDump of assembler code for function _GLOBAL__I_65535_0_g_cmd_param:   0x000000000053c4ab <+0>:     push   %rbp   0x000000000053c4ac <+1>:     mov    %rsp,%rbp   0x000000000053c4af <+4>:     mov    $0x78d4a0,%edi   0x000000000053c4b4 <+9>:     callq  0x53c4c0 <__gcov_init>   0x000000000053c4b9 <+14>:    leaveq   0x000000000053c4ba <+15>:    retqEnd of assembler dump.

可以看到传入__gcov_init的参数为0x78d4a0,也就是指向gcov_info结构体的地址,查看这个地址的内容:

(gdb) x/64xb 0x78d4a00x78d4a0:       0x52    0x34    0x30    0x34    0x00    0x00    0x00    0x000x78d4a8:       0x00    0x00    0x00    0x00    0x82    0xf0    0xc7    0xa50x78d4b0:       0x60    0xc4    0x57    0x00    0x00    0x00    0x00    0x000x78d4b8:       0x0b    0x00    0x00    0x00    0xac    0xc4    0x57    0x000x78d4c0:       0x00    0x00    0x00    0x00    0x01    0x00    0x00    0x000x78d4c8:       0x39    0x01    0x00    0x00    0xc0    0xa4    0x47    0x030x78d4d0:       0x00    0x00    0x00    0x00    0xe0    0xd8    0x53    0x000x78d4d8:       0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00

可以看到对应filename成员的值应该为0x57c4ac0000000b,的确是个无效地址。问题分析到这里,没了思路。后来,在gccbugzilla里找到这个问题:,才搞清楚是“-fpack-struct=4”这个编译选项导致的。

我们使用的是64位Linux,默认编译生成的可执行文件是64位的。所以gcov_info的默认内存布局应该是(gcov_unsigned_t类型占4个字节,指针类型占8个字节):

Offset 4 bytes 4 bytes
0 version 填充成员
8 next next
16 stamp 填充成员
24 filename filename

当使用“-fpack-struct=4”这个编译选项后,gcov_info的内存布局变为:

Offset 4 bytes 4 bytes
0 version next
8 next stamp
16 filename filename

经过推算,filename成员的值应该为0x57c460,验证一下:

(gdb) p (char*)0x57c460$1 = 0x57c460 "/home/.../.....gcda"

打印出的是正确的值。在Solaris上没问题的原因是因为64位Solaris默认编译出来的程序是32位的。

看了一下gcc网站对-fpack-struct的介绍(),使用这个编译选项会导致ABI(Application Binary Interface)的改变,所以使用时一定要谨慎。

转载地址:http://hubbl.baihongyu.com/

你可能感兴趣的文章
MVC前台Post/Get异步获得数据时参数的取值问题
查看>>
8086/8088指令详解
查看>>
iOS:自定义代码块{ }
查看>>
C# 远程链接指定计算机,获取该计算机的计算机名等信息
查看>>
OpenGL入门笔记(十一)
查看>>
windowsXP用户被禁用导致不能网站登录
查看>>
第 8 章 TokyoCabinet/Tyrant
查看>>
智慧城市逐步推进 未来城市建设突破口分析
查看>>
是谁在推动路由器智能连接功能的普及?
查看>>
物联网软件更新政策不明 智能冰箱也易沦为犯罪工具
查看>>
基于 SaaS 解决库存问题, Nextail 获 160 万美元融资
查看>>
Windows 10新版可以更新了!这些新功能值得升级
查看>>
《微信公众平台开发最佳实践》——2.2 微信开发者中心
查看>>
《IPv6精髓(第2版)》——1.4 常见误解
查看>>
《精通ArcGIS Server 应用与开发》——2.2 ArcGIS Server架构
查看>>
《UNIX网络编程 卷1:套接字联网API(第3版)》——2.10 TCP端口号与并发服务器...
查看>>
Centrifugo —— 用 Golang 实现的实时消息通信平台
查看>>
《善用佳软:高效能人士的软件应用之道》一2.6 小工具之计算器
查看>>
《Web前端工程师修炼之道(原书第4版)》——关于浏览器
查看>>
关于CKEditor4.5.6的使用,自定义toolbar配置,上传图片案例(SpringMVC+MyBatis案例),自定义行高,去编辑器的中内容,将编辑器中内容设置到指定的位置等...
查看>>