Как разместить переменную по заданному абсолютному адресу в памяти (с помощью GCC) - PullRequest
22 голосов
/ 01 ноября 2010

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

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

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

Ответы [ 5 ]

20 голосов
/ 01 ноября 2010

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

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

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

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

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

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

14 голосов
/ 02 ноября 2010

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

7 голосов
/ 01 ноября 2010

Вы ответили на свой вопрос. В вашей ссылке выше указано:

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

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

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

4 голосов
/ 01 июня 2016
    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]);
3 голосов

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

Техника упоминалась по адресу: https://stackoverflow.com/a/4081574/895245, но сейчас я приведу конкретный пример.

main.c

#include <stdio.h>

int __attribute__((section(".mySection"))) myvar = 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.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...