Что я делаю не так?
Вы заставляете компоновщик выводить в программе две секции, каждая из которых называется .my_section
.
Один из них вызван:
static volatile unsigned char SECTION __attribute__((section(".my_section")));
в main.c
.В этом .my_section
статически определяется символ с именем SECTION
, который обращается к char
, который по умолчанию статически инициализирован = 0. Когда вы
printf("hello %d\n", SECTION)
, вы, конечно, печатаете целое число при этомИнициализированный 0 символ.
Другой .my_section
вызван:
.my_section : { BYTE(0xAA); }
in ls.ld
.Эта вторая .my_section
начинается с байта = 0xAA
, но никогда не используется программой.
Вот иллюстрация.У меня есть:
main.c
#include <stdio.h>
static volatile unsigned char MY_SECTION __attribute__((section(".my_section"))) = '!';
int main(){
printf("hello %c\n", MY_SECTION);
return 0;
}
, и у меня есть скрипт компоновщика ls.ld
, который является моим gcc
скриптом компоновщика по умолчанию с:
.my_section : { BYTE(0xAA); }
добавлено последним в SECTIONS
.
Компиляция, связывание и запуск:
$ gcc -Wall -Wextra -T ls.ld -o prog main.c
$ ./prog
hello !
Посмотрите подробности раздела prog
:
$ readelf -t prog
There are 31 section headers, starting at offset 0x3990:
Section Headers:
[Nr] Name
Type Address Offset Link
Size EntSize Info Align
Flags
...
[24] .my_section
PROGBITS PROGBITS 0000000000004010 0000000000003010 0
0000000000000001 0000000000000000 0 1
[0000000000000003]: WRITE, ALLOC
[25] .bss
NOBITS NOBITS 0000000000004011 0000000000003011 0
0000000000000007 0000000000000000 0 1
[0000000000000003]: WRITE, ALLOC
[26] .comment
PROGBITS PROGBITS 0000000000000000 0000000000003019 0
0000000000000023 0000000000000001 0 1
[0000000000000030]: MERGE, STRINGS
[27] .my_section
PROGBITS PROGBITS 0000000000006018 0000000000003018 0
0000000000000001 0000000000000000 0 1
[0000000000000003]: WRITE, ALLOC
...
Раздел 24 называется .my_section
, как и раздел 27. Локальный символ MY_SECTION
:
$ readelf -s prog | grep 'MY_SECTION'
37: 0000000000004010 1 OBJECT LOCAL DEFAULT 24 MY_SECTION
определен в разделе 24
.
Затем посмотрите наразборка:
$ objdump --disassemble-all prog
prog: file format elf64-x86-64
...
...
Disassembly of section .my_section:
0000000000004010 <__TMC_END__>:
4010: 21 .byte 0x21
...
...
Disassembly of section .my_section:
0000000000006018 <.my_section>:
6018: aa stos %al,%es:(%rdi)
...
...
Первый, начиная с 0x21
= !
, - это тот, который создан в main.c
и доступен для программы.Второй, начинающийся с 0xaa
, - тот, который создан сценарием компоновщика и не доступен для программы.
Выберите один из способов вывода .my_section
или другой: -
Вы можете сделать это в своем исходном коде с помощью:
static volatile unsigned char MY_SECTION __attribute__((section(".my_section"))) = 0xAA;
Или вы можете сделать это в скрипте компоновщика, как прокомментировал @MichaelPetch, например:
.my_section : { my_section_addr = .; BYTE(0xAA); }
и получить доступ к разделу впрограмма наподобие
$ cat main1.c
#include <stdio.h>
extern unsigned char my_section_addr[];
int main(){
printf("section `.my_section` starts at %p and the 1st byte is %x\n",
my_section_addr, (unsigned int)my_section_addr[0]);
return 0;
}
$ gcc -Wall -Wextra -T ls.ld -o prog main1.c
$ ./prog
section `.my_section` starts at 0x560a32964018 and the 1st byte is aa
Но на самом деле нет необходимости настраивать скрипт компоновщика для получения адреса пользовательского раздела в программе.См .:
$ cat main2.c
#include <stdio.h>
static unsigned char pling __attribute__((section("my_section"))) = '!';
extern unsigned char __start_my_section;
extern unsigned char __stop_my_section;
static char * p_my_section_start = &__start_my_section;
static char * p_my_section_end = &__stop_my_section;
int main(){
printf("section `my_section` starts at %p, ends at %p, and the 1st byte is %c\n",
p_my_section_start, p_my_section_end, p_my_section_start[0]);
return 0;
}
$ gcc -o prog main2.c
$ ./prog
section `my_section` starts at 0x55db7b0fb020, ends at 0x55db7b0fb021, and the 1st byte is !
При просмотре extern
объявлений в форме __start_<section_name
или __stop_<section_name>
компоновщик автоматически разместит эти символы в начале и конце соответственно раздела <section_name>
.
И если вы хотите скомпилировать и связать несколько исходных файлов, которые все обращаются к одному и тому же пользовательскому разделу my_section
в программе, вы можете просто определить символы, относящиеся к разделу my_section
в нескольких исходных файлах и компоновщике, по умолчаниюскрипт компоновщика, с помощью которого объединяются все разделы с именем my_section
во входных объектных файлах в один вывод my_section
в программе.(Так же, как он объединяет, например, все секции .text
входных объектных файлов в одну секцию .text
программы).См .:
$ cat foo.c
#include <stdio.h>
unsigned int foo __attribute__((section("my_section"))) = 0xf00;
$ cat boo.c
#include <stdio.h>
unsigned int boo __attribute__((section("my_section"))) = 0xb00;
$ cat main3.c
#include <stdio.h>
extern unsigned int foo;
extern unsigned int boo;
int main(){
printf("foo=%x, boo=%x\n",foo,boo);
return 0;
}
$ gcc -Wall -o prog main3.c foo.c boo.c
$ ./prog
foo=f00, boo=b00
и:
$ readelf -t prog | grep my_section
[24] my_section
в программе имеется только один раздел 24, называемый my_section
, который:
$ readelf -s prog | egrep '(foo|boo)'
36: 0000000000000000 0 FILE LOCAL DEFAULT ABS foo.c
37: 0000000000000000 0 FILE LOCAL DEFAULT ABS boo.c
59: 0000000000004010 4 OBJECT GLOBAL DEFAULT 24 foo
66: 0000000000004014 4 OBJECT GLOBAL DEFAULT 24 boo
содержитопределения foo
и boo
.