std::string은 에 나타납니다M_dispose SIGABRT 오류 문제

5857 단어
주의: 이것은 gcc/stl의 버그가 아닙니다.
예제 절차는 다음과 같습니다.
inc.h
#include 

#pragma pack(1)

typedef struct {
    char    a1;
    char    a2;
    char    a3;
    char    a4;
} record_t;

main.cc
#include "inc.h"
#include 

/*
 * 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
", prefix.c_str()); return 0; }

Makefile
CXXFLAGS=-g -O2

all: testpack

.PHONY: all clean

%.o: %.cc
    $(CXX) $(CXXFLAGS) -c -o $@ $<

testpack: main.o
    $(CXX) -o testpack -O2 main.o

clean:
    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=, argv=) at main.cc:15
(gdb) p prefix $1 = {static npos = 18446744073709551615, _M_dataplus = {<:allocator>> = {<:new_allocator>> = {}, },     _M_p = 0x601058 "abc"}}
(prefix의 M dataplus가 가리키는 영역을 해제하는 동안 문제가 발생했습니다. 코드를 보십시오.)
(gdb) f 6 #6  ~basic_string (this=, __in_chrg=) 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, std::allocator >::_Rep) $4 = 20
(이 구조체의 상세한 정보를 보십시오:)
(gdb) ptype std::basic_string, std::allocator >::_Rep type = struct std::basic_string, std::allocator >::_Rep         : public std::basic_string, std::allocator >::_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, std::allocator >::_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 &, const std::allocator &);     static std::basic_string, std::allocator >::_Rep * _S_create(unsigned long, unsigned long, const std::allocator &);     void _M_dispose(const std::allocator &);     void _M_destroy(const std::allocator &);     char * _M_refcopy(void);     char * _M_clone(const std::allocator &, unsigned long); } (상속된 부분은 공간을 차지하지 않을 것이다. 기류를 보자:)
(gdb) ptype std::basic_string, std::allocator >::_Rep_base type = struct std::basic_string, std::allocator >::_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바이트를 합치면 20바이트... 잠깐만! 정렬을 고려하면 마지막 int도 8바이트를 차지해야 하기 때문에 합치면 24바이트가 아닌가? 왜 gdb에서 20바이트가 나오나요?)
(밑바닥-24인지 -20이 진짜 구조체인지 확인해 보자.) (gdb) p * (std:::babasic string, std:::allocator >::Rep base* (0x601058-20) (0x60101010101010:) (gdb) (gdb) p * (0x60101058-20) * (gdb) p:) * (0x60: gdb) p:) p * (std:) * (: (gd:) (: gd:) $60 b) p:) * (: (: gd:) (:): (gs:): (: (: griststring:::) string:::::::::::::::::::M capacity = 3, M refcount = 0}
(분명히 우리prefix에 저장된 abc는 후자일 것이다. 잠깐만, 마지막'\0'은 공간을 차지하지 않는다? 이것은 분배할 때 한 바이트를 더 분배한 것 같다.capacity는terminating zero를 포함하지 않은 것 같다.)
따라서 이 오류의 직접적인 원인은 이 프로그램의 std::string이 방출될 때 문자열 위치를 통해 문자열의 헤더 데이터(공유된string 데이터 영역)를 계산할 때 헤더 데이터의 크기가 잘못되었기 때문이다.
다시 주의: 이것은 틀림없이 std::string의 버그가 아닙니다.보시다시피 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데이터로 이루어져야 하는지에 대해 관련 문서를 스스로 참고할 수 있습니다.
마지막으로 다시 한 번 강조하지만 이것은 gcc의 버그도 아니고 stl의 버그도 아니고 인위적인 재해이다.
본고의 목적은 유사한 오류가 발생하여 원인을 찾지 못할 경우 검사를 참고하는 데 있다.(물론, 또 다른 가능한 이유는 여러 개의 gcc버전을 포함하는 동적 라이브러리를 사용했기 때문에 이 원인을 먼저 배제해야 한다)
그리고 너는 아마도 본문에서 약간의 디버깅 기교를 배울 수 있을 것이다.
[끝]


좋은 웹페이지 즐겨찾기