esp-idf linker script question

paddlesteamer
Posts: 5
Joined: Thu Apr 01, 2021 2:52 pm

esp-idf linker script question

Postby paddlesteamer » Sat May 15, 2021 6:12 pm

Hello,

I'm trying to keep an unreferenced function in the memory and later call it from another file. My files are like this:

main/linker.lf:

Code: Select all

[sections:fnreg]
entries:
    fnreg

[scheme:fnreg]
entries:
    fnreg -> dram0_data

[mapping:map]
archive: libmain.a
entries:
    fn1 (fnreg);
        fnreg -> dram0_data KEEP() SURROUND(fnreg)

main/main.c:

Code: Select all


#include "esp_log.h"

extern void (*_fnreg_start)();
extern void (*_fnreg_end)();

void call_fns() {
    void (**fn)() = &_fnreg_start;

    ESP_LOGI("CALL_FNS", "%x - %x", (uint32_t) &_fnreg_start, (uint32_t) &_fnreg_end);

    for (; fn < &_fnreg_end; fn++) {
        (*fn)();
    }
}

void app_main(void) {
    call_fns();
}
main/fn1.c:

Code: Select all

#include "esp_log.h"

static void fn1() {
    ESP_LOGI("FN1", "fn1 is called");
}

static void (*reg_func)()
    __attribute__((__section__("fnreg")))
    __attribute__((__used__)) = fn1;
main/CMakeLists.txt:

Code: Select all

idf_component_register(SRCS "registry.c" "fn1.c"
                    INCLUDE_DIRS "."
                    LDFRAGMENTS "linker.lf")
When I compile my code with `idf.py build`, the `fn1` function or section `fnreg` is removed in the main.elf file. Is it possible to force the linker to keep fn1 without referencing it directly in the code?

Note that I'm using the latest esp-idf version at github.

ESP_Angus
Posts: 2344
Joined: Sun May 08, 2016 4:11 am

Re: esp-idf linker script question

Postby ESP_Angus » Tue May 18, 2021 12:08 am

paddlesteamer wrote:
Sat May 15, 2021 6:12 pm
Hello,

I'm trying to keep an unreferenced function in the memory and later call it from another file. My files are like this:

main/linker.lf:

Code: Select all

[sections:fnreg]
entries:
    fnreg

[scheme:fnreg]
entries:
    fnreg -> dram0_data

[mapping:map]
archive: libmain.a
entries:
    fn1 (fnreg);
        fnreg -> dram0_data KEEP() SURROUND(fnreg)

main/main.c:

Code: Select all


#include "esp_log.h"

extern void (*_fnreg_start)();
extern void (*_fnreg_end)();

void call_fns() {
    void (**fn)() = &_fnreg_start;

    ESP_LOGI("CALL_FNS", "%x - %x", (uint32_t) &_fnreg_start, (uint32_t) &_fnreg_end);

    for (; fn < &_fnreg_end; fn++) {
        (*fn)();
    }
}

void app_main(void) {
    call_fns();
}
main/fn1.c:

Code: Select all

#include "esp_log.h"

static void fn1() {
    ESP_LOGI("FN1", "fn1 is called");
}

static void (*reg_func)()
    __attribute__((__section__("fnreg")))
    __attribute__((__used__)) = fn1;
main/CMakeLists.txt:

Code: Select all

idf_component_register(SRCS "registry.c" "fn1.c"
                    INCLUDE_DIRS "."
                    LDFRAGMENTS "linker.lf")
When I compile my code with `idf.py build`, the `fn1` function or section `fnreg` is removed in the main.elf file. Is it possible to force the linker to keep fn1 without referencing it directly in the code?

Note that I'm using the latest esp-idf version at github.
Hi paddlesteamer,

I'm not sure I fully understand what you're doing here, but you need to give the linker a reason to look inside the fn1.c.obj file and add it to the link. KEEP() in the linker script will prevent it from removing the un-referenced "fn1" later on, but it won't get it added in the first place.

The way we usually do this in ESP-IDF is to add a "linker hook" dummy variable or function, like this:

In fn1.c:

Code: Select all

// Function to ensure the linker includes this file
void fn1_link_impl(void) { }
In CMakeLists.txt:

Code: Select all

target_link_libraries(${COMPONENT_LIB} INTERFACE "-u fn1_link_impl")
Note: If there is at least one globally visible (non-static) function or variable in fn1.c that you want linked into the binary then you don't need the dummy function, you can just put the name of the real function or variable here.

maplerian
Posts: 1
Joined: Sat Apr 02, 2022 3:25 am

Re: esp-idf linker script question

Postby maplerian » Sat Apr 02, 2022 3:29 am

auto_init.lf

Code: Select all

[sections:auto_init]
entries:
    .auto_init+

[scheme:auto_default]
entries:
    auto_init -> dram0_data

[mapping:auto_init]
archive: *
entries:
    * (auto_default);
    auto_init -> dram0_data KEEP() ALIGN(4, pre, post) SURROUND(auto_init)
You can try this link configuration

Who is online

Users browsing this forum: No registered users and 344 guests