ld script expression - wrong result when mixing constants & symbols

Asked by TM

Hi there

Hopefully I'm not doing something dumb here and overlooking it but I get odd/wrong results when I mix constants and symbols in my linker script and would be grateful for any help/advice that anybody can provide...

When I used this:

RAM_START_ADDRESS = 0x20000000; /* Must be the same value MEMORY region ram ORIGIN above. */
RAM_SIZE = 64k; /* Must be the same value MEMORY region ram LENGTH above. */
MAIN_STACK_SIZE = 8k; /* Cortex main stack size. */
PROCESS_STACK_SIZE = 4k; /* Cortex process stack size (only available with OS extensions).*/

...

  .bss :
  {
    __bss_start__ = . ;
    _sbss = .;
    *(.shbss)
    *(.bss .bss.* .gnu.linkonce.b.*)
    *(COMMON)
    . = ALIGN(0x10);
    __bss_end__ = .;
    _end = .;
    __end = _end;
    _ebss = .;
    PROVIDE(end = .);
  } >ram

  .heap :
  {
    __heap_start__ = .;
    . += (RAM_START_ADDRESS + RAM_SIZE - PROCESS_STACK_SIZE - MAIN_STACK_SIZE - _end); /* PROBLEM HERE! */
    . = ALIGN(0x10);
    _eheap = .;
  } >ram

I get this in the map file:

                0x200017b0 _end = .
                0x200017b0 __end = _end
                0x200017b0 _ebss = .
                0x200017b0 PROVIDE (end, .)

.heap 0x200017b0 0x6000fda0
                0x200017b0 __heap_start__ = .
                0x6000fda0 . = (. + ((((RAM_START_ADDRESS + RAM_SIZE) - PROCESS_STACK_SIZE) - MAIN_STACK_SIZE) - _end))
 *fill* 0x200017b0 0x6000fda0
                0x80011550 . = ALIGN (0x10)
                0x80011550 _eheap = .

And the program fails to link:

'Building target: test.elf'
'Invoking: Cross ARM C Linker'
arm-none-eabi-gcc -mcpu=cortex-m3 -mthumb -O0 -fmessage-length=0 -fsigned-char -ffunction-sections -fdata-sections -g3 -T ../CMSIS/startup_gcc/debug-in-microsemi-smartfusion2-esram.ld -Xlinker --gc-sections -Wl,-Map,"test.map" -o "test.elf" ./hal/CortexM3/GNU/hal.o ./hal/CortexM3/GNU/hw_reg_access.o ./hal/CortexM3/cortex_nvic.o ./drivers_config/sys_config/sys_config.o ./drivers/mss_uart/mss_uart.o ./drivers/mss_sys_services/mss_comblk.o ./drivers/mss_sys_services/mss_sys_services.o ./drivers/mss_nvm/mss_nvm.o ./drivers/mss_hpdma/mss_hpdma.o ./drivers/mss_gpio/mss_gpio.o ./CMSIS/startup_gcc/newlib_stubs.o ./CMSIS/startup_gcc/startup_m2sxxx.o ./CMSIS/system_m2sxxx.o ./main.o
c:/users/murphyt/downloads/softconsole_v4/new/arm-none-eabi-gcc/bin/../lib/gcc/arm-none-eabi/4.8.4/../../../../arm-none-eabi/bin/ld.exe: test.elf section `.heap' will not fit in region `ram'
c:/users/murphyt/downloads/softconsole_v4/new/arm-none-eabi-gcc/bin/../lib/gcc/arm-none-eabi/4.8.4/../../../../arm-none-eabi/bin/ld.exe: region `ram' overflowed by 1610630480 bytes
collect2.exe: error: ld returned 1 exit status
make: *** [test.elf] Error 1

The increment of the current location at the "PROBLEM HERE" line seems to be wrong. Because when I use this (i.e. literal integer value of _end instead of the symbol):

  .heap :
  {
    __heap_start__ = .;
    /* . += (RAM_START_ADDRESS + RAM_SIZE - PROCESS_STACK_SIZE - MAIN_STACK_SIZE - _end); */
    . += (RAM_START_ADDRESS + RAM_SIZE - PROCESS_STACK_SIZE - MAIN_STACK_SIZE - 0x200017b0);
    . = ALIGN(0x10);
    _eheap = .;
  } >ram

I get this and the program links fine:

.heap 0x200017b0 0xb850
                0x200017b0 __heap_start__ = .
                0x2000d000 . = (. + ((((RAM_START_ADDRESS + RAM_SIZE) - PROCESS_STACK_SIZE) - MAIN_STACK_SIZE) - 0x200017b0))
 *fill* 0x200017b0 0xb850
                0x2000d000 . = ALIGN (0x10)
                0x2000d000 _eheap = .

Any ideas?

If any further info is required let me know.
Thanks a lot for your attention.

Regards
TM

Question information

Language:
English Edit question
Status:
Solved
For:
GNU Arm Embedded Toolchain Edit question
Assignee:
No assignee Edit question
Solved by:
Thomas Preud'homme
Solved:
Last query:
Last reply:
Revision history for this message
Joey Ye (jinyun-ye) said :
#1

Adding RAM_START_ADDRESS to . is highly suspicious to me. Current point is already within ram scope, adding it again to ram start address does not make sense.

Revision history for this message
TM (tm1234) said :
#2

Hi Joey - thanks a lot for your reply.

I can appreciate your concern about the calculation but ultimately the result of this calculation:

 . += (RAM_START_ADDRESS + RAM_SIZE - PROCESS_STACK_SIZE - MAIN_STACK_SIZE - _end);

should be (expanding and parenthesizing as it appears in the map file):

. = . + ((((0x20000000 + 0x10000) - 0x1000) - 0x2000) - 0x200017b0)

or

. = . + 0xb850

But it's not when _end is used but IS when the literal constant value of _end (0x200017b0) is used.

I can't see any obvious risk of underflow or type incompatibility issues here.

Oh - and I should have added that there is no problem when I use a different/older toolchain - Mentor Sourcery CodeBench Lite 2010q1-188 which uses ld 2.19.51.20090709.

Any further ideas?

Thanks again.

Revision history for this message
TM (tm1234) said :
#3

BTW - I also tried rearranging the expression - e.g.:

 . += (_end - RAM_START_ADDRESS + RAM_SIZE - PROCESS_STACK_SIZE - MAIN_STACK_SIZE);

and parenthesizing it to make the precedence explicit but still no difference.

As long as I use the _end symbol (or any of the synonyms) the value calculated seems completely wrong and, so far, inexplicable.

:-|

Revision history for this message
Terry Guo (terry.guo) said :
#4

IMHO you are calculating two different objects (_end - RAM_START_ADDRESS) from the view of linker script. The _end is a variable defined in C source code while the RAM_START_ADDRESS is linker script own variable. I don't think current linker is smart enough to distinguish them. Perhaps this could be a new feature to linker.

Revision history for this message
TM (tm1234) said :
#5

Thanks Terry but I don't think that you are correct:

(1) _end is not a C variable - it is a ld script defined symbol same as the others (although the others are global symbols)

(2) This worked fine in the earlier version of ld (CodeSourcery) that I mentioned above

Curiously I found that this works properly but I still don't understand why it works or why the change is necessary:

 . += (ABSOLUTE(RAM_START_ADDRESS) + RAM_SIZE - PROCESS_STACK_SIZE - MAIN_STACK_SIZE - _end);

Ideally I'd still like to understand this and why the change since the earlier version of ld...

Revision history for this message
TM (tm1234) said :
#6

This also works:

  .heap :
  {
    __heap_start__ = .;
    . = ORIGIN(ram) + RAM_SIZE - PROCESS_STACK_SIZE - MAIN_STACK_SIZE;
    . = ALIGN(0x10);
    _eheap = .;
  } >ram

Revision history for this message
Thomas Preud'homme (thomas-preudhomme) said :
#7

Hi TM,

I'm not very familiar with linker script but from what I understood of the documentation your _end symbol is defined relative to the start of the .bss section (it evaluates as an offset from the beginning of the .bss section) but you use it as if it were the absolute address where it'll be loaded in memory. I'm a bit surprised that taking the absolute value of RAM_START_ADDRESS works but maybe, despite what the syntax suggest, it takes the absolute of everything on the line. Can you try to take the ABSOLUTE address of _end instead of RAM_START_ADDRESS? Does it still work?

Revision history for this message
TM (tm1234) said :
#8

Hi Thomas

Thanks for your reply.

> your _end symbol is defined relative to the start of the .bss section
> (it evaluates as an offset from the beginning of the .bss section)

No - it's absolute already as shown in the map file:

                0x200017b0 _end = .

As are all of the other symbols used in the original calculation:

                0x20000000 RAM_START_ADDRESS = 0x20000000
                0x00010000 RAM_SIZE = 0x10000
                0x00002000 MAIN_STACK_SIZE = 0x2000
                0x00001000 PROCESS_STACK_SIZE = 0x1000

In spite of this the original calculation (which works in an older version of ld) gives the wrong result even though plugging in the values above gives the correct one.

.heap 0x200017b0 0x6000fda0
                0x200017b0 __heap_start__ = .
                0x6000fda0 . = (. + ((((RAM_START_ADDRESS + RAM_SIZE) - PROCESS_STACK_SIZE) - MAIN_STACK_SIZE) - _end))
 *fill* 0x200017b0 0x6000fda0
                0x80011550 . = ALIGN (0x10)
                0x80011550 _eheap = .

.stack 0x80011550 0x3000
                0x80011550 __stack_start__ = .

I should have said that I already tried ABSOLUTE(_end) but that gave the same wrong result.

.heap 0x200017b0 0x2000d000
                0x200017b0 __heap_start__ = .
                0x2000d000 . = (. + ((((RAM_START_ADDRESS + RAM_SIZE) - PROCESS_STACK_SIZE) - MAIN_STACK_SIZE) - ABSOLUTE (_end)))
 *fill* 0x200017b0 0x2000d000
                0x4000e7b0 . = ALIGN (0x10)
                0x4000e7b0 _eheap = .

Only ABSOLUTE(RAM_START_ADDRESS) or ORIGIN(ram) cause the calculation to return the correct result.

.heap 0x200017b0 0xb850
                0x200017b0 __heap_start__ = .
                0x2000d000 . = (. + ((((ABSOLUTE (RAM_START_ADDRESS) + RAM_SIZE) - PROCESS_STACK_SIZE) - MAIN_STACK_SIZE) - _end))
 *fill* 0x200017b0 0xb850
                0x2000d000 . = ALIGN (0x10)
                0x2000d000 _eheap = .

.heap 0x200017b0 0xb850
                0x200017b0 __heap_start__ = .
                0x2000d000 . = (((ORIGIN (ram) + RAM_SIZE) - PROCESS_STACK_SIZE) - MAIN_STACK_SIZE)
 *fill* 0x200017b0 0xb850
                0x2000d000 . = ALIGN (0x10)
                0x2000d000 _eheap = .

.stack 0x2000d000

Seems to me that this is a change in ld from how it used to work (since the original expression returns the correct result in an older ld but not in this version) or it's a bug in ld (or this specific build of ld). But I cannot find any evidence to support either hypothesis to date.

I will most likely go with one of the expressions that work (assuming that it also works with the older tools as I need backward compatibility) but I would like to understand this better and why it works different (and incorrectly?) in the latest ld but not an older version.

Thanks.

Revision history for this message
Thomas Preud'homme (thomas-preudhomme) said :
#9

Sorry, I'm still lost, especially at the initial error. The output map when using ABSOLUTE(_end) makes a bit more sense but the original error don't make any. I tried to read more of the ld documentation, especially about its lazy evaluation and how it evaluate an expression depending on whether it's in an output section or not but still got nowhere.

My feeling is that there might be an information missing. Is it possible for you to show us the whole linker script?

Best regards.

Revision history for this message
TM (tm1234) said :
#10

Hi Thomas - thanks for looking into this. Much appreciated.
Here is the ld script in full.
Just to reiterate this works fine with the earlier version of ld (from CodeSourcery) mentioned earlier.
But it fails with the current GCC ARM Embedded ld.

/*******************************************************************************
 * (c) Copyright 2012-2014 Microsemi SoC Products Group. All rights reserved.
 *
 * SmartFusion2 Cortex-M3 linker script for creating a SoftConsole downloadable
 * debug image executing in SmartFusion2 internal eSRAM.
 *
 * SVN $Revision: 6693 $
 * SVN $Date: 2014-07-08 12:22:53 +0100 (Tue, 08 Jul 2014) $
 */

OUTPUT_FORMAT("elf32-littlearm", "elf32-bigarm", "elf32-littlearm")
GROUP(-lc -lgcc -lm)
OUTPUT_ARCH(arm)
ENTRY(Reset_Handler)
SEARCH_DIR(.)
__DYNAMIC = 0;

/*******************************************************************************
 * Start of board customization.
 *******************************************************************************/
MEMORY
{
  /* SmartFusion2 internal eSRAM */
  ram (rwx) : ORIGIN = 0x20000000, LENGTH = 64k
}

RAM_START_ADDRESS = 0x20000000; /* Must be the same value MEMORY region ram ORIGIN above. */
RAM_SIZE = 64k; /* Must be the same value MEMORY region ram LENGTH above. */
MAIN_STACK_SIZE = 8k; /* Cortex main stack size. */
PROCESS_STACK_SIZE = 4k; /* Cortex process stack size (only available with OS extensions).*/

/* Please note that unassigned RAM will be allocated to the .heap section. */

/*******************************************************************************
 * End of board customization.
 *******************************************************************************/

PROVIDE (__main_stack_start = RAM_START_ADDRESS + RAM_SIZE);
PROVIDE (__process_stack_start = __main_stack_start - MAIN_STACK_SIZE);
PROVIDE (_estack = __main_stack_start);
PROVIDE (__mirrored_nvm = 0); /* Indicate to startup code that NVM is not mirrored to VMA address .text copy is required. */

/*
 * Remap instruction for startup code and debugger:
 * 0: remap eNVM to address 0x00000000
 * 1: remap eSRAM to address 0x00000000
 * 2: remap external DDR memory to address 0x00000000
 */
PROVIDE (__smartfusion2_memory_remap = 1);

SECTIONS
{
  .vector_table :
  {
    __vector_table_load = LOADADDR(.vector_table);
    __vector_table_start = .;
    __vector_table_vma_base_address = .;
    KEEP(*(.isr_vector))
    . = ALIGN(0x10);
    _evector_table = .;
  } >ram

  .init :
  {
    /* SystemInit() is called before relocation to RAM so keep in ROM */
    *(.boot_code)
    *system_m2sxxx.o(.text*)
    *sys_config.o(.rodata*)
    . = ALIGN(0x10);
  } >ram

  .text :
  {
    CREATE_OBJECT_SYMBOLS
    __text_load = LOADADDR(.text);
    __text_start = .;
    *(.text .text.* .gnu.linkonce.t.*)
    *(.plt)
    *(.gnu.warning)
    *(.glue_7t) *(.glue_7) *(.vfp11_veneer)

    . = ALIGN(0x4);
    /* These are for running static constructors and destructors under ELF. */
    KEEP (*crtbegin.o(.ctors))
    KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors))
    KEEP (*(SORT(.ctors.*)))
    KEEP (*crtend.o(.ctors))
    KEEP (*crtbegin.o(.dtors))
    KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors))
    KEEP (*(SORT(.dtors.*)))
    KEEP (*crtend.o(.dtors))

    *(.rodata .rodata.* .gnu.linkonce.r.*)

    *(.ARM.extab* .gnu.linkonce.armextab.*)
    *(.gcc_except_table)
    *(.eh_frame_hdr)
    *(.eh_frame)

    KEEP (*(.vector_table))
    KEEP (*(.init))
    KEEP (*(.fini))

    PROVIDE_HIDDEN (__preinit_array_start = .);
    KEEP (*(.preinit_array))
    PROVIDE_HIDDEN (__preinit_array_end = .);
    PROVIDE_HIDDEN (__init_array_start = .);
    KEEP (*(SORT(.init_array.*)))
    KEEP (*(.init_array))
    PROVIDE_HIDDEN (__init_array_end = .);
    PROVIDE_HIDDEN (__fini_array_start = .);
    KEEP (*(.fini_array))
    KEEP (*(SORT(.fini_array.*)))
    PROVIDE_HIDDEN (__fini_array_end = .);
    . = ALIGN(0x10);
  } >ram
  /* .ARM.exidx is sorted, so has to go in its own output section. */
   __exidx_start = .;
  .ARM.exidx :
  {
    *(.ARM.exidx* .gnu.linkonce.armexidx.*)
  } >ram
  __exidx_end = .;
  _etext = .;
  PROVIDE(__text_end = .);

  .data :
  {
    __data_load = LOADADDR (.data);
    _sidata = LOADADDR (.data);
    __data_start = .;
    _sdata = .;
    KEEP(*(.jcr))
    *(.got.plt) *(.got)
    *(.shdata)
    *(.data .data.* .gnu.linkonce.d.*)
    . = ALIGN(0x10);
    _edata = .;
  } >ram

  .bss :
  {
    __bss_start__ = . ;
    _sbss = .;
    *(.shbss)
    *(.bss .bss.* .gnu.linkonce.b.*)
    *(COMMON)
    . = ALIGN(0x10);
    __bss_end__ = .;
    _end = .;
    __end = _end;
    _ebss = .;
    PROVIDE(end = .);
  } >ram

  .heap :
  {
    __heap_start__ = .;
    /* . = ORIGIN(ram) + RAM_SIZE - PROCESS_STACK_SIZE - MAIN_STACK_SIZE; */
    /* . += (ABSOLUTE(RAM_START_ADDRESS) + RAM_SIZE - PROCESS_STACK_SIZE - MAIN_STACK_SIZE - _end); */
    . += (RAM_START_ADDRESS + RAM_SIZE - PROCESS_STACK_SIZE - MAIN_STACK_SIZE - _end);
    . = ALIGN(0x10);
    _eheap = .;
  } >ram

  .stack :
  {
    __stack_start__ = .;
    . += PROCESS_STACK_SIZE;
    . = ALIGN(4);
    . += MAIN_STACK_SIZE;
    . = ALIGN(0x10);
    _estack = .;
  } >ram

  .stab 0 (NOLOAD) :
  {
    *(.stab)
  }

  .stabstr 0 (NOLOAD) :
  {
    *(.stabstr)
  }
  /* DWARF debug sections.
     Symbols in the DWARF debugging sections are relative to the beginning
     of the section so we begin them at 0. */
  /* DWARF 1 */
  .debug 0 : { *(.debug) }
  .line 0 : { *(.line) }
  /* GNU DWARF 1 extensions */
  .debug_srcinfo 0 : { *(.debug_srcinfo) }
  .debug_sfnames 0 : { *(.debug_sfnames) }
  /* DWARF 1.1 and DWARF 2 */
  .debug_aranges 0 : { *(.debug_aranges) }
  .debug_pubnames 0 : { *(.debug_pubnames) }
  /* DWARF 2 */
  .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) }
  .debug_abbrev 0 : { *(.debug_abbrev) }
  .debug_line 0 : { *(.debug_line) }
  .debug_frame 0 : { *(.debug_frame) }
  .debug_str 0 : { *(.debug_str) }
  .debug_loc 0 : { *(.debug_loc) }
  .debug_macinfo 0 : { *(.debug_macinfo) }
  /* SGI/MIPS DWARF 2 extensions */
  .debug_weaknames 0 : { *(.debug_weaknames) }
  .debug_funcnames 0 : { *(.debug_funcnames) }
  .debug_typenames 0 : { *(.debug_typenames) }
  .debug_varnames 0 : { *(.debug_varnames) }
  .note.gnu.arm.ident 0 : { KEEP (*(.note.gnu.arm.ident)) }
  .ARM.attributes 0 : { KEEP (*(.ARM.attributes)) }
  /DISCARD/ : { *(.note.GNU-stack) *(.isr_vector) }
}

Revision history for this message
Best Thomas Preud'homme (thomas-preudhomme) said :
#11

TL;DR: the behavior is normal and consistent with the documentation

I'm not 100% sure but I think I found the explanation for all results. It evolves around the rules to evaluate terms of an expression (I'll call this the "promotion" rules), the ones to decide of what type is the result of an expression (I'll call this the "evaluation" rules_ and the important fact that _end is relative to a different section (.bss) that the one you are defining (.heap). All the rules are defined in section 3.10.8 ("The section of an Expression") of the ld documentation provided with the toolchain.

Before looking at each expression we must understand the type of each operand in the expressions:

1) RAM_START_ADDRESS, RAM_SIZE, PROCESS_STACK_SIZE and MAIN_STACK_SIZE were defined *outside* any output section by numbers. As such, the numbers evaluates as absolute addresses making these symbols absolute symbols. Then they are referenced *inside* an output section definition so will be treated as numbers.

2) _end is a symbol defined inside an output section and as such is a relative symbol (I cannot find a phrase that say it explicitely but the example in section 3.10.8 and the following text makes it quite clear

3) . is relative to the beginning of the section. I'm not sure if it's because of the same rule but a sentence in section 3.10.5 ("The Location Counter") reads: "Note: . actually refers to the byte offset from the start of the current containing object".

Now let's look at the expressions one by one.

First approach:
(. + ((((RAM_START_ADDRESS + RAM_SIZE) - PROCESS_STACK_SIZE) - MAIN_STACK_SIZE) - _end))

each subexpression involving only numbers, these are left unchanged by the promotion rules (none of the rule match as we have a binary operation with only numbers) and the evaluation yields a number (first rule of evaluation rules). The result being a number, the same will happen when considering the result of (RAM_START_ADDRESS + RAM_SIZE) and PROCESS_STACK_SIZE. Eventually we'll get 2000d000 (as a number) - _end to evaluate and that's when it gets wrong.

_end will evaluate as an offset from the beginning of its section there because number and relative fits the condition of the first promotion rule. However the fourth evaluation rule tells us the result is relative to the section of the relative term. So we'll get 0x2000b000 - small value relative to the beginning of .bss. So we now have a result close to 0x20000000 relative to .bss (so an absolute value around 0x40000000). Later, we need to consider the addition of . and this value. This value is relative to .bss but . is relative to .heap so the second promotion rule apply and both values must be converted to an absolute value (as if ABSOLUTE was called). So this gives 0x200017b0 + something close to 0x40000000 (bss has an address close to 0x20000000 and we are at an offset around 0x20000000 from it). Thus we get an offset of 0x6000fda0 from the beginning of the section which brings us to 0x80011550.

Second approach:
(. + ((((RAM_START_ADDRESS + RAM_SIZE) - PROCESS_STACK_SIZE) - MAIN_STACK_SIZE) - ABSOLUTE (_end)))

The beginning is same and we get to examine 0x2000d000 as a number with ABSOLUTE (_end). ABSOLUTE(_end) will evaluate as an absolute address of value 0x200017b0. Second promotion rule match but we don't have a non-absolute term (we have an absolute address and a number). Then, according to the fifth/last evaluation rule this evaluates as an absolute address. So we have the expected 0xb850 as an absolute address. Then, we examine this absolute address and the location counter. Relative symbol and absolute address fits the second promotion rule so . is converted to an absolute address: 0x200017b0. Adding the two we get the 0x4000e7b0.

Third approach:
(. + ((((ABSOLUTE (RAM_START_ADDRESS) + RAM_SIZE) - PROCESS_STACK_SIZE) - MAIN_STACK_SIZE) - _end))

The ABSOLUTE (RAM_START_ADDRESS) will transform the number into an absolute address. Adding this to a number will trigger the second promotion rule without any change and then the fifth/last evaluation rule yielding an absolute address. We'll eventually get 0x2000b000 as an absolute address compared to _end which is relative. Per the second promotion rule, _end is converted to an absolute address. The result of the difference is a number according to the third evaluation rule (two absolute address yields a number). Adding a number to a relative symbol (the location counter) is governed by the first promotion rule so we only look at the offset of . and add it to 0xb850. The result is an address relative to the section of the location counter which is always the current section. So we get the expected result of 0x0 (offset of . from start of .heap) + 0xb850 = 0xb850 relative to the start of .heap (0x200017b0) which means we are at address 0x2000b000.

Fourth/last approach:
(((ORIGIN (ram) + RAM_SIZE) - PROCESS_STACK_SIZE) - MAIN_STACK_SIZE)

ORIGIN is a function that returns an absolute address. So as for above, we'll have absolute and number all the way which will give a result of type absolute address. No _end is involved here so no strange result. We are thus setting the location counter at absolute address (((0x20000000 + 0x10000) - 0x1000) - 0x2000) = 0x2000b000.

I hope my explanation were clear. Feel free to ask if something is not clear.

Revision history for this message
TM (tm1234) said :
#12

Hi Thomas. Wow! Thanks a lot for digging so deeply into this. Much appreciated.
Your analysis certainly seems to add up alright.
So I guess the fact that the calculation returned the result that I expected with the older version of ld was probably down to a bug in that version?

Thanks again.

Revision history for this message
Thomas Preud'homme (thomas-preudhomme) said :
#13

It might also be due to a change of behavior or the toolchain being built with different defaults. I don't know the details of Mentor Sourcery CodeBench toolchain so I can only guess.

Revision history for this message
TM (tm1234) said :
#14

Thanks Thomas Preud'homme, that solved my question.