Символы доступа, определенные в компоновщике script по заявке

В моем файле компоновщика script я определил два символа

define symbol _region_RAM_start__     = 0xC0000000;
define symbol _region_RAM_end__       = 0xC00fffff; 

а затем я экспортировал их, как показано ниже

export symbol _region_RAM_start__;
export symbol _region_RAM_end__;

Из кода приложения я пытаюсь получить доступ к этим символам

extern const unsigned int _region_RAM_start__;
extern const unsigned int _region_RAM_end__;
....
int GetRAMSize()
{
    int size = 0;
    unsigned int address_1 = _region_RAM_start__;
    unsigned int address_2 = _region_RAM_end__;
    size = address_2 - address_1 + 1U;
    return size;
}

Теперь я ожидал, что возвращаемое значение будет 0x00100000, однако все, что я получаю, равно 0. Поэтому, когда я обратился к отладчику, я заметил, что _region_RAM_start__ и _region_RAM_end__ имеют значения 0xC0000000 и 0xC00fffff соответственно, но адрес_1 и address_2 имеют значение 0.

Оптимизация компилятора установлена ​​на "Нет". Это уже давно подтачивало меня. Есть ли что-то очень очевидное, что я здесь отсутствует (кроме того, что я не должен делать это в первую очередь)?

Решение Благодаря н.м. для ответа

  unsigned int address_1 = (unsigned int) (&_region_RAM_start__);

В противном случае адреса_1 и address_2 оба содержат значения мусора (т.е. значения, доступные по адресу 0xC0000000 и 0xC00fffff, но мусор с точки зрения этого кода)

Ответ 1

Это немного стар, но я все равно отвечу...

Из руководства ld:

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

Прежде чем идти дальше, важно отметить, что компиляторы часто преобразуют имена в исходном коде в разные имена, когда они хранятся в таблице символов. Например, компиляторы Фортрана обычно добавляют или добавляют подчеркивание, а C++ выполняет обширное искажение имени. Поэтому может возникнуть несоответствие между именем переменной, используемой в исходном коде, и именем той же переменной, которая определена в сценарии компоновщика. Например, в C переменная сценария компоновщика может называться:

extern int foo;

Но в скрипте компоновщика это может быть определено как:

_foo = 1000;

Однако в оставшихся примерах предполагается, что преобразование имен не происходило.

Когда символ объявляется на языке высокого уровня, таком как C, происходят две вещи. Во-первых, компилятор резервирует достаточно места в памяти программы для хранения значения символа. Во-вторых, компилятор создает запись в таблице символов программы, которая содержит адрес символа. то есть таблица символов содержит адрес блока памяти, содержащего значение символа. Так, например, следующее объявление C в области видимости файла:

int foo = 1000;

создает запись с именем "foo" в таблице символов. Эта запись содержит адрес блока памяти размера int, где изначально хранится число 1000.

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

foo = 1;

ищет символ foo в таблице символов, получает адрес, связанный с этим символом, и затем записывает значение 1 в этот адрес. В то время как:

int * a = & foo;

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

В отличие от этого, объявления символов сценариев компоновщика создают запись в таблице символов, но не выделяют для них память. Таким образом, они являются адресом без значения. Так, например, определение сценария компоновщика:

foo = 1000;

создает запись в таблице символов с именем @samp {foo}, которая содержит адрес ячейки памяти 1000, но по адресу 1000 ничего особенного не сохраняется. Это означает, что вы не можете получить доступ к значению символа, определенного сценарием компоновщика, - он не имеет значения - все, что вы можете сделать, это использовать адрес определенного в скрипте компоновщика символа.

Следовательно, когда вы используете определенный в скрипте компоновщик символ в исходном коде, вы всегда должны брать адрес символа и никогда не пытаться использовать его значение. Например, предположим, что вы хотите скопировать содержимое раздела памяти с именем .ROM в раздел с именем .FLASH, а скрипт компоновщика содержит следующие объявления:

start_of_ROM   = .ROM;
end_of_ROM     = .ROM + sizeof (.ROM);
start_of_FLASH = .FLASH;

Тогда исходный код C для выполнения копии будет:

extern char start_of_ROM, end_of_ROM, start_of_FLASH;

memcpy (& start_of_FLASH, & start_of_ROM, & end_of_ROM - & start_of_ROM);

Обратите внимание на использование операторов "&". Они верны.

Ответ 2

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

extern void _region_RAM_START;

тогда & _region_RAM_START будет иметь соответствующий тип 'void *'.

Ответ 3

Ниже код должен работать как положено:

extern const volatile unsigned int _region_RAM_start__;

extern const volatile unsigned int _region_RAM_end__;

....
int GetRAMSize()

{

int size = 0;

unsigned int address_1 = &_region_RAM_start__;

unsigned int address_2 = &_region_RAM_end__;

size = address_2 - address_1 + 1U;

return size;

}

Ответ 4

extern const volatile unsigned int _region_RAM_start__;

extern const volatile unsigned int _region_RAM_end__;

.... int GetRAMSize()

{

int size = 0;

неподписанный int address_1 = & _region_RAM_start__;

неподписанный int address_2 = & _region_RAM_end__;

размер = адрес_2 - адрес_1 + 1U;

возвращаемый размер;

}

Работа с компилятором IAR