Определите раздел в скрипте компоновщика ld по умолчанию и выведите его значение - PullRequest
0 голосов
/ 16 февраля 2019

Я хочу определить раздел в скрипте компоновщика и взять его значение из исходного кода во время выполнения.

До сих пор я взял файл сценария gcc linker по умолчанию и добавил свой раздел какследующее:

...
.my_section : { BYTE(0xAA); }
...

После компиляции я вижу раздел:

> gcc -T ls.ld main.c -o main
> objdump -h main
...
...
 27 .my_section   00000001  0000000000a01040  0000000000a01040  00001040  2**0
                  CONTENTS, ALLOC, LOAD, DATA
 28 .comment      00000034  0000000000000000  0000000000000000  00001041  2**0
                  CONTENTS, READONLY

Теперь я хочу напечатать это значение до stdout (и я ожидаю получить 0xAA):

#include <stdio.h>

static volatile unsigned char SECTION __attribute__((section(".my_section")));

int main(){
    printf("hello %d\n", SECTION);
    return 0;
}

Значение, которое я получаю, всегда равно 0. Что я делаю неправильно?

1 Ответ

0 голосов
/ 17 февраля 2019

Что я делаю не так?

Вы заставляете компоновщик выводить в программе две секции, каждая из которых называется .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.

...