Ответ 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);
Обратите внимание на использование операторов "&". Они верны.