"multiple definition of" error when using std::function with argument (teensy41, platformio)

ctadlock

Active member
Teensy 4.1 1.58
Clion 2023.3.4
PlatformIO 6.1.13
toolchain-gccarmnoneeabi-teensy 11.3.1
Windows 11
platformio.ini (nothing interesting)

Im trying to create a function callback in a class. If the callback takes an argument it fails.

This works:
Code:
std::function<void()> onChanged_;

This does not work:
Code:
std::function<void(int)> onChanged_;

I get the following errors in linking:
Code:
Linking .pio\build\teensy41_debug\firmware.elf
c:/users/xxx/.platformio/packages/toolchain-gccarmnoneeabi-teensy/bin/../lib/gcc/arm-none-eabi/11.3.1/../../../../arm-none-eabi/bin/ld.exe: .pio\build\teensy41_debug\libFrameworkArduino.a(new.cpp.o): in function `operator delete(void*)':
new.cpp:(.text._ZdlPv+0x0): multiple definition of `operator delete(void*)'; c:/users/xxx/.platformio/packages/toolchain-gccarmnoneeabi-teensy/bin/../lib/gcc/arm-none-eabi/11.3.1/thumb/v7e-m+dp/hard\libstdc++.a(del_op.o):(.text._ZdlPv+0x0): first defined here
c:/users/xxx/.platformio/packages/toolchain-gccarmnoneeabi-teensy/bin/../lib/gcc/arm-none-eabi/11.3.1/../../../../arm-none-eabi/bin/ld.exe: .pio\build\teensy41_debug\libFrameworkArduino.a(new.cpp.o): in function `operator delete(void*, unsigned int)':
new.cpp:(.text._ZdlPvj+0x0): multiple definition of `operator delete(void*, unsigned int)'; c:/users/xxx/.platformio/packages/toolchain-gccarmnoneeabi-teensy/bin/../lib/gcc/arm-none-eabi/11.3.1/thumb/v7e-m+dp/hard\libstdc++.a(del_ops.o):(.text._ZdlPvj+0x0): first defined here
collect2.exe: error: ld returned 1 exit status
*** [.pio\build\teensy41_debug\firmware.elf] Error 1

Thoughts?

Atttached are success and failure builds.
 

Attachments

  • success.txt
    81.8 KB · Views: 74
  • failure.txt
    82.5 KB · Views: 78
Last edited:
Tried it on Arduino IDE 1.8.19 with Teensyduino 1.59. Put that line inside a class, created an instance, and assigned it. Hopefully that's ok?

Compiles without any error.

1708224470715.png
 
Ive made some progress but still dont fully understand. What I think is going on is that when the stdlib is being included it somewhere includes the same functions that are in "\.platformio\packages\framework-arduinoteensy\cores\teensy4\new.cpp" like "operator delete(void*)". The linker sees both and throws the "multiple definition of..." error.

I built a minimal sample in platformio and it works OK. Then I took my code and removed all the other code besides the code that was using "std::function" and it then started working. I think this is somehow a order of including/loading/linking issue. If I manually comment out the "new.cpp" code then my full project works. I know thats not a good ieda but shows Im on the correct track.

This thread is basicly the same issue that I am having, except that Im not trying to overload those functions myself.. the stdlib is (I think).


Any guidance would be appreciated.
 
If I manually add the weak attribute to the delete functions in new.cpp it works. Again, not a good idea.

Code:
__attribute__((weak)) void operator delete(void * ptr)
{
    free(ptr);
}

__attribute__((weak)) void operator delete[](void * ptr)
{
    free(ptr);
}

__attribute__((weak)) void operator delete(void * ptr, size_t size __attribute__((unused)))
{
    free(ptr);
}

__attribute__((weak)) void operator delete[](void * ptr, size_t size __attribute__((unused)))
{
    free(ptr);
}

The other functions are defined here "\.platformio\packages\toolchain-gccarmnoneeabi-teensy\arm-none-eabi\include\c++\11.3.1\new". Note the comment...

Code:
//@{
/** These are replaceable signatures:
 *  - normal single new and delete (no arguments, throw @c bad_alloc on error)
 *  - normal array new and delete (same)
 *  - @c nothrow single new and delete (take a @c nothrow argument, return
 *    @c NULL on error)
 *  - @c nothrow array new and delete (same)
 *
 *  Placement new and delete signatures (take a memory address argument,
 *  does nothing) may not be replaced by a user's program.
*/
_GLIBCXX_NODISCARD void* operator new(std::size_t) _GLIBCXX_THROW (std::bad_alloc)
  __attribute__((__externally_visible__));
_GLIBCXX_NODISCARD void* operator new[](std::size_t) _GLIBCXX_THROW (std::bad_alloc)
  __attribute__((__externally_visible__));
void operator delete(void*) _GLIBCXX_USE_NOEXCEPT
  __attribute__((__externally_visible__));
void operator delete[](void*) _GLIBCXX_USE_NOEXCEPT
  __attribute__((__externally_visible__));
#if __cpp_sized_deallocation
void operator delete(void*, std::size_t) _GLIBCXX_USE_NOEXCEPT
  __attribute__((__externally_visible__));
void operator delete[](void*, std::size_t) _GLIBCXX_USE_NOEXCEPT
  __attribute__((__externally_visible__));
#endif
_GLIBCXX_NODISCARD void* operator new(std::size_t, const std::nothrow_t&) _GLIBCXX_USE_NOEXCEPT
  __attribute__((__externally_visible__, __alloc_size__ (1), __malloc__));
_GLIBCXX_NODISCARD void* operator new[](std::size_t, const std::nothrow_t&) _GLIBCXX_USE_NOEXCEPT
  __attribute__((__externally_visible__, __alloc_size__ (1), __malloc__));
void operator delete(void*, const std::nothrow_t&) _GLIBCXX_USE_NOEXCEPT
  __attribute__((__externally_visible__));
void operator delete[](void*, const std::nothrow_t&) _GLIBCXX_USE_NOEXCEPT
  __attribute__((__externally_visible__));
 
Last edited:
See related to the SPI library. It seems to be using "new" and "delete". If I remove my code that uses the SPI1 then it works.
 
A sample program which demonstrates the problem when copied into Arduino IDE with the latest Teensyduino is required.

Today 1.59 is the latest. When we start publishing 1.60 betas, "latest" means the latest beta version.
 
It's not clear that this problem is reproducible in the arduino ide as it doesn't support multiple files. I believe this to be a linking order issue. Without multiple source files you can't have ordering.

The issue is easy to see. The original arduino new.cpp had a limitation that it never expected to be used with the stdlib. Then arduino grew up and they fixed it. The copied file in teensy is still of the old version and has not yet fixed it.
 
Just to be perfectly clear, I will not consider any change without a test case in Arduino IDE.

I don't care what code is in any other core library, even Arduino's core library for AVR.

If this really is a problem, demonstrate it with code in Arduino IDE. If the problem only happens in PlatformIO but not Arduino IDE, then this problem will be considered an issue or misconfiguration with PlatformIO.

I should also add, over and over we've seen PlatformIO problems which are due to using different compiler command line or other libraries than are meant to be used with Teensy.
 
The other functions are defined here "\.platformio\packages\toolchain-gccarmnoneeabi-teensy\arm-none-eabi\include\c++\11.3.1\new". Note the comment...
The comment says they are replaceable, which is true if the replacement functions are linked first. The logs you've posted don't show the link command line but it seems libstdc++ is being specified before the Teensy's core library instead of the other way around.
 
The comment says they are replaceable, which is true if the replacement functions are linked first. The logs you've posted don't show the link command line but it seems libstdc++ is being specified before the Teensy's core library instead of the other way around.
Exactly.

The commit where my proposed change is in the Arduino code explains the issue.

 
Exactly.

The commit where my proposed change is in the Arduino code explains the issue.
That's not the correct fix for this situation.
They are replaceable in the standard library. Teensy's core is attempting to replace them. You just have the libraries in the wrong order in your linker command line.
 
I recently stumbled across this linking error and also have no idea what the real issue is!

In Arduino IDE all sibling derived classes linked fine including the troublesome one, but only that one derived class (a *very* thin adapter) would fail to link on PlatformIO.

For anyone else ending up here, the workaround was to move one of the class members out of the stack and in to the heap.

Whilst I understand your position Paul, PlatformIO and Teensy are a good partnership for large/complex projects - which can be quite challenging to develop in the Arduino IDE.
 
Best thing we ever did was remove PlatformIO; all it did was act as an annoying middle man. Given we use CLion we made our own cmake for Teensy and it has been great. We use the Arduino IDE and Teensyduino tools from cmake so we get all the updates and tooling.
 
Back
Top