Intereting Posts
несоответствие типа указателя / целого в условном выражении Работа с std :: function в библиотеках C как записать значение символа из структуры в последовательный интерфейс и преобразовать в целочисленное значение? Выгода от передачи int по ссылке или по значению? Сигналы автоматического соединения с GtkBuilder, но на GTKmm Parsing Call и Ret с ptrace. fork + exec без обработчиков как показать процесс, как в ps -e Как добавить несколько каталогов заголовков и библиотек в путь поиска в одной команде gcc? В чем смысл спецификатора форматирования% m? Неожиданное расширение знака указателя int32 или 32bit при преобразовании в uint64 определение типа возврата функции в сборке Как разрешить ошибку компоновщика в компиляторе C ++ нужна помощь в поиске, почему переменная счетчика цикла цикла изменяется функцией внутри цикла Объявление и проверка / сравнение (битмаска-) перечислений в Objective-C

Где статические локальные переменные

Где хранятся статические локальные переменные в памяти? Доступ к локальным переменным возможен только внутри функции, в которой они объявлены.

Глобальные статические переменные попадают в сегмент .data.

Если имя статической глобальной и статической локальной переменной одинаково, то как компилятор их отличает?

Статические переменные переходят в тот же сегмент, что и глобальные переменные. Единственное, что отличается между этими двумя, заключается в том, что компилятор «скрывает» все статические переменные от компоновщика: отображаются только имена внешних (глобальных) переменных. Таким образом, компиляторы позволяют статическим переменным с тем же именем существовать в разных единицах перевода. Имена статических переменных остаются известными на этапе компиляции, но затем их данные помещаются в сегмент .data анонимно.

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

Как упоминалось dasblinken, GCC 4.8 ставит локальную статику на то же место, что и глобальные.

Точнее:

  • static int i = 0 продолжается .bss
  • static int i = 1 продолжается .data

Давайте проанализируем один пример Linux x86-64 ELF, чтобы увидеть его сами:

 #include  int f() { static int i = 1; i++; return i; } int main() { printf("%d\n", f()); printf("%d\n", f()); return 0; } 

Чтобы сделать выводы, нам необходимо понять информацию о переseleniumии. Если вы никогда не касались этого, сначала прочитайте этот пост .

Скомпилируйте его:

 gcc -ggdb -c main.c 

Декомпилируйте код с помощью:

 objdump -S main.o 

f содержит:

 int f() { 0: 55 push %rbp 1: 48 89 e5 mov %rsp,%rbp static int i = 1; i++; 4: 8b 05 00 00 00 00 mov 0x0(%rip),%eax # a  a: 83 c0 01 add $0x1,%eax d: 89 05 00 00 00 00 mov %eax,0x0(%rip) # 13  return i; 13: 8b 05 00 00 00 00 mov 0x0(%rip),%eax # 19  } 19: 5d pop %rbp 1a: c3 retq 

Что делает 3 обращения к i :

  • 4 перемещается в eax для подготовки к приращению
  • d перемещает добавочное значение обратно в память
  • 13 перемещает i в eax для возвращаемого значения. Очевидно, что это необязательно, поскольку eax уже содержит его, и -O3 способен удалить это.

Итак, давайте сосредоточимся только на 4 :

 4: 8b 05 00 00 00 00 mov 0x0(%rip),%eax # a  

Давайте посмотрим на данные перемещения:

 readelf -r main.o 

в котором говорится, что ссылки в текстовом разделе будут изменены компоновщиком при создании исполняемого файла.

Это содержит:

 Relocation section '.rela.text' at offset 0x660 contains 9 entries: Offset Info Type Sym. Value Sym. Name + Addend 000000000006 000300000002 R_X86_64_PC32 0000000000000000 .data - 4 

Мы смотрим на .rela.text а не на другие, потому что нас интересуют перемещения .text .

Offset 6 попадает прямо в инструкцию, начинающуюся с байта 4:

 4: 8b 05 00 00 00 00 mov 0x0(%rip),%eax # a  ^^ This is offset 6 

Из нашего знания кодировки команд x86-64:

  • 8b 05mov часть
  • 00 00 00 00 – адресная часть, начинающаяся с байта 6

AMD64 System V ABI Update сообщает нам, что R_X86_64_PC32 действует на 4 байта ( 00 00 00 00 ) и вычисляет адрес как:

 S + A - P 

что значит:

  • S : сегмент, указывающий на: .data
  • A : Added : -4
  • P : адрес байта 6 при загрузке

-P необходимо, потому что GCC использует относительную адресацию RIP , поэтому мы должны уклоняться от позиции в .text

-4 потому что RIP указывает на следующую команду в байте 0xA но P является байтом 0x6 , поэтому нам нужно скидка 4.

Вывод: после ссылки он укажет на первый байт сегмента .data .