Port, board and/or hardware
All ports that use Make (I think)
MicroPython version
4efc5e1
Reproduction
Modify examples/cppexample/example.cpp to use a std::vector (or anything else that actually requires libstdc++.a or any other libs):
#include <vector>
#include <numeric>
extern "C" {
#include <examplemodule.h>
#include <py/objstr.h>
// Here we implement the function using C++ code, but since it's
// declaration has to be compatible with C everything goes in extern "C" scope.
mp_obj_t cppfunc(mp_obj_t a_obj, mp_obj_t b_obj) {
// The following no-ops are just here to verify the static assertions used in
// the public API all compile with C++.
MP_STATIC_ASSERT_STR_ARRAY_COMPATIBLE;
if (mp_obj_is_type(a_obj, &mp_type_BaseException)) {
}
// Prove we have (at least) C++11 features.
const auto a = mp_obj_get_int(a_obj);
const auto b = mp_obj_get_int(b_obj);
std::vector<int> vec;
vec.push_back(a);
vec.push_back(b);
int std_accumulate = std::accumulate(vec.begin(), vec.end(), 0);
const auto sum = [&]() {
return mp_obj_new_int(std_accumulate);
} ();
// Prove we're being scanned for QSTRs.
mp_obj_t tup[] = {sum, MP_ROM_QSTR(MP_QSTR_hellocpp)};
return mp_obj_new_tuple(2, tup);
}
}
Try to build firmware for any port that uses Make (I've tested stm32 and mimxrt, but I assume others are the same):
cd ports/stm32
make USER_C_MODULES=../../examples/usercmodule
Expected behaviour
The firmware should build without errors.
Observed behaviour
The linker can't find any of the required symbols from libstdc++.a:
LINK build-PYBV10/firmware.elf
/usr/lib/gcc/arm-none-eabi/13.2.1/../../../arm-none-eabi/bin/ld: build-PYBV10/cppexample/example.o: in function `std::__new_allocator<int>::deallocate(int*, unsigned int)':
/usr/include/newlib/c++/13.2.1/bits/new_allocator.h:168:(.text._ZNSt12_Vector_baseIiSaIiEED2Ev[_ZNSt12_Vector_baseIiSaIiEED5Ev]+0x8): undefined reference to `operator delete(void*)'
/usr/lib/gcc/arm-none-eabi/13.2.1/../../../arm-none-eabi/bin/ld: build-PYBV10/cppexample/example.o: in function `std::vector<int, std::allocator<int> >::_M_check_len(unsigned int, char const*) const':
/usr/include/newlib/c++/13.2.1/bits/stl_vector.h:1896:(.text._ZNSt6vectorIiSaIiEE17_M_realloc_insertIJRKiEEEvN9__gnu_cxx17__normal_iteratorIPiS1_EEDpOT_[_ZNSt6vectorIiSaIiEE17_M_realloc_insertIJRKiEEEvN9__gnu_cxx17__normal_iteratorIPiS1_EEDpOT_]+0x22): undefined reference to `std::__throw_length_error(char const*)'
/usr/lib/gcc/arm-none-eabi/13.2.1/../../../arm-none-eabi/bin/ld: build-PYBV10/cppexample/example.o: in function `void std::vector<int, std::allocator<int> >::_M_realloc_insert<int const&>(__gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, int const&)':
/usr/include/newlib/c++/13.2.1/bits/new_allocator.h:147:(.text._ZNSt6vectorIiSaIiEE17_M_realloc_insertIJRKiEEEvN9__gnu_cxx17__normal_iteratorIPiS1_EEDpOT_[_ZNSt6vectorIiSaIiEE17_M_realloc_insertIJRKiEEEvN9__gnu_cxx17__normal_iteratorIPiS1_EEDpOT_]+0x40): undefined reference to `operator new(unsigned int)'
/usr/lib/gcc/arm-none-eabi/13.2.1/../../../arm-none-eabi/bin/ld: build-PYBV10/cppexample/example.o: in function `std::__new_allocator<int>::deallocate(int*, unsigned int)':
/usr/include/newlib/c++/13.2.1/bits/new_allocator.h:168:(.text._ZNSt6vectorIiSaIiEE17_M_realloc_insertIJRKiEEEvN9__gnu_cxx17__normal_iteratorIPiS1_EEDpOT_[_ZNSt6vectorIiSaIiEE17_M_realloc_insertIJRKiEEEvN9__gnu_cxx17__normal_iteratorIPiS1_EEDpOT_]+0x7c): undefined reference to `operator delete(void*)'
/usr/lib/gcc/arm-none-eabi/13.2.1/../../../arm-none-eabi/bin/ld: build-PYBV10/cppexample/example.o: in function `std::vector<int, std::allocator<int> >::~vector()':
/usr/include/newlib/c++/13.2.1/bits/stl_vector.h:735:(.text.cppfunc+0x6e): undefined reference to `__cxa_end_cleanup'
/usr/lib/gcc/arm-none-eabi/13.2.1/../../../arm-none-eabi/bin/ld: build-PYBV10/cppexample/example.o:(.ARM.extab.text.cppfunc+0x0): undefined reference to `__gxx_personality_v0'
collect2: error: ld returned 1 exit status
make: *** [Makefile:658: build-PYBV10/firmware.elf] Error 1
Additional Information
The root problem is in these 3 locations:
|
LDFLAGS_USERMOD += -lstdc++ |
|
LDFLAGS += $(LDFLAGS_USERMOD) |
|
$(Q)$(CC) $(LDFLAGS) -o $(1) $(2) $(LDFLAGS_MOD) $(LIBS) |
The linker requires arguments to be passed in a certain order. -L flags must come before -l flags, like so:
arm-none-eabi-gcc -Lpath/to/lib src.o -lstdc++
However when building with V=1, the actual output is like so:
arm-none-eabi-gcc -lstdc++ -Lpath/to/lib src.o
The resolution really requires LDFLAGS_USERMOD += -lstdc++ to change to something like LIBS_USERMOD += -lstdc++, then adding a LIBS += $(LIBS_USERMOD) to py/py.mk. LDFLAGS_USERMOD should remain, in case the module requires additional libraries. I can make a PR for this.
Code of Conduct
Yes, I agree
Port, board and/or hardware
All ports that use Make (I think)
MicroPython version
4efc5e1
Reproduction
Modify
examples/cppexample/example.cppto use astd::vector(or anything else that actually requireslibstdc++.aor any other libs):Try to build firmware for any port that uses Make (I've tested
stm32andmimxrt, but I assume others are the same):Expected behaviour
The firmware should build without errors.
Observed behaviour
The linker can't find any of the required symbols from
libstdc++.a:Additional Information
The root problem is in these 3 locations:
micropython/examples/usercmodule/cppexample/micropython.mk
Line 12 in 4efc5e1
micropython/py/py.mk
Line 71 in 4efc5e1
micropython/ports/stm32/Makefile
Line 561 in 4efc5e1
The linker requires arguments to be passed in a certain order.
-Lflags must come before-lflags, like so:However when building with
V=1, the actual output is like so:The resolution really requires
LDFLAGS_USERMOD += -lstdc++to change to something likeLIBS_USERMOD += -lstdc++, then adding aLIBS += $(LIBS_USERMOD)topy/py.mk.LDFLAGS_USERMODshould remain, in case the module requires additional libraries. I can make a PR for this.Code of Conduct
Yes, I agree