Хотя верно то, что сказано Lightness Races in Orbit
, также верно и то, что когда вы компилируете с отладочной информацией (то есть, используя gcc / clang параметр -g), компилятор выдает line information
, позволяя теоретически отладчику связать каждая машинная инструкция с номером строки источника, даже при компиляции с -O3, где действительно fancy оптимизации происходят.
Сказал, что объяснение того, почему gdb
говорит вам, что программа потерпела крах в строке 15
, очень просто: крах действительно не произошел в строке 13
. Достаточно взглянуть на трассировку стека [я скомпилировал вашу программу с gdb
в Linux]:
(gdb) bt
#0 __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:51
#1 0x00007ffff7a24801 in __GI_abort () at abort.c:79
#2 0x00007ffff7a6d897 in __libc_message (action=action@entry=do_abort,
fmt=fmt@entry=0x7ffff7b9a988 "*** %s ***: %s terminated\n") at ../sysdeps/posix/libc_fatal.c:181
#3 0x00007ffff7b18cd1 in __GI___fortify_fail_abort (need_backtrace=need_backtrace@entry=false,
msg=msg@entry=0x7ffff7b9a966 "stack smashing detected") at fortify_fail.c:33
#4 0x00007ffff7b18c92 in __stack_chk_fail () at stack_chk_fail.c:29
#5 0x00005555555547e2 in main (argc=1, argv=0x7fffffffdde8) at crash.cpp:15
Как вы можете видеть в кадре # 4, ваша программа не вылетала из-за переполнения буфера, а из-за защиты стека компилятора (функция __stack_chk_fail
)
Поскольку это не код, написанный вами, а автоматически генерируемый компилятором именно для обнаружения таких ошибок, строчная информация не может быть реальной. Компилятор просто использовал строку 15
, потому что именно там заканчивается ваша функция main()
и, конечно, место, где, если вы посмотрите на код разборки, компилятор испустил код, используя stack sentinels
для обнаружения stack corruption
.
Чтобы увидеть всю картинку еще лучше, вот код разборки (просто используйте disass /s main
в gdb, чтобы увидеть ее):
13 edgePoints[3] = edgePoint3;
0x000000000000079e <+308>: lea rax,[rbp-0x50]
0x00000000000007a2 <+312>: mov esi,0x3
0x00000000000007a7 <+317>: mov rdi,rax
0x00000000000007aa <+320>: call 0x7e4 <std::array<std::array<double, 3ul>, 3ul>::operator[](unsigned long)>
0x00000000000007af <+325>: mov rcx,rax
0x00000000000007b2 <+328>: mov rax,QWORD PTR [rbp-0x70]
0x00000000000007b6 <+332>: mov rdx,QWORD PTR [rbp-0x68]
0x00000000000007ba <+336>: mov QWORD PTR [rcx],rax
0x00000000000007bd <+339>: mov QWORD PTR [rcx+0x8],rdx
0x00000000000007c1 <+343>: mov rax,QWORD PTR [rbp-0x60]
0x00000000000007c5 <+347>: mov QWORD PTR [rcx+0x10],rax
14 return 0;
0x00000000000007c9 <+351>: mov eax,0x0
15 }
0x00000000000007ce <+356>: mov rdx,QWORD PTR [rbp-0x8]
0x00000000000007d2 <+360>: xor rdx,QWORD PTR fs:0x28
0x00000000000007db <+369>: je 0x7e2 <main(int, char**)+376>
0x00000000000007dd <+371>: call 0x540 <__stack_chk_fail@plt>
0x00000000000007e2 <+376>: leave
Как вы можете видеть, в line 15
есть несколько инструкций, явно выдаваемых компилятором, потому что stack protector
включен по умолчанию.
Если вы скомпилируете свою программу с помощью -fno-stack-protector
, она не будет аварийно завершаться, [по крайней мере, это не происходит на моей машине с моим компилятором], но фактическое stack corruption
будет там, просто производя непредсказуемые эффекты . В более крупной программе, когда происходит повреждение стека, вы можете ожидать любое странное поведение намного позже , чем в тот момент, когда произошло повреждение. Другими словами, защита стека - это очень хорошая вещь, которая помогает вам разоблачить проблему вместо сокрытия , то есть то, что естественно произойдет без него .