Skip to content

ESP32 build fails at link: dangerous relocation: call8: call target out of range in precompiled libOpenPLCUserLib.a (missing -mlongcalls) #160

Description

@felipeauti

ESP32 build fails at link: dangerous relocation: call8: call target out of range in precompiled libOpenPLCUserLib.a (missing -mlongcalls)

Summary

Any OpenPLC Editor v4 program that targets an ESP32 board fails at the final link step with a large batch of Xtensa dangerous relocation: call8: call target out of range errors coming from the precompiled libOpenPLCUserLib.a (both arduino_runtime_glue.o and pou_*.o). The user's Structured Text and C/C++ POU code compile fine — the failure is purely in linking the precompiled OpenPLC user library.

Root cause: the objects inside libOpenPLCUserLib.a are built without -mlongcalls. On Xtensa, a direct call8 only reaches ±512 KB. Once the firmware .text grows past that (which it always does on ESP32 because WiFi + Network + Modbus are pulled in), the linker cannot relax the direct call8 into an indirect l32r/callx8 — because the compiler never reserved the literal — so it aborts with "dangerous relocation".

The normal Arduino sketch compilation for the same board does use -mlongcalls (it is present in esp32/tools/esp32-libs/<ver>/flags/cpp_flags, alongside -fno-builtin-memcpy -fno-builtin-memset). Only the OpenPLC precompile step ([precompile] Compiling N TU(s) ...Archiving ... into libOpenPLCUserLib.a) omits it.

Environment

  • OpenPLC Editor: 4.2.10
  • STruC++: 0.6.1
  • Arduino CLI: 1.4.1
  • ESP32 Arduino core: esp32:esp32 3.3.11
  • Toolchain: xtensa-esp32-elf-g++ (esp-x32 2601)
  • Host: Windows 11 x64
  • Board targets tested: "ESP32 Generic" and "ESP32 WROOM" — both fail identically.

Representative linker errors

libOpenPLCUserLib.a(arduino_runtime_glue.o): in function `runtime_discover_tasks':
arduino_runtime_glue.cpp:(.text+0x5f): dangerous relocation: call8: call target out of range: __umoddi3
arduino_runtime_glue.cpp:(.text+0xf6): dangerous relocation: call8: call target out of range: __udivdi3
... in function `runtime_plc_cycle':
arduino_runtime_glue.cpp:(.text+0x163): dangerous relocation: call8: call target out of range: updateInputBuffers
arduino_runtime_glue.cpp:(.text+0x1aa): dangerous relocation: call8: call target out of range: updateOutputBuffers
... strucpp::debug::write_string(...): dangerous relocation: call8: call target out of range: memcpy
... strucpp::debug::read_wstring(...): dangerous relocation: call8: call target out of range: memset
libOpenPLCUserLib.a(pou_A8_OUTPUT.o): in function `strucpp::A8_OUTPUT::operator()()':
pou_A8_OUTPUT.cpp:(.text+0xa5): dangerous relocation: call8: call target out of range: a8_output_setup
pou_A8_OUTPUT.cpp:(.text+0xb4): dangerous relocation: call8: call target out of range: a8_output_loop
collect2.exe: error: ld returned 1 exit status

The out-of-range targets are libgcc (__udivdi3, __umoddi3), libc (memcpy, memset), OpenPLC runtime (updateInputBuffers, updateOutputBuffers) and the user C-block entry points (a8_output_setup, a8_output_loop) — i.e. ordinary symbols that are simply too far for a non-relaxable call8.

Steps to reproduce

  1. Create any project with a program targeting an ESP32 board (with or without a C/C++ POU — arduino_runtime_glue.o is always in the archive).
  2. Compile. Link fails with the errors above once total .text exceeds the call8 range.

Suggested fix

Add -mlongcalls to the compiler flags used by the precompile step that builds libOpenPLCUserLib.a for Xtensa targets (esp32/esp32s2/esp32s3). Ideally mirror the core's cpp_flags for that architecture, which already includes -mlongcalls -fno-builtin-memcpy -fno-builtin-memset. Without longcalls the archive can never be safely linked into a full-size ESP32 firmware.

Notes

  • Not board-specific (reproduced on ESP32 Generic and ESP32 WROOM).
  • Not related to any third-party library — a plain program without user C code hits the same glue-object relocations.
  • No link-time flag can fix this after the fact; the call8 opcodes are already emitted in the archived objects, so the archive must be recompiled with -mlongcalls.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions