2012-05-08 5 views
6

Что может вызвать ошибку сегментации при простом вводе функции?SIGSEGV при вводе функции

вошла функция выглядит следующим образом:

21: void eesu3(Matrix & iQ) 
22: { 

где Matrix является struct. При работе с помощью GDB трассировка производит:

(gdb) backtrace 
#0 eesu3 (iQ=...) at /home/.../eesu3.cc:22 
#1 ... 

GDB не говорит, что iQ есть. ... буквально там. Что может быть причиной этого?

GCC: (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3

Программа построена с -O3 -g

Вызывающий абонент не идет, как:

Matrix q; 
// do some stuff with q 
eesu3(q); 

Ничего особенного здесь

Я перепрограммировал программу с помощью valgrind:

valgrind --tool=memcheck --leak-check=yes --show-reachable=yes --num-callers=20 --track-fds=yes <prgname> 

Выход:

==2240== Warning: client switching stacks? SP change: 0x7fef7ef68 --> 0x7fe5e3000 
==2240==   to suppress, use: --max-stackframe=10076008 or greater 
==2240== Invalid write of size 8 
==2240== at 0x14C765B: eesu3(Matrix &) (eesu3.cc:22) 
... 
==2240== Address 0x7fe5e3fd8 is on thread 1's stack 
==2240== 
==2240== Can't extend stack to 0x7fe5e2420 during signal delivery for thread 1: 
==2240== no stack segment 
==2240== 
==2240== Process terminating with default action of signal 11 (SIGSEGV) 
==2240== Access not within mapped region at address 0x7FE5E2420 
==2240== at 0x14C765B: eesu3(Matrix&) (eesu3.cc:22) 
==2240== If you believe this happened as a result of a stack 
==2240== overflow in your program's main thread (unlikely but 
==2240== possible), you can try to increase the size of the 
==2240== main thread stack using the --main-stacksize= flag. 
==2240== The main thread stack size used in this run was 8388608. 

Похоже, его поврежденным стека.

Dump of assembler code for function eesu3(Matrix &): 
    0x00000000014c7640 <+0>: push %rbp 
    0x00000000014c7641 <+1>: mov %rsp,%rbp 
    0x00000000014c7644 <+4>: push %r15 
    0x00000000014c7646 <+6>: push %r14 
    0x00000000014c7648 <+8>: push %r13 
    0x00000000014c764a <+10>: push %r12 
    0x00000000014c764c <+12>: push %rbx 
    0x00000000014c764d <+13>: and $0xfffffffffffff000,%rsp 
    0x00000000014c7654 <+20>: sub $0x99b000,%rsp 
=> 0x00000000014c765b <+27>: mov %rdi,0xfd8(%rsp) 

Хорошо, чтобы было ясно: данные Матрицы живут в куче. Он в основном содержит указатель на данные. Структура мала, 32 байта. (Только что проверил)

Теперь я перестроил программу с различными вариантами оптимизации:

-O0: ошибка не отображается.

-O1: ошибка показывает.

-O3: ошибка показывает.

--update

-O3 -fno-inline -fno-inline-functions: ошибка не отображается.

Это объясняет это. Слишком много встроенных функций привело к чрезмерному использованию стека.

Проблема была из-за переполнения стека в

+2

Чтобы посмотреть на переменные и т. Д., Не оптимизируйте. Компилировать с '-O0 -g' – RageD

+2

Stack corp? – Benj

+0

Хорошо, перестроим с помощью '-O0 -g'. Проходит некоторое время. – ritter

ответ

13

Что может вызвать ошибку сегментации при простом вводе функции?

Наиболее частой причиной является истощение штанов. Do (gdb) disas в точке крушения. Если команда, которая потерпела крах, является первым чтением или записью в местоположение стека после уменьшения %rsp, то исчерпание столов является почти определенной причиной.

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

Другая возможная причина: если Matrix содержит очень большой массив, вы не можете поместить его в стек: ядро ​​не расширения стека за пределы тока более чем 128К (или так, я не помню точное значение) , Если Matrix больше этого предела, вы не можете поместить его в стек.

Update:

0x00000000014c7654 <+20>: sub $0x99b000,%rsp 
=> 0x00000000014c765b <+27>: mov %rdi,0xfd8(%rsp) 

Это разборку подтверждает диагноз.

Кроме того, вы резервируете 0x99b000 байт в стеке (это почти 10 МБ). Должны быть какие-то сверхъестественные объекты, которые вы пытаетесь найти в стеке в рутине eesu3. Не делай этого.

Что вы имеете в виду под «ядро не будет распространяться за пределы стека тока больше, чем»

Когда вы расширяете стек (декремент %rsp), например, посредством 1 МБ, а затем попытаться коснуться места расположения стека, память не будет доступна (ядро растет по требованию). Это создаст аппаратную ловушку и передаст управление ядру. Когда ядро ​​решает, что делать, он смотрит на

  1. Current %rsp
  2. Meemory месте, приложение попытается получить доступ
  3. предел стека для текущего потока

Если разломообразования адрес ниже текущий %rsp, но в пределах 128K (или некоторой другой константы с одинаковой величиной) ядро ​​просто расширяет стек (при условии, что такое расширение не будет превышать ограничение стека).

Если неисправный адрес более 128K ниже текущего %rsp (как здесь видно), вы получаете SIGSEGV.

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

В любом случае, do (gdb) info locals в точке столкновения, и посмотрите, какие местные жители могут потребовать 10 МБ стека. Затем переместите их в кучу.

Update 2:

Нет местные

Ах, эта программа, вероятно, не сделал это достаточно далеко в eesu3 там быть местными жителями.

При построении с -O0 ошибка исчезает. Ошибка GCC?

Это может быть ошибка GCC, но, скорее всего, это просто, что GCC является встраивание много других подпрограмм в eesu3, и каждый из встраиваемыми подпрограмм нужны свои собственные N KBS стека. Проблема исчезнет, ​​если вы создали источник, содержащий eesu3 с -fno-inline?

К сожалению, сортировка такого поведения и выяснение соответствующих обходных решений или исправление GCC требует опыта компилятора. Вы можете начать с компиляции с -fdump-tree-all и посмотреть на созданные <source>.*t.* файлы. Они содержат текстовые дампы внутреннего представления GCC на разных этапах процесса компиляции. Вы можете мая быть в состоянии понять достаточно, чтобы добиться дальнейшего прогресса.

+0

Полезная нагрузка Матрицы живет уже на куче. Это действительно просто владеет указателем на данные. Это не размер структурной матрицы (которая составляет 8 или 16 байт). См. Выше вывод 'disas' – ritter

+0

@Employed Russian. Что вы подразумеваете под« ядро ​​не будет расширять стек за пределами текущего более чем »вы могли бы написать более подробный, пожалуйста? – azat

+0

'info locals': Нет местных жителей. Странно. Определено 30 локальных варов, каждый 32 байта. Куда они ушли? Возможный эффект '-O3'? Как и в обновленном вопросе при создании с '-O0', ошибка исчезает. Ошибка GCC? – ritter

0

Если его матрица, проверьте индексы, которые вы пытаетесь получить доступ. Может быть, вы получаете доступ к элементам, выходящим за пределы размеров объекта Matrix?

4

Это переполнение стека.

eesu3 пытается выделить что-то очень большое в стеке, который можно увидеть в ассемблере:

sub $0x99b000,%rsp 

Это означает, что более 10 МБ пространства стека потребляются.

Проблема может быть в eesu3 или функции, которую он вызывает, и компилятор выбирает встроенную.

Я думаю, что проблема заключается в вызове функции eesu3, но не в случае, если вы тест (функция отладки?)
Я думаю, это потому, что это не происходит без оптимизации - с оптимизацией, функция вставляется в eesu3, поэтому eesu3 использует много стека. Без этого функция не является встроенной, поэтому вы получите проблему только тогда, когда она действительно вызвана.

+0

Да, вероятно, это переполнение стека. Я надеюсь, что это будет хорошо для вас, если @Emploed Russian получит смысл. Он был первым, кто указал на это – ritter

+0

. Нет смысла указывать точку снова. – hochl

0

Вы, вероятно, есть некоторые переменные инициализируются в функции

void eesu3(Matrix & iQ) 

Allthough отладчик может пошагово объявления переменных, они, вероятно, инициализируется с началом объема (то есть ваша функция). Если вы объявите очень большой буфер, например:

char * buffer[268435456]; 

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

void * pvBuffer = malloc(268435456); 

Вы объявили большой буфер? Что слишком велико, чтобы положить на стек? Это может означать, что разные архитектуры приводят к различным возможным максимальным размерам для буферов (64-битных и 32-разрядных ОС)? Разные ядра? Как вы сказали, программа работает нормально на одной машине, но не на другой.

Смежные вопросы