почему существует разница с ошибкой с (SIGABRT) и без (SIGSEGV) защитного устройства, когда оба обращаются к нелегальной памяти (поэтому, я думаю, должно быть то же самое).
Когда вы компилируете без стекового протектора, вы просто перезаписываете часть памяти, к которой у вас нет прав, и программа убивается операционной системой через SIGSEGV
сигнал.
Однако, когда вы компилируете с помощью стекового протектора, ошибка может быть обнаружена библиотекой lib c, и в этом случае вызывается функция __stack_chk_fail()
. Это можно увидеть с помощью objdump
:
72d: b8 00 00 00 00 mov eax,0x0
732: 48 8b 55 f8 mov rdx,QWORD PTR [rbp-0x8]
736: 64 48 33 14 25 28 00 xor rdx,QWORD PTR fs:0x28
73d: 00 00
73f: 74 05 je 746 <main+0x76>
741: e8 3a fe ff ff call 580 <__stack_chk_fail@plt>
746: c9 leave
747: c3 ret
Затем функция __stack_chk_fail()
вызывает __fortify_fail_abort()
, что вызывает __libc_message()
и вывод сообщения об ошибке. и, наконец, выполнение abort()
, которое посылает процессу SIGABRT
сигнал через raise()
, убивая его.
Начало основного адрес виртуальный 0000000000001125
неверный . Это , а не виртуальный адрес, то есть просто смещение внутри двоичного файла, что означает, что код, который вы видите, начинается с байта 0x1125
в самом двоичном файле. Когда двоичный файл затем выполняется, для программы создается область виртуальной памяти, начинающаяся с некоторого (обычно рандомизированного) базового виртуального адреса. Базовый виртуальный адрес будет определять положение main()
и все остальное. Например, main()
будет на base_virtual_addr + 0x1125
. У вас нет способа определить реальный физический адрес, по которому ваша программа загружена в ОЗУ, это знает только операционная система (то есть ядро), и программе пространства пользователя это не нужно.
Вы можете взглянуть на карту виртуальной памяти вашей программы, когда она работает под GDB, с помощью команды info proc mappings
, результат будет примерно таким:
(gdb) info proc mappings
process 11084
Mapped address spaces:
Start Addr End Addr Size Offset objfile
0x555555554000 0x555555555000 0x1000 0x0 /home/marco/Desktop/a.out
0x555555754000 0x555555755000 0x1000 0x0 /home/marco/Desktop/a.out
0x555555755000 0x555555756000 0x1000 0x1000 /home/marco/Desktop/a.out
0x7ffff7a3a000 0x7ffff7bcf000 0x195000 0x0 /lib/x86_64-linux-gnu/libc-2.24.so
0x7ffff7bcf000 0x7ffff7dcf000 0x200000 0x195000 /lib/x86_64-linux-gnu/libc-2.24.so
0x7ffff7dcf000 0x7ffff7dd3000 0x4000 0x195000 /lib/x86_64-linux-gnu/libc-2.24.so
0x7ffff7dd3000 0x7ffff7dd5000 0x2000 0x199000 /lib/x86_64-linux-gnu/libc-2.24.so
0x7ffff7dd5000 0x7ffff7dd9000 0x4000 0x0
0x7ffff7dd9000 0x7ffff7dfc000 0x23000 0x0 /lib/x86_64-linux-gnu/ld-2.24.so
0x7ffff7fcf000 0x7ffff7fd1000 0x2000 0x0
0x7ffff7ff8000 0x7ffff7ffa000 0x2000 0x0 [vvar]
0x7ffff7ffa000 0x7ffff7ffc000 0x2000 0x0 [vdso]
0x7ffff7ffc000 0x7ffff7ffd000 0x1000 0x23000 /lib/x86_64-linux-gnu/ld-2.24.so
0x7ffff7ffd000 0x7ffff7ffe000 0x1000 0x24000 /lib/x86_64-linux-gnu/ld-2.24.so
0x7ffff7ffe000 0x7ffff7fff000 0x1000 0x0
0x7ffffffde000 0x7ffffffff000 0x21000 0x0 [stack]
0xffffffffff600000 0xffffffffff601000 0x1000 0x0 [vsyscall]
В этом случае Вы можете видеть, что моя программа (/home/marco/Desktop/a.out
) запускается с виртуального базового адреса 0x555555554000
.
Что такое макрос __GI_raise? и что в скобках sig=sig@entry=6, добавляет ли g cc таблицу ошибок или что это за отметки от компоновщика?
__GI_raise()
- это реализация raise()
функция. Помните? Я только что упомянул об этом ранее, когда говорил о защитнике стека. В вашем случае gdb
останавливается после того, как программа была убита raise(SIGABRT)
, и показывает точную точку, в которой программа умерла, что находится внутри внутренней функции __GI_raise()
, используемой lib c для доставки сигнала.
Строка sig=sig@entry=6
- это просто хороший способ для GDB сообщить вам, что функция была вызвана с единственным аргументом, установленным в 6
, который является номером сигнала для SIGABRT
.