Firmware build resulting in linker error

Hello

I’m trying to build the firmware, so far without success. Things seem to compile correcty without errors or warnings, but then at the end I get lots and lots of linker errors.
The end of the output looks like below (there is plenty before that, but all follows the same pattern):

/home/felix/cache/gcc/bin/../lib/gcc/arm-none-eabi/13.2.1/../../../../arm-none-eabi/bin/ld: /home/felix/src/openmv/lib/micropython/ports/stm32/extint.c:836:(.text.Handle_EXTI_Irq+0x48): undefined reference to `gc_lock'
/home/felix/cache/gcc/bin/../lib/gcc/arm-none-eabi/13.2.1/../../../../arm-none-eabi/bin/ld: /home/felix/src/openmv/lib/micropython/ports/stm32/extint.c:838:(.text.Handle_EXTI_Irq+0x4e): undefined reference to `nlr_push'
/home/felix/cache/gcc/bin/../lib/gcc/arm-none-eabi/13.2.1/../../../../arm-none-eabi/bin/ld: /home/felix/src/openmv/lib/micropython/ports/stm32/extint.c:839:(.text.Handle_EXTI_Irq+0x5c): undefined reference to `mp_call_function_1'
/home/felix/cache/gcc/bin/../lib/gcc/arm-none-eabi/13.2.1/../../../../arm-none-eabi/bin/ld: /home/felix/src/openmv/lib/micropython/ports/stm32/extint.c:840:(.text.Handle_EXTI_Irq+0x60): undefined reference to `nlr_pop'
/home/felix/cache/gcc/bin/../lib/gcc/arm-none-eabi/13.2.1/../../../../arm-none-eabi/bin/ld: /home/felix/src/openmv/lib/micropython/ports/stm32/extint.c:848:(.text.Handle_EXTI_Irq+0x64): undefined reference to `gc_unlock'
/home/felix/cache/gcc/bin/../lib/gcc/arm-none-eabi/13.2.1/../../../../arm-none-eabi/bin/ld: /home/felix/src/openmv/lib/micropython/ports/stm32/extint.c:849:(.text.Handle_EXTI_Irq+0x68): undefined reference to `mp_sched_unlock'
/home/felix/cache/gcc/bin/../lib/gcc/arm-none-eabi/13.2.1/../../../../arm-none-eabi/bin/ld: /home/felix/src/openmv/lib/micropython/ports/stm32/extint.c:845:(.text.Handle_EXTI_Irq+0x80): undefined reference to `mp_printf'
/home/felix/cache/gcc/bin/../lib/gcc/arm-none-eabi/13.2.1/../../../../arm-none-eabi/bin/ld: /home/felix/src/openmv/lib/micropython/ports/stm32/extint.c:846:(.text.Handle_EXTI_Irq+0x88): undefined reference to `mp_obj_print_exception'
/home/felix/cache/gcc/bin/../lib/gcc/arm-none-eabi/13.2.1/../../../../arm-none-eabi/bin/ld: /home/felix/src/openmv/lib/micropython/ports/stm32/extint.c:846:(.text.Handle_EXTI_Irq+0x90): undefined reference to `mp_state_ctx'
/home/felix/cache/gcc/bin/../lib/gcc/arm-none-eabi/13.2.1/../../../../arm-none-eabi/bin/ld: /home/felix/src/openmv/lib/micropython/ports/stm32/extint.c:846:(.text.Handle_EXTI_Irq+0xa0): undefined reference to `mp_plat_print'
/home/felix/cache/gcc/bin/../lib/gcc/arm-none-eabi/13.2.1/../../../../arm-none-eabi/bin/ld: /home/felix/src/openmv/lib/micropython/ports/stm32/extint.c:829:(.text.Handle_EXTI_Irq+0x40): undefined reference to `mp_sched_schedule'
/home/felix/cache/gcc/bin/../lib/gcc/arm-none-eabi/13.2.1/../../../../arm-none-eabi/bin/ld: /home/felix/src/openmv/build/lib/micropython/pins_OPENMV4.o:/home/felix/src/openmv/build/lib/micropython/pins_OPENMV4.c:792:(.rodata.machine_pin_board_pins_locals_dict+0x0): undefined reference to `mp_type_dict'
/home/felix/cache/gcc/bin/../lib/gcc/arm-none-eabi/13.2.1/../../../../arm-none-eabi/bin/ld: /home/felix/src/openmv/build/lib/micropython/pins_OPENMV4.o:/home/felix/src/openmv/build/lib/micropython/pins_OPENMV4.c:755:(.rodata.machine_pin_cpu_pins_locals_dict+0x0): undefined reference to `mp_type_dict'
Memory region         Used Size  Region Size  %age Used
            DTCM:          0 GB       128 KB      0.00%
            ITCM:         64 KB        64 KB    100.00%
           SRAM0:        512 KB       512 KB    100.00%
           SRAM1:      282824 B       280 KB     98.64%
           SRAM2:          8 KB         8 KB    100.00%
           SRAM4:         64 KB        64 KB    100.00%
       FLASH_FFS:          0 GB       128 KB      0.00%
      FLASH_TEXT:      260928 B      1664 KB     15.31%
collect2: error: ld returned 1 exit status
make: *** [/home/felix/src/openmv/ports/stm32/omv_portconfig.mk:250: firmware] Error 1

I followed the “Linux Build” guide, cloning the full repo (–recursively). I’ve also verified I have the correct toolchain gcc version 13.2.1 20231009 (Arm GNU Toolchain 13.2.rel1.
It fails the same way for both OPENMV4 and OPENMV4P targets.

I’m doing this on a VM running Linux Mint 22.1.

Any ideas what’s wrong here?
Thanks
Felix

Hi Felix,

The docs are out of date with regard to the right version of GCC to use. Please grab 14.3: openmv/tools/ci.sh at master · openmv/openmv · GitHub

However, did you build mpy-cross per the guide?

cd openmv
make -j$(nproc) -C lib/micropython/mpy-cross   # Builds Micropython mpy cross-compiler
make -j$(nproc) TARGET=<TRAGET_NAME>           # Builds the OpenMV firmware

Hi

Thanks for the hint. I’ve now installed the version you mentioned (path is also added to $PATH:

$ arm-none-eabi-gcc --version
arm-none-eabi-gcc (Arm GNU Toolchain 14.3.Rel1 (Build arm-14.174)) 14.3.1 20250623

Then I downloaded again the repo as per guide:

$ git clone --depth=1 https://github.com/openmv/openmv.git
$ cd openmv
$ git submodule update --init --depth=1 --no-single-branch
$ git -C lib/micropython/ submodule update --init --depth=1

Then I ran the following from the openmv repository directory:

$ make -j1 -C lib/micropython/mpy-cross
$ make -j1 TARGET=OPENMV4

(I have to limit to a single CPU as otherwise my VM keeps freezing up and crashes, but even with this, the VM is still not always stable..)

The cross-compiler seems to be built correctly, but building the firmware still results in the errors as above, ending in:

[...]
/home/felix/bin/arm-none-eabi-toolchain/bin/../lib/gcc/arm-none-eabi/14.3.1/../../../../arm-none-eabi/bin/ld: /home/felix/src/openmv/lib/micropython/ports/stm32/extint.c:846:(.text.Handle_EXTI_Irq+0x90): undefined reference to `mp_state_ctx'
/home/felix/bin/arm-none-eabi-toolchain/bin/../lib/gcc/arm-none-eabi/14.3.1/../../../../arm-none-eabi/bin/ld: /home/felix/src/openmv/lib/micropython/ports/stm32/extint.c:846:(.text.Handle_EXTI_Irq+0xa0): undefined reference to `mp_plat_print'
/home/felix/bin/arm-none-eabi-toolchain/bin/../lib/gcc/arm-none-eabi/14.3.1/../../../../arm-none-eabi/bin/ld: /home/felix/src/openmv/lib/micropython/ports/stm32/extint.c:829:(.text.Handle_EXTI_Irq+0x40): undefined reference to `mp_sched_schedule'
/home/felix/bin/arm-none-eabi-toolchain/bin/../lib/gcc/arm-none-eabi/14.3.1/../../../../arm-none-eabi/bin/ld: (mp_sched_schedule): Unknown destination type (ARM/Thumb) in /home/felix/src/openmv/build/lib/micropython/extint.o
/home/felix/src/openmv/lib/micropython/ports/stm32/extint.c:829:(.text.Handle_EXTI_Irq+0x40): dangerous relocation: unsupported relocation
/home/felix/bin/arm-none-eabi-toolchain/bin/../lib/gcc/arm-none-eabi/14.3.1/../../../../arm-none-eabi/bin/ld: /home/felix/src/openmv/build/lib/micropython/pins_OPENMV4.o:/home/felix/src/openmv/build/lib/micropython/pins_OPENMV4.c:792:(.rodata.machine_pin_board_pins_locals_dict+0x0): undefined reference to `mp_type_dict'
/home/felix/bin/arm-none-eabi-toolchain/bin/../lib/gcc/arm-none-eabi/14.3.1/../../../../arm-none-eabi/bin/ld: /home/felix/src/openmv/build/lib/micropython/pins_OPENMV4.o:/home/felix/src/openmv/build/lib/micropython/pins_OPENMV4.c:755:(.rodata.machine_pin_cpu_pins_locals_dict+0x0): undefined reference to `mp_type_dict'
Memory region         Used Size  Region Size  %age Used
            DTCM:           0 B       128 KB      0.00%
            ITCM:         64 KB        64 KB    100.00%
           SRAM0:        512 KB       512 KB    100.00%
           SRAM1:      283288 B       280 KB     98.80%
           SRAM2:          8 KB         8 KB    100.00%
           SRAM4:         64 KB        64 KB    100.00%
       FLASH_FFS:           0 B       128 KB      0.00%
      FLASH_TEXT:      267532 B      1664 KB     15.70%
collect2: error: ld returned 1 exit status
make: *** [/home/felix/src/openmv/ports/stm32/omv_portconfig.mk:250: firmware] Error 1

Running $ make -j1 TARGET=OPENMV4 again (out of curiosity):

Use make V=1 or set BUILD_VERBOSE in your environment to increase build verbosity.
Including User C Module from /home/felix/src/openmv/modules
Memory region         Used Size  Region Size  %age Used
            DTCM:           0 B       128 KB      0.00%
            ITCM:         64 KB        64 KB    100.00%
           SRAM0:        512 KB       512 KB    100.00%
           SRAM1:      284860 B       280 KB     99.35%
           SRAM2:          8 KB         8 KB    100.00%
           SRAM4:         64 KB        64 KB    100.00%
       FLASH_FFS:           0 B       128 KB      0.00%
      FLASH_TEXT:     1663196 B      1664 KB     97.61%
arm-none-eabi-objcopy: '/home/felix/src/openmv/build/bin/bootloader.bin': No such file
make: *** [/home/felix/src/openmv/ports/stm32/omv_portconfig.mk:254: firmware] Error 1

firmware.bin, firmware.dfu, firmware.elf are generated (after the first or second time, I only checked after the second), but nothing else.
The size of the firmware.bin file created is the same as the one downloaded from github.
The development version however doesn’t work for me: I’m getting errors about sensor control failed when setting up gain or exposure, and buffer errors when taking a snapshot (I’m using the global shutter sensor). This is the same with the .bin from github or the one compiled myself as above. I’m guessing the development version messes with the global shutter support.
Not a real issue as long as there is some version to use, but that didn’t work so far, see below.

Felix

As the development version doesn’t work, I tried checking out the 4.7.0 version (the latest stable as I understand), doing a make cleanin both the main repo and the mpu-cross compiler directory, and then rebuilding as above, I get the same very similar errors as when building the latest version.

Running $ make -j1 TARGET=OPENMV4 again, I’m getting new errors (final part shown)

[...]
/home/felix/bin/arm-none-eabi-toolchain/bin/../lib/gcc/arm-none-eabi/14.3.1/../../../../arm-none-eabi/bin/ld: (i2c_slave_irq_handler): Unknown destination type (ARM/Thumb) in /home/felix/src/openmv/build/lib/micropython/i2c.o
/home/felix/src/openmv/lib/micropython/ports/stm32/i2c.c:656:(.text.I2C2_ER_IRQHandler+0xa): dangerous relocation: unsupported relocation
/home/felix/bin/arm-none-eabi-toolchain/bin/../lib/gcc/arm-none-eabi/14.3.1/../../../../arm-none-eabi/bin/ld: /home/felix/src/openmv/build/lib/micropython/i2c.o: in function `I2C4_EV_IRQHandler':
/home/felix/src/openmv/lib/micropython/ports/stm32/i2c.c:708:(.text.I2C4_EV_IRQHandler+0xa): undefined reference to `i2c_slave_irq_handler'
/home/felix/bin/arm-none-eabi-toolchain/bin/../lib/gcc/arm-none-eabi/14.3.1/../../../../arm-none-eabi/bin/ld: (i2c_slave_irq_handler): Unknown destination type (ARM/Thumb) in /home/felix/src/openmv/build/lib/micropython/i2c.o
/home/felix/src/openmv/lib/micropython/ports/stm32/i2c.c:708:(.text.I2C4_EV_IRQHandler+0xa): dangerous relocation: unsupported relocation
/home/felix/bin/arm-none-eabi-toolchain/bin/../lib/gcc/arm-none-eabi/14.3.1/../../../../arm-none-eabi/bin/ld: /home/felix/src/openmv/build/lib/micropython/i2c.o: in function `I2C4_ER_IRQHandler':
/home/felix/src/openmv/lib/micropython/ports/stm32/i2c.c:724:(.text.I2C4_ER_IRQHandler+0xa): undefined reference to `i2c_slave_irq_handler'
/home/felix/bin/arm-none-eabi-toolchain/bin/../lib/gcc/arm-none-eabi/14.3.1/../../../../arm-none-eabi/bin/ld: (i2c_slave_irq_handler): Unknown destination type (ARM/Thumb) in /home/felix/src/openmv/build/lib/micropython/i2c.o
/home/felix/src/openmv/lib/micropython/ports/stm32/i2c.c:724:(.text.I2C4_ER_IRQHandler+0xa): dangerous relocation: unsupported relocation
Memory region         Used Size  Region Size  %age Used
            DTCM:           0 B       128 KB      0.00%
            ITCM:         64 KB        64 KB    100.00%
           SRAM0:        512 KB       512 KB    100.00%
           SRAM1:      284996 B       280 KB     99.40%
           SRAM2:          8 KB         8 KB    100.00%
           SRAM4:         64 KB        64 KB    100.00%
       FLASH_FFS:           0 B       128 KB      0.00%
      FLASH_TEXT:     1655164 B      1664 KB     97.14%
collect2: error: ld returned 1 exit status
make: *** [/home/felix/src/openmv/ports/stm32/omv_portconfig.mk:228: firmware] Error 1

This time, no build artifacts are generated at all.

I don’t have any idea what’s wrong here. Any ideas on how to further debug or solve this?

Thank you
Felix

Hi, I’m not really sure about those errors. You’ll need to debug them yourself. I just tried this on my machine without issues:

git clone git@github.com:openmv/openmv.git --recursive
cd lib/micropython/mpy-cross
make
cd ../../..
make TARGET=OPENMV4P

And it built the firmware correctly. I have arm gcc 14.3-rel1.

Not really sure what your issue is… I cannot duplicate. However, the best guide I have is to follow the docker file instructions for machine setup: openmv/docker at master · openmv/openmv · GitHub

Nope, no luck with your steps, still the exact same errors: No errors or warnings during compilation, but linking seems to completely fail.

I guess I have to try the Docker.based approach…

The docker build script is referring to LLVM (which the docker file has installed). Is LLVM needed? (but then I didn’t get any errors before linking).

Thanks
Felix

It’s just needed for the AE3 to compile one Helium file. We may use it more as Helium optimizations grow in our code base. Previously, we saw a 20% performance over GCC when using Helium with LLVM.

Hi

Ok, the Docker build works as advertised. I checked out the latest release (v4.7.0) and built that, this works as well.

I’m now trying to avoid everything being recompiled after any change (based on v4.7.0), i.e. to take advantage of make, otherwise its taking a very long time each time.

To this extent I tried to comment some of the lines in the build.shscript (the ones with ##) but that only results in make[1]: *** lib/micropython/ports/stm32: No such file or directory. Stop (I tried commenting out more, with similar results).

[...]
# Update submodules.
##git submodule update --init --depth=1
make -j$(nproc) TARGET=${TARGET} submodules

# Build the firmware.
##make -j$(nproc) BUILD=${BUILD_DIR} clean
make -j$(nproc) -C lib/micropython/mpy-cross
make -j$(nproc) BUILD=${BUILD_DIR} TARGET=${TARGET} LLVM_PATH=/workspace/llvm/bin
[...]

This being the first time I’m dealing with Docker, I don’t really have any idea what I’m doing here…
Any (specific or general) pointers about what to do would be apprecciated.

Thank you
Felix

ChatGPT has some great answers here: ChatGPT - Partial docker builds

Thanks! I’ll dig into that.

Felix