Подтвердить что ты не робот

Как разместить переменную с заданным абсолютным адресом в памяти (с GCC)

Компилятор RealView ARM C поддерживает размещение переменной по заданному адресу памяти с использованием атрибута переменной at(address):

int var __attribute__((at(0x40001000)));
var = 4;   // changes the memory located at 0x40001000

Имеет ли GCC аналогичный атрибут переменной?

4b9b3361

Ответ 1

Я не знаю, но вы можете легко создать обходной путь, как это:

int *var = (int*)0x40001000;
*var = 4;

Это не совсем то же самое, но в большинстве ситуаций идеальная замена. Он будет работать с любым компилятором, а не только с GCC.

Если вы используете GCC, я предполагаю, что вы также используете GNU ld (хотя это, конечно, не обязательно), и ld поддерживает размещение переменных там, где вы хотите.

Я думаю, что позволить компоновщику выполнять эту работу довольно часто.

Вдохновленный ответом @rib, я добавлю, что если абсолютный адрес для некоторого управляющего регистра, я бы добавил volatile в определение указателя. Если это просто RAM, это не имеет значения.

Ответ 2

Вы можете использовать атрибуты и ld компоновщик script, чтобы определить желаемый адрес для этого раздела. Это, вероятно, более беспорядочно, чем ваши альтернативы, но это вариант.

Ответ 3

Вы ответили на свой вопрос, в вашей ссылке выше говорится:

С GNU GCC Compiler вы можете использовать только определения указателей для доступа к абсолютным ячейкам памяти. Например:

#define IOPIN0         (*((volatile unsigned long *) 0xE0028000))
IOPIN0 = 0x4;

Кстати, http://gcc.gnu.org/onlinedocs/gcc-4.5.0/gcc/Variable-Attributes.html#Variable%20Attributes

Ответ 4

    extern const uint8_t dev_serial[12];
    asm(".equ dev_serial, 0x1FFFF7E8");
/* or    asm("dev_serial = 0x1FFFF7E8"); */
    ...

    for (i = 0 ; i < sizeof(dev_serial); i++)
        printf((char *)"%02x ", dev_serial[i]);

Ответ 5

Пример сценария минимального запуска компоновщика

Техника была упомянута на: fooobar.com/info/268955/..., но сейчас я приведу конкретный пример.

main.c

#include <stdio.h>

int myvar __attribute__((section(".mySection"))) = 0x9ABCDEF0;

int main(void) {
    printf("adr %p\n", (void*)&myvar);
    printf("val 0x%x\n", myvar);
    myvar = 0;
    printf("val 0x%x\n", myvar);
    return 0;
}

link.ld

SECTIONS
{
  .mySegment 0x12345678 : {KEEP(*(.mySection))}
}

GitHub upstream.

Скомпилируйте и запустите:

gcc -fno-pie -no-pie -o main.out -std=c99 -Wall -Wextra -pedantic link.ld main.c
./main.out

Выход:

adr 0x12345678
val 0x9abcdef0
val 0x0

Итак, мы видим, что он был помещен по нужному адресу.

Я не могу найти, где это описано в руководстве GCC, но следующий синтаксис:

gcc link.ld main.c

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

Требуется -fno-pie -no-pie, поскольку цепочка инструментов Ubuntu теперь настроена на генерацию исполняемых файлов PIE по умолчанию, что заставляет ядро Linux каждый раз размещать исполняемый файл по другому адресу, что мешает нашему эксперименту. См. также: Что такое опция -fPIE для позиционно-независимых исполняемых файлов в gcc и ld?

TODO: компиляция выдает предупреждение:

/usr/bin/x86_64-linux-gnu-ld: warning: link.ld contains output sections; did you forget -T?

Я что-то не так делаю? Как от этого избавиться? См. также: Как удалить предупреждение: link.res содержит выходные разделы; ты забыл -T?

Протестировано на Ubuntu 18.10, GCC 8.2.0.