Что может вызвать этот код для segfault - PullRequest
3 голосов
/ 08 декабря 2011

В следующем коде я вижу segfault в строке, которая говорит signaling_thread_->Send(this, id, data);, которая вызывается из деструктора класса PeerConnectionProxy.

bool PeerConnectionProxy::Send(uint32 id, talk_base::MessageData* data) {
  if (!signaling_thread_)
    return false;
  signaling_thread_->Send(this, id, data);
  return true;
}

Работая в gdb, я получаюsegfault и этот стек отслеживаются, как только я (gdb) step перехожу на эту строку:

Program received signal SIGSEGV, Segmentation fault.

0x00000000 in ?? ()
(gdb) bt
#0  0x00000000 in ?? ()
#1  0xa782eed4 in webrtc::PeerConnectionProxy::Send (this=0xab889e80, id=6, data=0xbfffc1e8)
    at third_party/libjingle/source/talk/app/webrtc/peerconnectionproxy.cc:219
#2  0xa782e91a in ~PeerConnectionProxy (this=0xab889e80, __in_chrg=<value optimised out>)
    at third_party/libjingle/source/talk/app/webrtc/peerconnectionproxy.cc:145

...

Прерывая непосредственно перед этой строкой, я проверяю, что, как и ожидалось, signaling_thread_ не является нулевым, как этоданные.Я просто очень озадачен тем, что может быть причиной segfault или сделать стек в 0x00000000.Код только segfaults на пути к коду через деструктор.Функция Send вызывается из многих других мест без проблем.

Обновление 2011-12-08:

Пошаговое выполнение с помощью stepi и разборкапри включении я получаю следующее:

0xa772eed2  219   signaling_thread_->Send(this, id, data);
   0xa772eea4 <_ZN6webrtc19PeerConnectionProxy4SendEjPN9talk_base11MessageDataE+24>:     8b 45 08   mov    0x8(%ebp),%eax
   0xa772eea7 <_ZN6webrtc19PeerConnectionProxy4SendEjPN9talk_base11MessageDataE+27>:     8b 40 0c   mov    0xc(%eax),%eax
   0xa772eeaa <_ZN6webrtc19PeerConnectionProxy4SendEjPN9talk_base11MessageDataE+30>:     8b 00  mov    (%eax),%eax
   0xa772eeac <_ZN6webrtc19PeerConnectionProxy4SendEjPN9talk_base11MessageDataE+32>:     83 c0 40   add    $0x40,%eax
   0xa772eeaf <_ZN6webrtc19PeerConnectionProxy4SendEjPN9talk_base11MessageDataE+35>:     8b 08  mov    (%eax),%ecx
   0xa772eeb1 <_ZN6webrtc19PeerConnectionProxy4SendEjPN9talk_base11MessageDataE+37>:     8b 45 08   mov    0x8(%ebp),%eax
   0xa772eeb4 <_ZN6webrtc19PeerConnectionProxy4SendEjPN9talk_base11MessageDataE+40>:     8d 70 04   lea    0x4(%eax),%esi
   0xa772eeb7 <_ZN6webrtc19PeerConnectionProxy4SendEjPN9talk_base11MessageDataE+43>:     8b 45 08   mov    0x8(%ebp),%eax
   0xa772eeba <_ZN6webrtc19PeerConnectionProxy4SendEjPN9talk_base11MessageDataE+46>:     8b 40 0c   mov    0xc(%eax),%eax
   0xa772eebd <_ZN6webrtc19PeerConnectionProxy4SendEjPN9talk_base11MessageDataE+49>:     8b 55 10   mov    0x10(%ebp),%edx
   0xa772eec0 <_ZN6webrtc19PeerConnectionProxy4SendEjPN9talk_base11MessageDataE+52>:     89 54 24 0c    mov    %edx,0xc(%esp)
   0xa772eec4 <_ZN6webrtc19PeerConnectionProxy4SendEjPN9talk_base11MessageDataE+56>:     8b 55 0c   mov    0xc(%ebp),%edx
   0xa772eec7 <_ZN6webrtc19PeerConnectionProxy4SendEjPN9talk_base11MessageDataE+59>:     89 54 24 08    mov    %edx,0x8(%esp)
   0xa772eecb <_ZN6webrtc19PeerConnectionProxy4SendEjPN9talk_base11MessageDataE+63>:     89 74 24 04    mov    %esi,0x4(%esp)
   0xa772eecf <_ZN6webrtc19PeerConnectionProxy4SendEjPN9talk_base11MessageDataE+67>:     89 04 24   mov    %eax,(%esp)
=> 0xa772eed2 <_ZN6webrtc19PeerConnectionProxy4SendEjPN9talk_base11MessageDataE+70>:     ff d1  call   *%ecx

ecx - это 0x0, так что это то, что делает его segfault, но я до сих пор не понимаю, что происходит.Другой код для строки не выглядит касающимся ecx, если только я не читаю его неправильно.

Ответы [ 3 ]

3 голосов
/ 09 декабря 2011

Скорее всего, причина в том, что signaling_thread_ - это висячий указатель - раньше он указывал на что-то, но что-то было delete ', оставляя указатель, который не равен нулю, но, вероятно, вызовет сбой если вы попытаетесь что-то с ним сделать (например, вызовете для него метод Send).

Поскольку вы говорите, что это вызывается из деструктора, вполне возможно, что вызов delete произошел ранее в том же деструкторе ...

1 голос
/ 09 декабря 2011

%ecx содержит запись виртуальной таблицы для метода Send, которая по какой-то причине обнулена.Чаще всего с деструктором является то, что signaling_thread_ удаляется перед вызовом PeerConnectionProxy::Send.Другая возможность - переполнение буфера при предыдущем вызове метода signaling_thread_, который перезаписывает запись виртуальной таблицы.Другая возможность - переполнение буфера в деструкторе, который перезаписывает указатель signaling_thread_.Если вы отправите код в свой деструктор, мы сможем сузить его.

1 голос
/ 09 декабря 2011

Всякий раз, когда у меня возникает ошибка, в которой участвуют деструкторы, она часто решается созданием деструкторов virtual. Не беспокойтесь, если это пока не имеет смысла.

Когда объект должен быть уничтожен, сначала будет вызван деструктор, затем будет предпринята попытка освободить объект. Часто сам деструктор полностью успешен, но попытка освободить память терпит неудачу из-за неясной проблемы, которую я попытаюсь описать позже. (Этот ответ относится к соответствующей части стандарта C ++.)

Вы уверены, что segfault происходит во время деструктора? Или, возможно, это происходит сразу после завершения деструктора. Не могли бы вы вставить printf в конец соответствующего деструктора, пожалуйста?

Я собираюсь предположить, что сама функция деструктора успешна и что ошибка происходит сразу после деструктора, при попытке освободить память.

рассмотрим эту структуру:

struct A {
    int x;
};
A a;

Здесь явно &a == &(a.x)

А B наследуется от A:

struct B : public A {
};
B b;

Опять &b == &(b.x)

Но если задействованы виртуальные методы, все становится сложнее.

struct C : public B {
   virtual void foo() {}
};
C c;

Теперь &c != &(c.x). Это связано с тем, что первая истинная запись c (зависит от компилятора) на самом деле представляет собой виртуальную таблицу, в которой указано расположение таких функций, как foo (). Теперь представьте следующий код:

{
    A * p = new C;
    delete p;
}

Оператор delete p считает , что имеет дело с объектом типа A, но на самом деле он имеет дело с объектом типа C. Деструктор будет работать правильно, но попытка вызвать free(p) будет неправильной, потому что он не использует правильный адрес. Это как int *p = malloc(100); free(p+1).

Если сомневаетесь, поместите деструктор virtual в каждый класс, если вы когда-либо наследуете его с помощью виртуальных функций в подклассе.

...