Где макросы, такие как глобальные целые числа хранятся в памяти? - PullRequest
1 голос
/ 31 января 2020

Я пытаюсь понять, где хранятся вещи в памяти, такие как глобальные переменные и переменные c (.data, если не инициализированы в ноль) и т.д. c.

Что я пытаюсь найти / рассмотреть макрос, такой как показано ниже:

#define thisInteger 100

Можно ли это найти с помощью objdump?

Кроме того, если бы мне пришлось назначить это новой переменной, такой как ниже, где бы это было найдено (угадывание в .data):

#define THIS_INTEGER 100

int newVariable = THIS_INTEGER;

Ответы [ 4 ]

5 голосов
/ 31 января 2020

Макросы не являются переменными, поэтому они нигде не хранятся. Когда вы делаете #define thisInteger 100, препроцессор C запускает исходный код и заменяет thisInteger на целочисленный литерал 100. Запрос о том, где хранится thisInteger, аналогичен запросу о том, где хранится 100. Чтобы убедиться в этом, попробуйте что-то вроде &thisInteger. Он не скомпилируется, потому что &100 недопустимо и не имеет смысла.

Можно ли это найти с помощью objdump?

Нет. Предварительная обработка - это операция копирования-вставки, выполняемая перед компиляцией.

Кроме того, если бы я затем назначил это новой переменной, например ниже, где это можно найти

Зависит от того, где вы определяете переменную.

3 голосов
/ 31 января 2020

Компилятор будет получать значение 100 везде, где используется макрос. Скорее всего, он встречается в различных инструкциях машинного кода с использованием адресации в непосредственном режиме , например, при использовании в выражениях выражений, таких как a = a + 100 или f(100).

Компилятор, скорее всего, будет встраивать маленькие константы, подобные этой, по требованию в инструкции, используемые для вычисления выражений, подобных приведенным выше, поэтому, если мы сделаем a = a + thisInteger; и f(thisInteger), вероятно, будут две разные инструкции машинного кода, которые встраивают константу 100 как непосредственную, одну для каждого такого использования. Глобальные данные требуют работы для адресации, в большей степени, чем встраивание небольших непосредственных элементов, поэтому компилятор не будет пытаться разделить 100 между двумя видами использования как глобальные или статические c данные.

Итак, да, вы Вы можете увидеть 100 в objdump, но для многих случаев вам, вероятно, нужно взглянуть на раздел кода (.text), чтобы найти инструкции, которые используют #100 в качестве непосредственного операнда (или #64h, если он напечатан в шестнадцатеричном виде). При разборке вам нужны такие инструкции, как add [rbp+24], #100 или move rdi, #100.

Вы правы в том, что если вы объявите непостоянную глобальную переменную int x = thisInteger;, вы можете найти 100 в данных (.data) раздел с objdump. Но локальная переменная того же объявления будет инициализирована во время выполнения с использованием инструкций машинного кода, что-то вроде mov ??, #100.

3 голосов
/ 31 января 2020

макросы имеют только время компиляции (они предварительно обрабатываются перед компиляцией)

Если вы используете компилятор gcc, вы можете увидеть предварительно обработанный файл C, используя опцию -E g cc. Этот предварительно обработанный файл будет использоваться в фактической компиляции.

Ваш предварительно обработанный пример

  1. , если newVariable имеет stati c или продолжительность хранения потока его инициализируется этим значением до вызова функции main
  2. , если newVariable имеет автоматическую c продолжительность хранения, оно инициализируется этим значением при вызове функции.
1 голос
/ 31 января 2020

попробуйте сами и посмотрите

Отправная точка: так. c

#define THIS_INTEGER 100

int newVariable = THIS_INTEGER;

void fun0 ( void )
{
    static int hello;
    hello = 100;
}
int fun1 ( void )
{
    int hello;
    hello = 100;
    return(hello);
}

препроцессор выполняет поиск и заменяет определения

arm-none-eabi-gcc -save-temps -O2 -c so.c -o so.o

so.i

# 1 "so.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "so.c"



int newVariable = 100;

void fun0 ( void )
{
    static int hello;
    hello = 100;
}
int fun1 ( void )
{
    int hello;
    hello = 100;
    return(hello);
}

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

Выход препроцессора so.i затем передается фактическому компилятору, который производит сборку: so.s

    .cpu arm7tdmi
    .eabi_attribute 20, 1
    .eabi_attribute 21, 1
    .eabi_attribute 23, 3
    .eabi_attribute 24, 1
    .eabi_attribute 25, 1
    .eabi_attribute 26, 1
    .eabi_attribute 30, 2
    .eabi_attribute 34, 0
    .eabi_attribute 18, 4
    .file   "so.c"
    .text
    .align  2
    .global fun0
    .arch armv4t
    .syntax unified
    .arm
    .fpu softvfp
    .type   fun0, %function
fun0:
    @ Function supports interworking.
    @ args = 0, pretend = 0, frame = 0
    @ frame_needed = 0, uses_anonymous_args = 0
    @ link register save eliminated.
    bx  lr
    .size   fun0, .-fun0
    .align  2
    .global fun1
    .syntax unified
    .arm
    .fpu softvfp
    .type   fun1, %function
fun1:
    @ Function supports interworking.
    @ args = 0, pretend = 0, frame = 0
    @ frame_needed = 0, uses_anonymous_args = 0
    @ link register save eliminated.
    mov r0, #100
    bx  lr
    .size   fun1, .-fun1
    .global newVariable
    .data
    .align  2
    .type   newVariable, %object
    .size   newVariable, 4
newVariable:
    .word   100
    .ident  "GCC: (GNU) 9.2.0"

То, что передается в ассемблер, а затем, если вы разберете это, вы получите:

Disassembly of section .text:

00000000 <fun0>:
   0:   e12fff1e    bx  lr

00000004 <fun1>:
   4:   e3a00064    mov r0, #100    ; 0x64
   8:   e12fff1e    bx  lr

Disassembly of section .data:

00000000 <newVariable>:
   0:   00000064 

Эхх, я надеялся, что stati c сохранит его там. Для инициализируемой глобальной переменной это делает .data, если это не было бы .bss. Затем в .data вы можете увидеть 100 (0x64). но это не имеет ничего общего с макросом / определение макроса / определение, просто поместите фактическое значение 100 в фактический скомпилированный код.

Для другого случая, с оптимизацией, в стеке нет переменной или что-либо подобное этому значению помещается в регистр возврата, поэтому в этом случае оно ненадолго живет в регистре.

Если бы stati c работал так, как хотелось бы, что в ретроспективе имеет смысл, что нет. Я надеялся на то, что я называю локальным глобальным. Это локальная переменная, но добавление stati c помещает ее в .bss или .data, а не в стек, а затем надеется увидеть сгенерированный код, который затем помещает 100 в переменную, а затем помещает ее в эту область .data / .bss, которая работает неоптимизированной конечно, но это сложнее читать:

Disassembly of section .text:

00000000 <fun0>:
   0:   e52db004    push    {r11}       ; (str r11, [sp, #-4]!)
   4:   e28db000    add r11, sp, #0
   8:   e59f3018    ldr r3, [pc, #24]   ; 28 <fun0+0x28>
   c:   e3a02064    mov r2, #100    ; 0x64
  10:   e5832000    str r2, [r3]
  14:   e1a00000    nop         ; (mov r0, r0)
  18:   e1a00003    mov r0, r3
  1c:   e28bd000    add sp, r11, #0
  20:   e49db004    pop {r11}       ; (ldr r11, [sp], #4)
  24:   e12fff1e    bx  lr
  28:   00000000    andeq   r0, r0, r0

0000002c <fun1>:
  2c:   e52db004    push    {r11}       ; (str r11, [sp, #-4]!)
  30:   e28db000    add r11, sp, #0
  34:   e24dd00c    sub sp, sp, #12
  38:   e3a03064    mov r3, #100    ; 0x64
  3c:   e50b3008    str r3, [r11, #-8]
  40:   e51b3008    ldr r3, [r11, #-8]
  44:   e1a00003    mov r0, r3
  48:   e28bd000    add sp, r11, #0
  4c:   e49db004    pop {r11}       ; (ldr r11, [sp], #4)
  50:   e12fff1e    bx  lr

Disassembly of section .data:

00000000 <newVariable>:
   0:   00000064    andeq   r0, r0, r4, rrx

Disassembly of section .bss:

00000000 <hello.4142>:
   0:   00000000    andeq   r0, r0, r0

В частности:

   c:   e3a02064    mov r2, #100    ; 0x64
  10:   e5832000    str r2, [r3]

100 записывается в регистр, затем это значение регистра записывается в память, где локальный глобальный привет поскольку fun0 живет в .bss.

макросах / определяет просто поиск и замену, препроцессор будет повторять столько раз, сколько необходимо для различных уровней / слоев макросов, пока все они не будут заменены, ни один из них не существует как написано в предварительно обработанном коде. Затем это отправляется компилятору.

ЗНАЧЕНИЕ 100 в этом случае видно в конечном выводе, но это зависит от того, как вы его использовали, как оно представлено или где оно хранится.

...