fix(ldgen): don't emit intermediate placements without sections

When a symbol needs to be placed to a different target than the one
designated for the object file, the object file is expanded, which
includes the following steps:

1. Creating a new placement for the symbol's input section with the
   specified target.
2. Excluding the object placement from the orignal target.
3. Creating a new intermediate placement for the object for the original
   target, where its input sections are expanded, excluding the input
   section for the symbol.

Let's illustrate the object expansion process with the following example:

[sections:rodata]
entries:
    .rodata+
    .sdata2+
    .srodata+

[scheme:default]
entries:
    text -> flash_text
    rodata -> flash_rodata

[scheme:noflash]
entries:
    text -> iram0_text
    rodata -> dram0_data

[mapping:soc_pm]
archive: libsoc.a
entries:
    gpio_periph: GPIO_HOLD_MASK (noflash)

gpio_periph section headers:
  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            00000000 000000 000000 00      0   0  0
  [ 1] .text             PROGBITS        00000000 000034 000000 00  AX  0   0  2
  [ 2] .data             PROGBITS        00000000 000034 000000 00  WA  0   0  1
  [ 3] .bss              NOBITS          00000000 000034 000000 00  WA  0   0  1
  [ 4] .rodata.GPIO_HOLD_MASK PROGBITS        00000000 000034 000058 00   A  0   0  4
  [ 5] .rodata.GPIO_PIN_MUX_REG PROGBITS        00000000 00008c 000058 00   A  0   0  4
  [ 6] .debug_info       PROGBITS        00000000 0000e4 0000d8 00      0   0  1
  [ 7] .rela.debug_info  RELA            00000000 0009d4 000108 0c   I 16   6  4
  [ 8] .debug_abbrev     PROGBITS        00000000 0001bc 000070 00      0   0  1
  [ 9] .debug_aranges    PROGBITS        00000000 00022c 000018 00      0   0  1
  [10] .rela.debug_aranges RELA            00000000 000adc 00000c 0c   I 16   9  4
  [11] .debug_line       PROGBITS        00000000 000244 0001ab 00      0   0  1
  [12] .debug_str        PROGBITS        00000000 0003ef 00022d 01  MS  0   0  1
  [13] .comment          PROGBITS        00000000 00061c 000030 01  MS  0   0  1
  [14] .note.GNU-stack   PROGBITS        00000000 00064c 000000 00      0   0  1
  [15] .riscv.attributes RISCV_ATTRIBUTES 00000000 00064c 000044 00      0   0  1
  [16] .symtab           SYMTAB          00000000 000690 000260 10     17  36  4
  [17] .strtab           STRTAB          00000000 0008f0 0000e1 00      0   0  1
  [18] .shstrtab         STRTAB          00000000 000ae8 0000d1 00      0   0  1

1. Creating a new placement
.dram0.data :
{
    *libsoc.a:gpio_periph.*(.rodata.GPIO_HOLD_MASK .sdata2.GPIO_HOLD_MASK .srodata.GPIO_HOLD_MASK)
}

2. Excluding the object placement
.flash.rodata :
{
    *(EXCLUDE_FILE(*libsoc.a:gpio_periph.*) .rodata.* ...)
}

3. Creating a new intermediate placement
.flash.rodata :
{
    *libsoc.a:gpio_periph.*(.rodata.GPIO_PIN_MUX_REG)
}

Now, let's do the same, but also move GPIO_PIN_MUX_REG to noflash with an updated mapping.

[mapping:soc_pm]
archive: libsoc.a
entries:
    gpio_periph: GPIO_HOLD_MASK (noflash)
    gpio_periph: GPIO_PIN_MUX_REG (noflash)

1. Creating a new placement
.dram0.data :
{
    *libsoc.a:gpio_periph.*(.rodata.GPIO_HOLD_MASK .sdata2.GPIO_HOLD_MASK .srodata.GPIO_HOLD_MASK)
    *libsoc.a:gpio_periph.*(.rodata.GPIO_PIN_MUX_REG .sdata2.GPIO_PIN_MUX_REG
                            .srodata.GPIO_PIN_MUX_REG)
}

2. Excluding the object placement
.flash.rodata :
{
    *(EXCLUDE_FILE(*libsoc.a:gpio_periph.*) .rodata.* ...)
}

3. Creating a new intermediate placement
.flash.rodata :
{
    *libsoc.a:gpio_periph.*
}

The *libsoc.a:gpio_periph.* entity in step 3 no longer has input
sections, as there are no remaining .rodata input sections in the object
file. The linker behavior for this mapping is to include all object
input sections that have not yet been placed as described in
https://sourceware.org/binutils/docs/ld.html#Input-Section-Basics
"If you use a file name without a list of sections, then all sections in
the input file will be included in the output section. This is not
commonly done, but it may by useful on occasion."

The map file for such mapping now contains following input sections

 .flash.rodata   0x3c0a0120    0x19b34
     *libsoc.a:gpio_periph.*()
     .debug_info    0x3c0b95bf       0xd8 esp-idf/soc/libsoc.a(gpio_periph.c.obj)
     .debug_abbrev  0x3c0b9697       0x70 esp-idf/soc/libsoc.a(gpio_periph.c.obj)
     .debug_aranges
                    0x3c0b9707       0x18 esp-idf/soc/libsoc.a(gpio_periph.c.obj)
     .debug_line    0x3c0b971f      0x1ab esp-idf/soc/libsoc.a(gpio_periph.c.obj)
     .debug_str     0x3c0b98ca      0x21a esp-idf/soc/libsoc.a(gpio_periph.c.obj)
                                    0x22d (size before relaxing)
     .comment       0x3c0b9ae4       0x30 esp-idf/soc/libsoc.a(gpio_periph.c.obj)
     .note.GNU-stack
                    0x3c0b9ae4        0x0 esp-idf/soc/libsoc.a(gpio_periph.c.obj)
     .riscv.attributes
                    0x3c0b9ae4       0x44 esp-idf/soc/libsoc.a(gpio_periph.c.obj)

This is incorrect, and such intermediate placement should not be
generated. This type of placement can be recognized because it is not
explicitly defined in the mapping and lacks input sections. We can
identify this in the significant function and prevent issuing commands
for such placement.

Signed-off-by: Frantisek Hrbata <frantisek.hrbata@espressif.com>
This commit is contained in:
Frantisek Hrbata
2025-02-14 08:04:29 +01:00
parent 15da152e21
commit c06b3d213e
3 changed files with 635 additions and 8 deletions
+48 -2
View File
@@ -1,6 +1,6 @@
#!/usr/bin/env python
#
# SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
# SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Apache-2.0
#
import collections
@@ -32,6 +32,7 @@ ROOT = Entity('*')
FREERTOS = Entity('libfreertos.a')
CROUTINE = Entity('libfreertos.a', 'croutine')
TIMERS = Entity('libfreertos.a', 'timers')
TEMPERATURE_SENSOR_PERIPH = Entity('libsoc.a', 'temperature_sensor_periph')
FREERTOS2 = Entity('libfreertos2.a')
@@ -68,6 +69,9 @@ class GenerationTest(unittest.TestCase):
with open('data/libfreertos.a.txt') as objdump:
self.entities.add_sections_info(objdump)
with open('data/libsoc.a.txt') as objdump:
self.entities.add_sections_info(objdump)
with open('data/linker_script.ld') as linker_script:
self.linker_script_expect = LinkerScript(linker_script)
@@ -336,6 +340,48 @@ entries:
self.compare_rules(expected, actual)
def test_nondefault_mapping_all_symbols(self):
# Test mapping entry different from default for all .rodata.* symbols in the temperature_sensor_periph
# object file. There should be exclusion in the default commands for flash_rodata, but
# no implicit intermediate object command(X), because there are no .rodata+
# symbols left to be placed in dram0_data.
#
# The X line with entity only(without any input sections) should not be emitted, because
# linker would include all not yet placed input sections from the temperature_sensor_periph
# object file, including .debug, .comment and other input section.
#
# flash.rodata
# *((EXCLUDE_FILE(*libsoc.a:temperature_sensor_periph.*)) .rodata.* ...) A
# # *libsoc.a:temperature_sensor_periph.* X
#
# Commands placing the entire library in iram should be generated:
#
# dram0_data
# *libsoc.a:temperature_sensor_periph.*(.rodata.temperature_sensor_attribute) B
mapping = u"""
[mapping:test]
archive: libsoc.a
entries:
temperature_sensor_periph:temperature_sensor_attributes (noflash) # 1
"""
self.add_fragments(mapping)
actual = self.generation.generate(self.entities, False)
expected = self.generate_default_rules()
flash_rodata = expected['flash_rodata']
dram0_data = expected['dram0_data']
# Generate exclusion in flash_text A
flash_rodata[0].exclusions.add(TEMPERATURE_SENSOR_PERIPH)
# Input section commands in dram0_data for #1 B
dram0_data.append(InputSectionDesc(TEMPERATURE_SENSOR_PERIPH,
set(['.rodata.temperature_sensor_attributes']),
[]))
self.compare_rules(expected, actual)
def test_default_symbol_nondefault_lib(self):
# Test default symbol mapping with different lib mapping. This should create an implicit intermediate object command.
# The significant targets are flash_text, flash_rodata, iram0_text, dram0_data.
@@ -571,7 +617,7 @@ entries:
def test_multiple_symbols_excluded_from_intermediate_command(self):
# Test mapping multiple symbols from the same object.
# All these symbols must be succesfully excluded from
# All these symbols must be successfully excluded from
# the intermediate command.
#
# flash_text