Я не специалист по сборке, но код выглядит достаточно ясно, поэтому я попытаюсь объяснить это.
0x0000000000401530 <+0>: push %rbp
0x0000000000401531 <+1>: mov %rsp,%rbp
(выше) Это некая "процедурная процедура""код, который генерирует компилятор, когда оптимизация не включена.Это сохраняет состояние RSP (64-битный указатель стека).
0x0000000000401534 <+4>: sub $0x30,%rsp
Это резервирует дополнительное место в стеке для локальных переменных.
0x0000000000401538 <+8>: mov %ecx,0x10(%rbp)
0x000000000040153b <+11>: mov %rdx,0x18(%rbp)
Это сохраняет argc и argv настек, потому что вы сделали отладочную сборку, поэтому все переменные должны быть в памяти.(Используемое пространство находится над обратным адресом, где зарезервированное пространство для вызывающего абонента main
. Это называется теневым пространством и является функцией соглашения о вызовах Windows x64.)
0x000000000040153f <+15>: callq 0x402170 <__main>
Это вызываеткакая-то ранняя функция инициализации.Он может использовать argc и argv (все еще в регистрах) или не может;мы не можем сказать по коду.
0x0000000000401544 <+20>: mov -0x8(%rbp),%rax
Это загружает неинициализированную стековую память как значение int *p
.Автоматические переменные помещаются в стек.Вы читаете p
, не написав этого сначала, и компилятор просто читает любой мусор или нули, которые уже были в слоте стека, который он выбрал для int *p;
=> 0x0000000000401548 <+24>: movl $0x3,(%rax)
В этой строке задается адрес, который rax
указывает на непосредственное значение 3.
Значение p
находится в rax
, так что это ваш p[0] = 3;
, разыменовывающий любой мусор, который содержит p
.Вы терпите крах, потому что это не указывает на доступную для записи память.(Перезапись некоторого случайного слова в памяти вряд ли будет лучше, но, по крайней мере, ваш код не потерпит крах здесь , возможно, в какой-то момент позже, если значение мусора p
окажется действительным указателем.)
0x000000000040154e <+30>: mov $0x0,%eax
Устанавливает регистр eax
в ноль, а эффективно устанавливает rax
в ноль, тоже .Windows x64 (как и любое стандартное соглашение о вызовах) использует RAX для возвращаемых значений, поэтому он реализует неявный return 0;
в нижней части main
.
0x0000000000401553 <+35>: add $0x30,%rsp
0x0000000000401557 <+39>: pop %rbp
0x0000000000401558 <+40>: retq
Восстанавливает состояния указателя до вызова функции.