Доступ к символам, определенным в скрипте компоновщика, приложением - PullRequest
19 голосов
/ 06 декабря 2011

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

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 соответственно, но address_1 и address_2 имеют значение 0.

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

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

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

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

Ответы [ 3 ]

19 голосов
/ 03 ноября 2016

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

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

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

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

extern int foo;

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

_foo = 1000;

В остальных примерах этоПредполагается, что преобразование имен не выполнялось.

Когда символ объявляется на языке высокого уровня, таком как C, происходят две вещи.Во-первых, компилятор резервирует достаточно места в памяти программы для хранения значения символа.Во-вторых, компилятор создает запись в таблице символов программы, которая содержит символ address .то есть таблица символов содержит адрес блока памяти, содержащего значение символа.Так, например, следующее объявление 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 для выполнения копирования будетbe:

extern char start_of_ROM, end_of_ROM, start_of_FLASH;

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

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

4 голосов
/ 16 ноября 2013

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

extern void _region_RAM_START;

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

1 голос
/ 06 марта 2018

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

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;

}
...