Search code examples
gcclinkerldlinker-scripts

Define a section in the ld default linker script and print its value


I want to define a section in a linker script and take its value from the source code at runtime.

So far, I have taken the default gcc linker script file and I have added my section as following:

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

After compiling, I can see the section:

> 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

Now, I want to print that value to stdout (and I'm expecting to obtain 0xAA):

#include <stdio.h>

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

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

The value I obtain is always 0. What I'm doing wrong?


Solution

  • What I'm doing wrong?

    You are forcing the linker to output in the program two sections, each called .my_section.

    One of them is caused by:

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

    in main.c. In this .my_section, a symbol named SECTION is statically defined that addresses a char that by default is statically initialized = 0. When you

    printf("hello %d\n", SECTION)
    

    you are of course printing the integer at that 0-initialized symbol.

    The other .my_section is caused by the:

    .my_section : { BYTE(0xAA); }
    

    in ls.ld. This second .my_section begins with a byte = 0xAA but is never accessed by the program.

    Here is an illustration. I have:

    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;
    }
    

    and I have a linker-script ls.ld that is my gcc default linker script with:

    .my_section : { BYTE(0xAA); }
    

    appended last in SECTIONS.

    Compile, link and run:

    $ gcc -Wall -Wextra -T ls.ld -o prog main.c
    $ ./prog
    hello !
    

    Look at the section details of 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
      ...
    

    Section 24 is called .my_section and so is section 27. The local symbol MY_SECTION:

    $ readelf -s prog | grep 'MY_SECTION'
    37: 0000000000004010     1 OBJECT  LOCAL  DEFAULT   24 MY_SECTION
    

    is defined in section 24.

    Then look at the disassembly:

    $ 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)
    ...
    ...
    

    The first one, starting with 0x21 = !, is the one created in main.c and accessed by the program. The second one, starting with 0xaa, is the one created by the linker script and not accessed by the program.

    Choose one way of outputting your .my_section or the other:-

    You can do it in your source code with:

    static volatile unsigned char MY_SECTION __attribute__((section(".my_section"))) = 0xAA;
    

    Or you can do it in the linker script as @MichaelPetch commented, like:

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

    and access the section in a program like:

    $ 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
    

    But it is not actually necessary to customize the linker script in order to obtain the address of a custom section in a program. See:

    $ 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 !
    

    Seeing extern declarations of the form __start_<section_name or __stop_<section_name>, the linker will automatically place these symbols at the start and end respectively of section <section_name>.

    And should you want to compile and link multiple source files that all access the same custom section my_section in a program, you can simply define symbols attributed to section my_section in the several source files and the linker, with the default linker script, with merge all the sections called my_section in the input object files into a single output my_section in the program. (Just as it merges, e.g. all the .text sections of the input object files into a single .text section of program). See:

    $ 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
    

    and:

    $ readelf -t prog | grep my_section
      [24] my_section
    

    there is only one section, 24, called my_section in the program, which:

    $ 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
    

    contains the definitions of both foo and boo.