Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Using multiple ULP program in a larger project fails silently at runtime (IDFGH-14143) #14945

Open
3 tasks done
X-Ryl669 opened this issue Nov 27, 2024 · 1 comment · May be fixed by #14954
Open
3 tasks done

Using multiple ULP program in a larger project fails silently at runtime (IDFGH-14143) #14945

X-Ryl669 opened this issue Nov 27, 2024 · 1 comment · May be fixed by #14954
Assignees
Labels
Status: Opened Issue is new

Comments

@X-Ryl669
Copy link
Contributor

Answers checklist.

  • I have read the documentation ESP-IDF Programming Guide and the issue is not addressed there.
  • I have updated my IDF branch (master or release) to the latest version and checked that the issue is present there.
  • I have searched the issue tracker for a similar issue and not found a similar issue.

General issue report

It's a bit similar to #14921 in the concept. I have multiple different program for the ULP's LP core on ESP32 C6 CPU. I've declared them like this in the main's CMakelists.txt:

set(ulp_app_name ulp_${COMPONENT_NAME})
set(ulp_sources "ulp/ulp_main.c")
set(ulp_exp_dep_srcs ${srcs})

ulp_embed_binary(${ulp_app_name} "${ulp_sources}" "${ulp_exp_dep_srcs}")

set(ulp_app_name2 ulp_ds_cal)
set(ulp_sources2 "ulp/ulp_ds_cal.c")

ulp_embed_binary(${ulp_app_name2} "${ulp_sources2}" "${ulp_exp_dep_srcs}")

The first issue happens if you have the same variable name in both file. In that case it's not possible, in C to include the automatically generated header in your main function, since both refer to the same variable, but variables are located at different address. So if I have, let's say a variable called uint32_t state in both file, the generated header ulp_main.h and ulp_ds_cal.h both have an extern uint32_t ulp_state;. Yet, the first ULP program will have this linker script: PROVIDE ( ulp_state = 0x50000ab8 ); and the second will have PROVIDE ( ulp_state = 0x50000640 );.
Yet, it'll link fine but obviously fails at runtime, since the variable will just refer to a random memory location when using the second program.

So, since it's clear that both variables can't be referred with a single name, I've used C++ like this to move each declaration in its own namespace:

namespace ULPMain 
{
  #include "ulp_main.h"
}
namespace ULPDSCal
{
  #include "ulp_ds_cal.h"
}

// Now, ULPDSCal::ulp_state should refer to ulp_ds_cal.c's version of state

This strangely links fine (I would have expected that the linker choked on an undefined reference on extern uint32_t ULPDSCal::ulp_state but it's perfectly happy with that), but obviously it doesn't work at runtime. If I print the address of the ULPDSCal::ulp_state variable in HP core, I get 0x50000ab8, which is the address of the first program, not the second one.

I think this is a bug in the linker script that's not using the expected mangled name for linking (it should have failed to link a symbol with a different name, the mangled name for the ULPDSCal::ulp_state variable is not ulp_state).

It can only be solved by either changing the symbol processing script to allow changing the prefix for the variable (so the first script could use ulp_ and the second would use, for example, ulp_2_) or, better, to allow using a C++ namespace instead (so instead of ulp_VARIABLE, simply have a NAMESPACE::VARIABLE declaration).

Another option would be to have a macro for variable declaration allowing to position the variable at a specific address. Notice that using uint32_t variable = *(volatile uint32_t*)0x50000210; is not possible since the linker can place whatever it wants at this address later on. On ARM, there's the attribute __attribute(at(address))__ that can be used for this exact feature.

There also the hacky:

extern const uint32_t state;
asm(".equ state, 0x50000210");

that seems to work but this become completely invisible to the linker's script and variable renaming so it's also very fragile since the linker doesn't not know that a variable exists at 0x50000210 and can put whatever it wants here, breaking the code.

@espressif-bot espressif-bot added the Status: Opened Issue is new label Nov 27, 2024
@github-actions github-actions bot changed the title Using multiple ULP program in a larger project fails silently at runtime Using multiple ULP program in a larger project fails silently at runtime (IDFGH-14143) Nov 27, 2024
@X-Ryl669
Copy link
Contributor Author

The fact that it links is because the generated header file contains an extern "C" directive, so effectively, removing the namespacing and thus, colliding the name. Please have a look at #14954 for a solution that works for both C (with different prefix) or C++ (with namespace).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment