关于std::string出现在_M_dispose发生SIGABRT错误的问题
来源:互联网 发布:数控加工中心编程教材 编辑:程序博客网 时间:2024/06/15 20:10
注意:这不是gcc/stl的bug。
例子程序如下:
inc.h
#include <stdio.h>#pragma pack(1)typedef struct { char a1; char a2; char a3; char a4;} record_t;
main.cc
#include "inc.h"#include <string>/* * A test program to evaluate the miss use of pragma pack * leading to std::string's strange SEGV problem. */int main(int argc, char** argv){ std::string filename("abc.txt"); std::string::size_type pos = filename.rfind('.'); std::string prefix = filename.substr(0, pos); printf("prefix=%s\n", prefix.c_str()); return 0;}
Makefile
CXXFLAGS=-g -O2all: testpack.PHONY: all clean%.o: %.cc $(CXX) $(CXXFLAGS) -c -o $@ $<testpack: main.o $(CXX) -o testpack -O2 main.oclean: rm testpack main.o -rf
在linux环境下,gcc3.3.3以上版本,编译运行,会出现以下错误:
prefix=abc
*** glibc detected *** /home/aaa/testpack/testpack: free(): invalid pointer: 0x0000000000601044 ***
======= Backtrace: =========
/lib64/libc.so.6[0x3b7a87247f]
/lib64/libc.so.6(cfree+0x4b)[0x3b7a8728db]
/home/aaa/testpack/testpack(__gxx_personality_v0+0x1c5)[0x4008cd]
/lib64/libc.so.6(__libc_start_main+0xf4)[0x3b7a81d994]
/home/aaa/testpack/testpack(__gxx_personality_v0+0x71)[0x400779]
... (下略)
不加-O2参数,该错误不出现。
重新运行,调试过程:
(gdb) r
Starting program: /home/aaa/testpack/testpack
warning: no loadable sections found in added symbol-file system-supplied DSO at 0x2aaaaaaab000
prefix=abc
*** glibc detected *** /home/aaa/testpack/testpack: free(): invalid pointer: 0x0000000000601044 ***
(gdb) f 7
#7 main (argc=<optimized out>, argv=<optimized out>) at main.cc:15
(gdb) p prefix
$1 = {static npos = 18446744073709551615, _M_dataplus = {<std::allocator<char>> = {<__gnu_cxx::new_allocator<char>> = {<No data fields>}, <No data fields>},
_M_p = 0x601058 "abc"}}
(释放prefix的_M_dataplus指向的区域时出现问题。看一下代码:)
(gdb) f 6
#6 ~basic_string (this=<optimized out>, __in_chrg=<optimized out>) at /usr/lib/gcc/x86_64-redhat-linux/4.1.2/../../../../include/c++/4.1.2/bits/basic_string.h:478
478 { _M_rep()->_M_dispose(this->get_allocator()); }
(看一下_M_rep()的实现:)
(gdb) list 281
276
277 _CharT*
278 _M_data(_CharT* __p)
279 { return (_M_dataplus._M_p = __p); }
280
281 _Rep*
282 _M_rep() const
283 { return &((reinterpret_cast<_Rep*> (_M_data()))[-1]); }
284
285 // For the internal use we have functions similar to `begin'/`end'
(看一下_M_data()的实现:)
(gdb) list 273
268
269 private:
270 // Data Members (private):
271 mutable _Alloc_hider _M_dataplus;
272
273 _CharT*
274 _M_data() const
275 { return _M_dataplus._M_p; }
276
(获取的_Rep*指针应为_M_dataplus._M_p减去一个_Rep结构体大小。看一下_Rep结构体大小:)
(gdb) p sizeof(std::basic_string<char, std::char_traits<char>, std::allocator<char> >::_Rep)
$4 = 20
(看一下这个结构体的详细信息:)
(gdb) ptype std::basic_string<char, std::char_traits<char>, std::allocator<char> >::_Rep
type = struct std::basic_string<char, std::char_traits<char>, std::allocator<char> >::_Rep
: public std::basic_string<char, std::char_traits<char>, std::allocator<char> >::_Rep_base {
static const size_t _S_max_size;
static const char _S_terminal;
static size_t _S_empty_rep_storage[3];
public:
static std::basic_string<char, std::char_traits<char>, std::allocator<char> >::_Rep & _S_empty_rep(void);
bool _M_is_leaked(void) const;
bool _M_is_shared(void) const;
void _M_set_leaked(void);
void _M_set_sharable(void);
void _M_set_length_and_sharable(unsigned long);
char * _M_refdata(void);
char * _M_grab(const std::allocator<char> &, const std::allocator<char> &);
static std::basic_string<char, std::char_traits<char>, std::allocator<char> >::_Rep * _S_create(unsigned long, unsigned long, const std::allocator<char> &);
void _M_dispose(const std::allocator<char> &);
void _M_destroy(const std::allocator<char> &);
char * _M_refcopy(void);
char * _M_clone(const std::allocator<char> &, unsigned long);
}
(继承的部份应该没有占空间,看一下基类:)
(gdb) ptype std::basic_string<char, std::char_traits<char>, std::allocator<char> >::_Rep_base
type = struct std::basic_string<char, std::char_traits<char>, std::allocator<char> >::_Rep_base {
size_t _M_length;
size_t _M_capacity;
_Atomic_word _M_refcount;
}
(因为是64位系统,所以前面两个size_t加起来是16位,后面一个_Atomic_word,经过在/usr/include下面grep,发现定义为int型,那么在当前x86_64的ABI下仍为4字节,加起来20字节……等一下!考虑对齐的话,最后一个int应该也占8字节,所以加起来应该是24字节,不是吗?为什么gdb显示20字节?)
(我们来检查一下倒底-24还是-20是这个真正的结构体:)
(gdb) p *(std::basic_string<char, std::char_traits<char>, std::allocator<char> >::_Rep_base*)(0x601058-20)
$9 = {_M_length = 12884901888, _M_capacity = 0, _M_refcount = -1}
(gdb) p *(std::basic_string<char, std::char_traits<char>, std::allocator<char> >::_Rep_base*)(0x601058-24)
$10 = {_M_length = 3, _M_capacity = 3, _M_refcount = 0}
(很显然我们的prefix存放的"abc"应该是后者。等一下,结尾的'\0'没有占空间?这个应该是分配的时候多分配了1个字节。capacity应该是没有包含terminating zero的。)
因此,该错误的直接原因在于,本程序的std::string在释放时,通过字符串位置计算字符串头部数据(一个共享的string data区域)时,把头部数据的大小搞错了。
再次注意:这肯定不是std::string的bug。你可以看到,inc.h里面有一句#pragma pack(1),这是相当糟糕的写法。没有#pragma pack(push, 1)和#pragma pop(),造成main.cc在引用时首先包含了这句#pragma pack,然后再引用stl的string头文件。那么在string头文件里面编译进目标文件的代码,会认为sizeof(_Rep_base)是20,而没有包含这个inc.h的代码(libc.so.6)会认为它是24。而stl的实现部份由内联函数直接在exe中展开,部份由libc.so.6预先提供,所以就会造成malloc/free计算出的地址不配对的问题。
关于stl的string为什么要用一块共享的string data实现,可以自行参考相关文档。
最后再次强调一下,这不是gcc的bug,也不是stl的bug,是人为灾害。
本文的目的在于,如果你遇到类似的错误而找不到原因时,可以参考检查。(当然,另外一个更可能的原因是你使用了包含多个gcc版本的动态库,你应当首先排除这个原因)
另外你或许可以从本文中学到一些调试技巧。
[完]
- 关于std::string出现在_M_dispose发生SIGABRT错误的问题
- 关于std::string的使用问题
- 关于cocos2dx ios 内支付 出现iOSProductByIdentifier(std::string &identifier) 为nullptr 的问题 解决方法
- SIGABRT错误的解决办法
- 关于std::string的引用计数在多线程中产生的问题
- QML在安卓上出现的关于“致命错误的问题”
- 关于字符串指针初始化std::string的一个小问题
- 关于EXC_BAD_ACCESS和SIGABRT在xcode下的处理办法
- 关于Xcode中"signal SIGABRT"调试崩溃的问题
- C++ 在DLL中使用std::string乱码的问题
- SIGABRT错误的调试办法
- SIGABRT错误的调试办法
- std::string crash的问题
- 关于从std::vector中删除std::string的方法
- 关于dll出现断言错误的问题
- 关于ibatis出现AutoResultMap错误的问题
- 关于ibatis出现AutoResultMap错误的问题
- 我晕,一个低级错误导致我DEBUG两天(std::string c_str()的问题)
- 致应届毕业生:第一份工作教会我们的7件事
- Xcdoe—CocoaPods安装和使用教程
- Mac OS X 安装 FreeTDS
- 留学新西兰 魂牵梦绕传智播客的课堂
- ListView上拉加载下拉刷新
- 关于std::string出现在_M_dispose发生SIGABRT错误的问题
- MVC实战教程 MVC实战通用系统后台
- 保密性文件丢失怎么恢复
- 二叉排序树的创建与查找
- iSCSI, FC和FCoE的比较和适用场景
- 关于制作敌人死亡后爆出不同种类金币的思路
- Mysql日期列,自动生成当前日期
- 抛弃系统Toast 让提示更优雅,解决toast的连续提示小bug
- 转:关于对Blocks的实现代理传值的介绍