Разве одного «использованного» тега может быть недостаточно?
Недостаточно и не нужно. Это не имеет отношения.
Согласно приведенной вами документации G CC, атрибут used
применим к определениям stati c переменных. И в качестве ответа, который теперь удален автором, указано, что ваш ApplicationID
не является stati c, поэтому атрибут used
не имеет никакого эффекта.
Здесь:
/* app_id_extern.c */
#include <stdint.h>
const uint8_t ApplicationID[16] = {
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x12, 0x34, 0x00, 0x00
};
у нас ApplicationID
определено по умолчанию как extern
переменная. Класс хранения по умолчанию для переменной файловой области, такой как ApplicationID
, равен extern
. Соответственно, компилятор сгенерирует объектный файл, в котором определение ApplicationID
предоставляется для связывания, как мы можем видеть:
$ gcc -c app_id_extern.c
$ readelf -s app_id_extern.o
Symbol table '.symtab' contains 10 entries:
Num: Value Size Type Bind Vis Ndx Name
...
9: 0000000000000000 16 OBJECT GLOBAL DEFAULT 4 ApplicationID
В объектном файле ApplicationID
является 16-байтовым GLOBAL
символ в разделе № 4 (в данном случае это .rodata
). Привязка GLOBAL
означает, что компоновщик stati c может видеть этот символ.
А здесь:
/* app_id_static.c */
#include <stdint.h>
static const uint8_t ApplicationID[16] = {
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x12, 0x34, 0x00, 0x00
};
, мы ApplicationID
явно определены как переменная static
. Соответственно, компилятор сгенерирует объектный файл, в котором определение ApplicationID
является , а не , доступным для связывания, как мы также можем видеть:
$ gcc -c app_id_static.c
$ readelf -s app_id_static.o
Symbol table '.symtab' contains 10 entries:
Num: Value Size Type Bind Vis Ndx Name
...
6: 0000000000000000 16 OBJECT LOCAL DEFAULT 4 ApplicationID
...
В этом объектном файле ApplicationID
- это 16-байтовый символ LOCAL
в разделе .rodata
. Привязка LOCAL
означает, что компоновщик * stai c не может увидеть этот символ.
Компилятор всегда выдает в объектном файле определение переменной extern
, например, ApplicationID
в app_id_extern.c
, даже если на это определение нет ссылки в объектном файле, поскольку внешнее определение будет доступно на компоновщик, и, следовательно, может ссылаться во время компоновки из других объектных файлов, для всего, что компилятор может знать.
Но если переменная static
, то компилятор знает, что ее определение недоступно для связи. Поэтому, если он может определить, что определение не указано в самом объектном файле, он может сделать вывод, что определение является избыточным, и вообще не выдавать его в объектный файл. Вот так:
$ gcc -O1 -c app_id_static.c
На этот раз мы просим компилятор выполнить минимальные оптимизации. И тогда
$ readelf -s app_id_static.o
Symbol table '.symtab' contains 8 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000000000 0 FILE LOCAL DEFAULT ABS app_id_static.c
2: 0000000000000000 0 SECTION LOCAL DEFAULT 1
3: 0000000000000000 0 SECTION LOCAL DEFAULT 2
4: 0000000000000000 0 SECTION LOCAL DEFAULT 3
5: 0000000000000000 0 SECTION LOCAL DEFAULT 5
6: 0000000000000000 0 SECTION LOCAL DEFAULT 6
7: 0000000000000000 0 SECTION LOCAL DEFAULT 4
не имеющее ссылки определение ApplicationID
больше не присутствует в объектном файле вообще . Он был оптимизирован.
Теперь для некоторых необычных приложений мы можем захотеть, чтобы компилятор выдал определение символа в объектном файле, который не ссылается на него, и скрывают его от Stati c компоновщик. Вот где в игру вступает атрибут used
:
/* app_id_static_used .c */
#include <stdint.h>
static const uint8_t ApplicationID[16] __attribute__((used,section(".rodata.$AppID"))) = {
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x12, 0x34, 0x00, 0x00
};
Еще раз мы компилируем с оптимизацией -O1:
$ gcc -O1 -c app_id_static_used.c
$ readelf -s app_id_static_used.o
Symbol table '.symtab' contains 10 entries:
Num: Value Size Type Bind Vis Ndx Name
...
6: 0000000000000000 16 OBJECT LOCAL DEFAULT 4 ApplicationID
...
Но на этот раз, благодаря атрибуту used
, LOCAL
определение ApplicationID
вновь появляется в разделе # 4 (который в этом объектном файле .rodata.$AppID
)
Так работает атрибут used
. Это влияет на поведение компилятора: оно не влияет на компоновщик.
Мы еще не сделали никакой связи. Давайте сделаем это сейчас.
/* hello_world.c */
#include <stdio.h>
int main(void)
{
puts("Hello world!")
return 0;
}
Эта программа не ссылается на ApplicationID
, но мы введем app_id_static_used.o
для связи независимо от:
$ gcc -O1 -c hello_world.c
$ gcc -o hello hello_world.o app_id_static_used.o -Wl,-gc-sections,-Map=mapfile.txt
В связи, Я попросил удалить неиспользуемые разделы ввода и вывести файл карты (-Wl,-gc-sections,-Map=mapfile.txt
)
В файле карты мы находим:
Mapfile.txt
...
Discarded input sections
...
.rodata.$AppID
0x0000000000000000 0x10 app_id_static_used.o
...
Компоновщик отбросил секцию .rodata.$AppID
, введенную из app_id_static_used.o
, поскольку в программе нет ссылок на символы, определенные в этой секции. С атрибутом used
мы заставили компилятор выдать определение этого символа static
в app_id_static_used.o
. Это не заставляет компоновщика нуждаться в нем или сохранять его в исполняемом файле.
Если мы переключаемся с app_id_static_used.c
на:
/* app_id_extern_used.c */
#include <stdint.h>
const uint8_t ApplicationID[16] __attribute__((used,section(".rodata.$AppID"))) = {
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x12, 0x34, 0x00, 0x00
};
, тогда мы делаем то, что вы сделали , применяя атрибут used
к определению extern
. Атрибут used
не имеет никакого эффекта в этом случае, потому что компилятор обязан выдавать определение extern
в любом случае. И компоновщик все еще отбрасывает секцию ввода .rodata.$AppID
из Исполняемый файл, если программа не ссылается на что-либо в нем.
Пока что ваш исходный файл идентификатора приложения также может быть:
/* app_id_extern_section.c */
#include <stdint.h>
const uint8_t ApplicationID[16] __attribute__((section(".rodata.$AppID"))) = {
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x12, 0x34, 0x00, 0x00
};
И что вам тогда нужно сделать, это сообщить компоновщик , для которого вы хотите сохранить определение символа ApplicationID
, даже если на него не ссылается ваша программа, и даже если даже неиспользуемые разделы удалены.
Чтобы достичь этого, используйте опцию компоновщика --undefined=ApplicationID
. Это заставит компоновщика с самого начала предположить, что компоновщик вашей программы обнаружил неопределенную ссылку на ApplicationID
, и заставит компоновщика найти и связать его определение, если таковой имеется в любом входном файле. Таким образом:
$ gcc -O1 -c app_id_extern_section.c
$ gcc -o hello hello_world.o app_id_extern_section.o -Wl,-gc-sections,--undefined=ApplicationID
Теперь программа содержит определение ApplicationID
, хотя и не ссылается на него:
$ readelf -s hello | grep ApplicationID
58: 0000000000002010 16 OBJECT GLOBAL DEFAULT 18 ApplicationID
Раздел # 18 - это раздел .rodata
программы:
$ readelf --sections hello | grep '.rodata'
[18] .rodata PROGBITS 0000000000002000 00002000
Наконец, обратите внимание, что секция ввода .rodata.$AppID
из app_id_extern_section.o
была объединена с секцией вывода .rodata
, поскольку скрипт компоновщика по умолчанию для компоновщика задает:
.rodata : { *(.rodata .rodata.* .gnu.linkonce.r.*) }
т.е. все входные секции, соответствующие .rodata
, .rodata.*
или .gnu.linkonce.r.*
, будут выведены в .rodata
. Это означает, что даже:
__attribute__((section(".rodata.$AppID")))
является избыточным. Таким образом, исходный файл app-id может быть просто тем, с которого я начал, app_id_extern.c
, а опция связывания --undefined=ApplicationID
- это все, что необходимо для сохранения в программе символа без ссылки. Если ваш компоновщик не отличается в этом отношении, вы найдете то же самое.