This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

Negative Addend in ELF relocation data

Other Parts Discussed in Thread: MSP430F1611

In the relocation entries (.rela.text section in ELF file) we see a negative addend: rodata - 2. The symbol .rodata is located at 0x4000. Thus the relocation address results in 0x3FFE. Contrarily to this the code uses the correct address 0x400c. The relocation information seems to be wrong.

Using gcc_msp430_4.9.14r1_167 : msp430-elf-gcc  -mmcu=msp430f1611 main.c

msp430-elf-objdump -sD:

00005560 <__do_global_ctors_aux>:
    5560:	0a 12       	push	r10		;
    5562:	1c 42 0c 40 	mov	&0x400c,r12	;0x400c
    5566:	3c 93       	cmp	#-1,	r12	;r3 As==11
    5568:	08 24       	jz	$+18     	;abs 0x557a

msp430-elf-readelf -a:

.rela.text
 Offset     Info    Type            Sym.Value  Sym. Name + Addend
...
00005564  00000203 R_MSP430_16       00004000   .rodata - 2
...

2260.main.c
Fullscreen
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <stdint.h>
int main (int argc, char *argv[])
{
uint8_t key[32];
uint8_t buf[16], i;
/* put a test vector */
for (i = 0; i < sizeof(buf);i++) buf[i] = i * 16 + i;
for (i = 0; i < sizeof(key);i++) key[i] = i;
return 0;
}
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

  • Hi Felix,

    The short answer is: "Here be dragons. But trust me, it works".

    The long answer is a bit more complicated...

    There are a couple of things to explain here. First of all this
    behaviour has nothing to do with the source file that you are
    compiling. Rather it is all part of the "magic" that gcc, the linker
    scripts and the runtime support code use in order to run global
    constructor and destructor functions correctly.

    The code you are looking at comes from crtend.o[1], where the function
    __do_global_ctors_aux is responsible for running *backwards* through a
    table of function pointers, executing each in turn. The table is of
    an unknown length, but the code does know five things:

    * The table always has at least one entry.
    * The first entry in the table is the value -1.
    * The table is NULL terminated.
    * The table starts at a symbol called __CTOR_LIST__.
    * The table ends at symbol called __CTOR_END__.

    The linker script is responsible for creating this table. It places
    the contents of any section called .ctors into the table, in the order
    in which the files containing them appear on the linker command line.

    The reason for the strange relocation with the negative offset is that
    the code in __do_global_ctors_aux is being a bit sneaky. It wants to
    walk the table backwards, starting at the last non-NULL entry, and
    going until it encounters a function pointer of -1. So it loads the
    value at "__CTOR_END__ - 2", checks to see if this is -1, and if not,
    calls the address and then loops backwards.

    Now, this is where things get a bit confusing...

    The assembler however tries to simplify things by converting
    relocations against arbitrary symbols into relocations against section
    symbols. (This helps to reduce the size of the symbol table in the
    final executable). So it translates the relocation against
    __CTOR_END__ -2 into a relocation against the .ctors section symbol
    instead. Except that the .ctors section is mapped into .rodata
    section, so in fact the relocation is made against the .rodata section
    symbol. (Well in the version of the toolchain that you are using.
    In more modern versions of the tools, the relocation remains against
    the .ctors section symbol. But that is a side issue).

    So now you have a relocation against ".rodata - 2". This works
    because, for the crtend.o file, the .rodata section, .ctors section,
    and the __CTOR_END__ symbol all have the same address. When crtend.o
    is linked in to the final executable the linker script will ensure
    that crtend.o's .ctors section is placed as the last entry in the the
    constructor table, and so the reference to .rodata - 2 will actually
    resolve to "end of the constructor table - 2". And so it magically
    works.

    The moral of this story is - don't muck with this code unless you
    really really have to!

    Cheers
    Nick


    [1] crtend.o is built from various source files, but it mostly
    involves libgcc/crtstuff.c and libgcc/gbl-ctors.h. Reading them can
    be quite enlightening, should you have the time.
  • Hi Nick,

    thanks for this good and detailed explanation. There are still some questions remaining.

    1. In general, ".rodata - 2" which addresses something outside of the section .rodata seems weird.

    > This works because, for the crtend.o file, the .rodata section, .ctors section, and the __CTOR_END__ symbol all have the same address.

    2. I wonder why __CTORS_END__ and .rodata should have the same address. The table has at least 2 values: -1 and 0. Thus __CTORS_LIST__ points to -1 and __CTORS_END points to 0. I would have expected that __CTORS_END__ = .rodata + 2.

    > When crtend.o is linked in to the final executable the linker script will ensure that crtend.o's .ctors section is placed as the last entry in the the constructor table, and so the reference to .rodata - 2 will actually resolve to "end of the constructor table - 2".

    3. Ok, the linker assembles the obj-files. I would expect that the relocation entries have to be updated while the rodata sections from different obj-files are merged. The correct value is rodata + c in my example.

    rodata = 0x4000
    __CTORS_LIST__ = 0x400c
    __CTORS_END__ = 0x400e

  • Hi Felix,

    > 1. In general, ".rodata - 2" which addresses something outside of the
    > section .rodata seems weird.

    True - this does not normally happen. But the global constructor /
    destructor code is a special case.

    >> This works because, for the crtend.o file, the .rodata section,
    >> .ctors section, and the __CTOR_END__ symbol all have the same
    >> address.
    >
    > 2. I wonder why __CTORS_END__ and .rodata should have the same
    > address. The table has at least 2 values: -1 and 0. Thus
    > __CTORS_LIST__ points to -1 and __CTORS_END points to 0. I would have
    > expected that __CTORS_END__ = .rodata + 2.

    Ah - but the table is built up by combining several object files
    together and it only comes together in its final version once the
    executable has been fully linked.

    In the crtend.o object file only *part* of the table is present - the
    NULL entry and the __CTOR_END__ symbol. The rest of the table is not
    there. So, for the crtend.o object file only, the .ctors section
    symbol, .rodata section symbol and address of the NULL entry all
    coincide. Once crtend.o is linked with other files the .ctors section
    symbol and .rodata section symbol will be adjusted to allow for the
    contents of those files. So the relocation which used to refer to
    ".rodata - 2" will be adjusted to refer to ".rodata + c", for
    example.


    >> When crtend.o is linked in to the final executable the linker script
    >> will ensure that crtend.o's .ctors section is placed as the last
    >> entry in the the constructor table, and so the reference to .rodata
    >> - 2 will actually resolve to "end of the constructor table - 2".
    >
    > 3. OK, the linker assembles the obj-files.

    [You have to be careful with the words you use here. "assembles"
    might make the reader think that the assembler is involved at this
    point, which obviously it is not. May I suggest "organises" instead ?]

    > I would expect that the
    > relocation entries have to be updated while the rodata sections from
    > different obj-files are merged. The correct value is rodata + c in my
    > example.

    Exactly right.

    Cheers
    Nick
  • to 1: ok, we have to believe :)

    to 2: ok, first entry -1 of table is only introduced by the linker.

    to 3:
    yes, I meant something like "organise".

    You agree the linker should update the -2. But I see the -2 in the executable (thus after linking).

  • > You agree the linker should update the -2. But I see the -2 in the executable (thus after linking).

    But there should not be *any* relocations left in the executable after linking. They all should have been resolved. Just exactly how are you linking this executable ? With the gcc command you described at the start ? If so, then please could you upload a copy of the executable so that I can examine it ?
  • I use -Wl,--emit-relocs to let the linker keep the relocation information in the executable. I attached the output of readelf which shows the rodata and -2 stuff (search for "rodata - 2") and the .elf file.


    main_readelf.txt
    Fullscreen
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    ELF Header:
    Magic: 7f 45 4c 46 01 01 01 ff 00 00 00 00 00 00 00 00
    Class: ELF32
    Data: 2's complement, little endian
    Version: 1 (current)
    OS/ABI: Standalone App
    ABI Version: 0
    Type: EXEC (Executable file)
    Machine: Texas Instruments msp430 microcontroller
    Version: 0x1
    Entry point address: 0x4014
    Start of program headers: 52 (bytes into file)
    Start of section headers: 24644 (bytes into file)
    Flags: 0xe: architecture variant: MSP430x14
    Size of this header: 52 (bytes)
    Size of program headers: 32 (bytes)
    Number of program headers: 3
    Size of section headers: 40 (bytes)
    Number of section headers: 16
    Section header string table index: 13
    Section Headers:
    [Nr] Name Type Addr Off Size ES Flg Lk Inf Al
    [ 0] NULL 00000000 000000 000000 00 0 0 0
    [ 1] __reset_vector PROGBITS 0000fffe 0018ea 000002 00 A 0 0 1
    [ 2] .rela__reset_vect RELA 00000000 005510 00000c 0c I 14 1 4
    [ 3] .rodata PROGBITS 00004000 000094 000014 00 WA 0 0 4
    [ 4] .rela.rodata RELA 00000000 00551c 00000c 0c I 14 3 4
    [ 5] .text PROGBITS 00004014 0000a8 0017be 00 AX 0 0 2
    [ 6] .rela.text RELA 00000000 005528 000ad4 0c I 14 5 4
    [ 7] .data PROGBITS 00001100 001868 000080 00 WA 0 0 2
    [ 8] .rela.data RELA 00000000 005ffc 000048 0c I 14 7 4
    [ 9] .bss NOBITS 00001180 0018e8 000010 00 WA 0 0 2
    [10] .noinit PROGBITS 00001190 0018ec 000000 00 W 0 0 1
    [11] .MP430.attributes MSP430_ATTRIBUT 00000000 0018ec 0001fa 00 0 0 1
    [12] .comment PROGBITS 00000000 001ae6 000056 01 MS 0 0 1
    [13] .shstrtab STRTAB 00000000 001b3c 00007a 00 0 0 1
    [14] .symtab SYMTAB 00000000 001bb8 002490 10 15 382 4
    [15] .strtab STRTAB 00000000 004048 0014c5 00 0 0 1
    Key to Flags:
    W (write), A (alloc), X (execute), M (merge), S (strings)
    I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)
    O (extra OS processing required) o (OS specific), p (processor specific)
    There are no section groups in this file.
    Program Headers:
    Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
    LOAD 0x000000 0x00003f6c 0x00003f6c 0x01866 0x01866 RWE 0x4
    LOAD 0x001868 0x00001100 0x000057d2 0x00080 0x00090 RW 0x4
    LOAD 0x0018ea 0x0000fffe 0x0000fffe 0x00002 0x00002 R 0x4
    Section to Segment mapping:
    Segment Sections...
    00 .rodata .text
    01 .data .bss
    02 __reset_vector
    There is no dynamic section in this file.
    Relocation section '.rela__reset_vector' at offset 0x5510 contains 1 entries:
    Offset Info Type Sym.Value Sym. Name + Addend
    0000fffe 0001aa05 R_MSP430_16_BYTE 00004014 __crt0_start + 0
    Relocation section '.rela.rodata' at offset 0x551c contains 1 entries:
    Offset Info Type Sym.Value Sym. Name + Addend
    00004000 00000405 R_MSP430_16_BYTE 00001100 .data + 0
    XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
    main.elf.123

  • Why are you keeping the relocations? It is highly likely there is a better way to accomplish what you need without using the relocations.
  • @Archaeologist: We are researching on fault tolerance methodologies and want to relocate the code after building. Thus we need the (correct) relocation information.

  • Ah, sorry - the --emit-relocs option makes no guarantee of emitting processed relocations.  Rather it just copies the relocations from the input files into the output file.  It is up to you to process those relocations as best you can.  You may find that producing a map file might help with this.

  • I don't agree. On the one hand this option is explained in the man page and on the other we are using the emitted relocs already, but one value is wrong (rodata - 2). Maybe the update of the relocs was forgotten in the magic ctors linker code.

    --emit-relocs
    Leave relocation sections and contents in fully linked executables. Post link analysis and optimization tools may need this information in order to perform correct modifications of executables. This results in larger executables.

    The map file is not helpful for relocation. It lists only how the sections are merged together.
  • Then you definitely need to read the recent threads we've had regarding relocations in executable files, including:

    e2e.ti.com/.../499782
    e2e.ti.com/.../499764

    I should tell you right away that most of the issues you're going to see are addressed by dynamic shared objects (DSO), which is not presently available from the TI compiler.