py/dynruntime.mk: Let natmods be built with Clang.#19308
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #19308 +/- ##
=======================================
Coverage 98.47% 98.47%
=======================================
Files 176 176
Lines 22845 22845
=======================================
Hits 22497 22497
Misses 348 348 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
|
Code size report: |
|
Still that's not enough, as then link fails even with all the symbols being present: make CC=clang ARCH=x64 V=1
/bin/mkdir -p build/
GEN build/deflate_x64.config.h
python3 ../../../tools/mpy_ld.py '-vvv' --arch x64 --preprocess -o build/deflate_x64.config.h deflate.c
CC deflate.c
clang -I. -I../../.. -std=c99 -Os -Wall -Werror -DNDEBUG -DNO_QSTR -DMICROPY_ENABLE_DYNRUNTIME -DMP_CONFIGFILE='<build/deflate_x64.config.h>' -fpic -fno-common -U_FORTIFY_SOURCE -fno-stack-protector -DMICROPY_FLOAT_IMPL=MICROPY_FLOAT_IMPL_DOUBLE -o build/deflate.o -c deflate.c
LINK build/deflate.o
python3 ../../../tools/mpy_ld.py '-vvv' --arch x64 --qstrs build/deflate_x64.config.h -l/usr/lib/gcc/x86_64-linux-gnu/13/libgcc.a -l/usr/lib/x86_64-linux-gnu/libm.a -l/usr/lib/x86_64-linux-gnu/libc.a -o build/deflate_x64.native.mpy build/deflate.o
qstr vals: DeflateIO, GZIP, RAW, ZLIB, deflate
Loading /usr/lib/gcc/x86_64-linux-gnu/13/libgcc.a
Loading /usr/lib/x86_64-linux-gnu/libm-2.39.a
Loading /usr/lib/x86_64-linux-gnu/libmvec.a
Loading /usr/lib/x86_64-linux-gnu/libc.a
Skippping weak dependency: _dl_find_object_init
...
Skippping weak dependency: _nl_C_LC_MONETARY
using /usr/lib/x86_64-linux-gnu/libc.a:memset.o
using /usr/lib/x86_64-linux-gnu/libc.a:memset-evex-unaligned-erms.o
using /usr/lib/x86_64-linux-gnu/libc.a:memset-sse2-unaligned-erms.o
using /usr/lib/x86_64-linux-gnu/libc.a:memset-erms.o
using /usr/lib/x86_64-linux-gnu/libc.a:dl-support.o
LinkError: /usr/lib/x86_64-linux-gnu/libc.a:dl-support.o: .data non-empty
make: *** [../../../py/dynruntime.mk:255: build/deflate_x64.native.mpy] Error 1Edit: [1] diff --git i/py/dynruntime.mk w/py/dynruntime.mk
index 26f2fc720..4c5b9655f 100644
--- i/py/dynruntime.mk
+++ w/py/dynruntime.mk
@@ -148,7 +148,7 @@ else
$(error architecture '$(ARCH)' not supported)
endif
-ifeq ($(firstword $(shell $(CC) --version)),clang)
+ifeq ($(findstring clang,$(shell $(CC) --version)),clang)
CROSS =
endif
@@ -199,6 +199,10 @@ LIBM_PATH := $(PICOLIBC_ROOT)/$(PICOLIBC_ARCH)/$(PICOLIBC_ABI)/$(LIBM_NAME)
endif
endif
MPY_LD_FLAGS += $(addprefix -l, $(LIBGCC_PATH) $(LIBM_PATH))
+ifeq ($(findstring clang,$(shell $(CC) --version)),clang)
+LIBC_PATH := $(realpath $(shell $(CROSS)$(CC) $(CFLAGS) --print-file-name=libc.a))
+MPY_LD_FLAGS += $(addprefix -l, $(LIBC_PATH))
+endif
endif
ifneq ($(MPY_EXTERN_SYM_FILE),)
MPY_LD_FLAGS += --externs "$(realpath $(MPY_EXTERN_SYM_FILE))" |
3ca75d4 to
6adecfb
Compare
|
On x86 and x64 things seem to work with not emitting the trampoline in certain cases - although this will be revisited before this PR is marked as ready for review. For cross-compilation, things are a bit more involved. The way Clang seems to work is that you need to cross-compile a runtime package for your target first, and then install it in a predefined place ( If you don't have any idea how this works, like I do, or already have a GCC cross-compiler installed, you can add That said, when building $ make CC=clang CFLAGS_EXTRA='--sysroot=/usr/arm-none-eabi --target=armv7m-none-eabi' LDFLAGS_EXTRA='--target=armv7m-none-eabi' ARCH=armv7m
CC features0.c
LINK build/features0.o
Traceback (most recent call last):
File "/micropython/examples/natmod/features0/../../../tools/mpy_ld.py", line 1684, in <module>
main()
~~~~^^
File "/micropython/examples/natmod/features0/../../../tools/mpy_ld.py", line 1680, in main
do_link(args)
~~~~~~~^^^^^^
File "/micropython/examples/natmod/features0/../../../tools/mpy_ld.py", line 1565, in do_link
link_objects(env, len(native_qstr_vals))
~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/micropython/examples/natmod/features0/../../../tools/mpy_ld.py", line 1325, in link_objects
do_relocation_text(env, sec.addr, r)
~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^
File "/micropython/examples/natmod/features0/../../../tools/mpy_ld.py", line 768, in do_relocation_text
assert 0, (r_info_type, s.name, s.entry, env.arch.name)
^
AssertionError: (96, 'mp_fun_table', Container({'st_name': 155, 'st_value': 0, 'st_size': 0, 'st_info': Container({'bind': 'STB_GLOBAL', 'type': 'STT_NOTYPE'}), 'st_other': Container({'local': 0, 'visibility': 'STV_DEFAULT'}), 'st_shndx': 'SHN_UNDEF'}), 'EM_ARM')
make: *** [../../../py/dynruntime.mk:251: build/features0.native.mpy] Error 196 is |
ab69b67 to
11b8580
Compare
|
ArmV6 and ArmV7 now sort of build, with some hacks of course. Using Arm's latest stable Clang toolchain (https://github.com/ARM-software/LLVM-embedded-toolchain-for-Arm/releases/tag/release-19.1.5):
[1] |
This commit modifies the build rules for native modules in order to remove the dependence on GCC for creating native MPY files. Whilst the Unix port of MicroPython can be built with Clang by overriding the `CC` variable, natmods require a bit more work. GCC builds compilers that are tailored for a single architecture, but Clang takes the opposite approach, so a single binary may target more than one architecture. Architecture selection is, by definition, not compatible between those two compilers. These changes attempt to make things easier to handle when using Clang. Native modules can now be built with something like this: make CC=clang ARCH=<arch> CFLAGS_EXTRA='--target=<clang-target-arch>' So, for example building an x86 native module the command line will look something like this: make CC=clang ARCH=x86 CFLAGS_EXTRA='--target=i686-unknown-linux-gnu' Clang and GCC, however, have different tolerances for deviations from the chosen C standard. Whilst GCC doesn't really mind whether a typedef is defined multiple times as long as it is defined to the same value, Clang does raise a warning which is then interpreted as an error. Unfortunately #ifdef/#ifndef does not work with typedefs, and the way native modules are built meant that `py/mpconfig.h` would first include the native module's generated configuration file and then proceed with the rest of the configuration. However, both files attempt to provide aliases for both `mp_int_t` and `mp_uint_t`, and that doesn't really work. Thus, the only sane way to work around it is to rely on the presence of a definition that indicates that `mp_int_t` and `mp_uint_t` are already there to begin with, letting builds proceed on both GCC and Clang. Signed-off-by: Alessandro Gatti <a.gatti@frob.it>
This commit lets `tools/mpy_ld.py` to not emit an entry point trampoline if `mpy_init` is placed at the very beginning of the text segment. These changes are currently a workaround to make natmods work when built with Clang for x86/x64, as there will be garbage between the entry point jump and `mpy_init` shortly after. This is not a proper fix since the root cause wasn't found yet, and this may break on other architectures, but in the meantime it is enough to get things going. Signed-off-by: Alessandro Gatti <a.gatti@frob.it>
This commit introduces support for the R_ARM_GOT_PREL relocation, found in the text segment of certain Arm native modules. Until now this was not needed, since the only supported compiler for linking native modules was GCC, which did not seem to ever generate such a relocation. With the recent work in making Clang a supported compiler as well, it was quickly found out that such a compiler actually does generate such a relocation type. This relocation seem to be enough to at least make `examples/natmod/features0` link with Clang targeting `armv7m`, and then run the output native module on an appropriate interpreter running under the QEMU MPS2-AN500 target. Signed-off-by: Alessandro Gatti <a.gatti@frob.it>
This commit fixes building the `btree` module using Clang rather than using GCC, on x86, x64, and ArmV7 targets. The Clang standard library (libc) implementation of `memset` for x86 and x64 depends on functions that have a non-empty data section, which is not currently supported. Therefore we provide our own `memset` implementation that is good enough to let linking succeed for `x86` and `x64` targets. On a more general note, Clang also required some additional flags to disable an extra warning that GCC did not seem to raise. On Arm targets, `memset` is not a builtin of the compiler toolchain, so it has to be fetched from the runtime support library. Signed-off-by: Alessandro Gatti <a.gatti@frob.it>
This commit fixes building the `deflate` module using Clang rather than using GCC, on x86, x64, and ArmV7 targets. The Clang standard library (libc) implementation of `memset` for x86 and x64 depends on functions that have a non-empty data section, which is not currently supported. Therefore we provide our own `memset` implementation that is good enough to let linking succeed for `x86` and `x64` targets. On Arm targets, `memset` is not a builtin of the compiler toolchain, so it has to be fetched from the runtime support library. Signed-off-by: Alessandro Gatti <a.gatti@frob.it>
6044fb8 to
575df98
Compare
|
Prebuilt LLVM toolchains support is still a mess, I've tried almost a dozen toolchains, when I should have probably built my own from the beginning. Anyway, here's what I found:
Qualcomm's toolchain won't build The results are as follows:
|
|
This is the best I can do at the moment. Docs are missing, I'll add them to this PR once the code changes are reviewed. If needed I can split the I've also encountered |
This commit fixes building the `features2` module using Clang rather than using GCC. Parts of the floating point support code for Clang may end up in libc.a rather than its builtins support library. This is the case for the armv7m target, so we have to force linking symbols from libc.a in that case. Depending on the toolchain, this may or may not build successfully, but the location of the `roundf` symbol should not move across toolchains. This makes it work on armv6m, rv32imc, and possibly on armv7m too. The latter depends on whether Clang's builtins support library not relying on features that are unsupported by `tools/mpy_ld.py`. Signed-off-by: Alessandro Gatti <a.gatti@frob.it>
This commit fixes building the `framebuf` module using Clang rather than using GCC, on x86, x64, and ArmV7 targets. The Clang standard library (libc) implementation of `memset` for x86 and x64 depends on functions that have a non-empty data section, which is not currently supported. Therefore we provide our own `memset` implementation that is good enough to let linking succeed for `x86` and `x64` targets. On Arm targets, `memset` is not a builtin of the compiler toolchain, so it has to be fetched from the runtime support library. Signed-off-by: Alessandro Gatti <a.gatti@frob.it>
This commit fixes building the `re` module using Clang rather than using GCC, on x86, x64, and ArmV7 targets. The Clang standard library (libc) implementation of `memset` for x86 and x64 depends on functions that have a non-empty data section, which is not currently supported. Therefore we provide our own `memset` implementation that is good enough to let linking succeed for `x86` and `x64` targets. On Arm targets, `memset` is not a builtin of the compiler toolchain, so it has to be fetched from the runtime support library. Signed-off-by: Alessandro Gatti <a.gatti@frob.it>
|
For Using this toolchain, all This can be downloaded from https://www.microchip.com/en-us/products/microprocessors/64-bit-mpus/pic64-hpsc/toolchain (pick "LLVM/GCC" and use |

Summary
This PR modifies the build rules for native modules in order to remove the dependence on GCC for creating native MPY files.
Whilst the Unix port of MicroPython can be built with Clang by overriding the
CCvariable, natmods require a bit more work. GCC builds compilers that are tailored for a single architecture, but Clang takes the opposite approach, so a single binary may target more than one architecture. Architecture selection is, by definition, not compatible between those two compilers.These changes attempt to make things easier to handle when using Clang. Native modules can now be built with something like this:
make CC=clang ARCH=<arch> CFLAGS_EXTRA='--target=<clang-target-arch>'. So, for example building an x86 native module the command line will look something like this:make CC=clang ARCH=x86 CFLAGS_EXTRA='--target=i686-unknown-linux-gnu'.Clang and GCC, however, have different tolerances for deviations from the chosen C standard. Whilst GCC doesn't really mind whether a typedef is defined multiple times as long as it is defined to the same value, Clang does raise a warning which is then interpreted as an error.
Unfortunately #ifdef/#ifndef does not work with typedefs, and the way native modules are built meant that
py/mpconfig.hwould first include the native module's generated configuration file and then proceed with the rest of the configuration. However, both files attempt to provide aliases for bothmp_int_tandmp_uint_t, and that doesn't really work. Thus, the only sane way to work around it is to rely on the presence of a definition that indicates thatmp_int_tandmp_uint_tare already there to begin with, letting builds proceed on both GCC and Clang.Testing
All natmods in
examples/natmodwere attempted to be built for x86 using the command line mentioned in the summary section. Results are as follows:btree: fails to compile because__bt_close,__bt_sync, andbt_metado not have a prototypedeflate:fails to link becausebuilds on both x86 and x64memsetis not definedfeatures0: builds on x86, x64, and armv7mfeatures1: builds on x86, x64, and armv7mfeatures2: builds on x86, x64, and armv7mfeatures3: builds on x86, x64, and armv7mfeatures4: builds on x86, x64, and armv7mframebuf:fails to link becausebuilds on both x86 and x64memsetis not definedheapq: builds on x86, x64, and armv7mrandom: buildsre:fails to link becausebuilds on both x86 and x64.memsetis not definedImportingWith Clang the entry trampoline gets emitted incorrectly, so that had to be patched somehow.features0in a Clang-built x86 interpreter, though, crashes due to "something" happening when callingmp_call_function_0. The same happens on an x86 interpreter built with GCC, and on x64 too. Fun for the whole architecture family.The same procedure was done successfully without overriding
CC,CFLAGS_EXTRA, andLDFLAGS_EXTRA, still building withARCH=x86. Then natmods were built forxtensawinbut specifyingCROSS=xtensa-esp32s3-elf-to make sure the new compiler was picked up correctly.Trade-offs and Alternatives
None I can see, although the crashes and link failures need to be sorted out first.
Generative AI
I did not use generative AI tools when creating this PR.
This is marked as draft due to the obvious shortcomings of the PR in its current state. Still working on it,
tools/mpy_ld.pydoesn't like something about the object files emitted by clang - maybe it's the section ordering or something else, but at least I have a rough idea on where to look.