Multiple definition of 'ceill'

Hi,
I’m getting a multiple definition of ‘ceill’ error in the linker.
The full error is thus:

Linking .pio/build/ut61e-wifi/firmware.elf
/Users/peter/.platformio/packages/toolchain-xtensa/bin/../lib/gcc/xtensa-lx106-elf/10.3.0/../../../../xtensa-lx106-elf/bin/ld: /Users/peter/.platformio/packages/framework-arduinoespressif8266/tools/sdk/lib/libstdc++.a(math_stubs_long_double.o): in function `ceill':
/workdir/repo/gcc-gnu/libstdc++-v3/src/c++98/math_stubs_long_double.cc:76: multiple definition of `ceill'; /Users/peter/.platformio/packages/toolchain-xtensa/bin/../lib/gcc/xtensa-lx106-elf/10.3.0/../../../../xtensa-lx106-elf/lib/libm.a(lib_a-ceill.o):/workdir/repo/newlib/newlib/libm/common/ceill.c:38: first defined here
collect2: error: ld returned 1 exit status
*** [.pio/build/ut61e-wifi/firmware.elf] Error 1

platformio.ini contains:

[platformio]
default_envs = ut61e-wifi

[env]
monitor_speed = 115200
build_flags = -fexceptions

[env:ut61e-wifi]
platform = espressif8266
board = d1_mini
framework = arduino
lib_extra_dirs = ~/Documents/Arduino/libraries

It appears to me that ‘ceill’ is being picked up from two different (math) libraries and I don’t think I am defining anything in the .h file that I shouldn’t be.

I’m really not sure where to start tracking this down or what to do about it.
Any pointers gratefully received.

What is the minimal code and set of libraries to reproduce this?

Hi @maxgerhardt ,

Thanks for responding.

I know ultimately this is the way to resolve an issue like this.
As you know there can be a huge amount of wrok involved in isolating a problem like this - that is work in progress (and hopefully when isolated, the solution will be self evident!).

However in the mean time, I’m looking to see if anyone has seen this issue and may have pointers as to what to look at or try.

I’ll post again here when I can isolate the issue.

Well basic info about this info isn’t too hard to get.

Can you post the “Dependency Graph” shown at the beginning of compilation?
When you execute the project task “Advanced → Verbose Build”, what is the final shown (linker) command? This reveals at least the list of linked in libraries and narrows down the problem immensly.

Something I can say at least is that I can’t reproduce the problem.

With a platformio.ini of

[env]
monitor_speed = 115200
build_flags = -fexceptions

[env:ut61e-wifi]
platform = espressif8266
board = d1_mini
framework = arduino

and code

#include <Arduino.h>
#include <math.h>
#include <tgmath.h>

void setup() {
  Serial.begin(115200);
  long double res = (long double) Serial.parseFloat();
  res = ceill(res);
  Serial.print("Result: ");
  Serial.println((double) res);
}

void loop() {
}

I get final linker command of

xtensa-lx106-elf-g++ -o .pio\build\ut61e-wifi\firmware.elf -T eagle.flash.4m1m.ld -Os -nostdlib -Wl,–no-check-sections -Wl,-static -Wl,–gc-sections -Wl,-wrap,system_restart_local -Wl,-wrap,spi_flash_read -u app_entry -u _printf_float -u _scanf_float -u _DebugExceptionVector -u _DoubleExceptionVector -u _KernelExceptionVector -u _NMIExceptionVector -u _UserExceptionVector .pio\build\ut61e-wifi\src\main.cpp.o -L.pio\build\ut61e-wifi -L.pio\build\ut61e-wifi\ld -LC:\Users\Max\.platformio\packages\framework-arduinoespressif8266\tools\sdk\lib -LC:\Users\Max\.platformio\packages\framework-arduinoespressif8266\tools\sdk\ld -LC:\Users\Max\.platformio\packages\framework-arduinoespressif8266\tools\sdk\lib\NONOSDK22x_190703 -Wl,–start-group .pio\build\ut61e-wifi\libFrameworkArduinoVariant.a .pio\build\ut61e-wifi\libFrameworkArduino.a -lhal -lphy -lpp -lnet80211 -lwpa -lcrypto -lmain -lwps -lbearssl -lespnow -lsmartconfig -lairkiss -lwpa2 -lm -lc -lgcc -llwip2-536-feat -lstdc++ -Wl,–end-group

The result is successful.

RAM:   [===       ]  34.3% (used 28060 bytes from 81920 bytes)
Flash: [===       ]  28.2% (used 294637 bytes from 1044464 bytes)
============= [SUCCESS] Took 9.13 seconds ===============

And the firmware really does call ceill.

>C:\Users\Max\.platformio\packages\toolchain-xtensa\bin\xtensa-lx106-elf-objdump.exe -d .pio\build\ut61e-wifi\firmware.elf | grep -A35 "<setup>:"
40201028 <setup>:
40201028:       e0c112                  addi    a1, a1, -32
4020102b:       61c9                    s32i.n  a12, a1, 24
4020102d:       fffbc1                  l32r    a12, 4020101c <core_version+0x4>
40201030:       070c                    movi.n  a7, 0
40201032:       e1a032                  movi    a3, 225
40201035:       075d                    mov.n   a5, a7
40201037:       160c                    movi.n  a6, 1
40201039:       c41c                    movi.n  a4, 28
4020103b:       113370                  slli    a3, a3, 9
4020103e:       0c2d                    mov.n   a2, a12
40201040:       7109                    s32i.n  a0, a1, 28
40201042:       001cc5                  call0   40201210 <_ZN14HardwareSerial5beginEm12SerialConfig10SerialModehb>
40201045:       0c2d                    mov.n   a2, a12
40201047:       005c05                  call0   40201608 <_ZN6Stream10parseFloatEv>
4020104a:       fff601                  l32r    a0, 40201024 <core_version+0xc>
4020104d:       0000c0                  callx0  a0
40201050:       0ba3c5                  call0   4020ca90 <ceill>  ; ceill() called
40201053:       035d                    mov.n   a5, a3
40201055:       fff231                  l32r    a3, 40201020 <core_version+0x8>
40201058:       024d                    mov.n   a4, a2
4020105a:       0c2d                    mov.n   a2, a12
4020105c:       0149                    s32i.n  a4, a1, 0
4020105e:       1159                    s32i.n  a5, a1, 4
40201060:       003045                  call0   40201368 <_ZN5Print5printEPKc>
40201063:       0148                    l32i.n  a4, a1, 0
40201065:       1158                    l32i.n  a5, a1, 4
40201067:       0c2d                    mov.n   a2, a12
40201069:       260c                    movi.n  a6, 2
4020106b:       0036c5                  call0   402013d8 <_ZN5Print7printlnEdi>
4020106e:       7108                    l32i.n  a0, a1, 28
40201070:       61c8                    l32i.n  a12, a1, 24
40201072:       20c112                  addi    a1, a1, 32
40201075:       f00d                    ret.n

Used packages

PLATFORM: Espressif 8266 (3.1.0) > WeMos D1 R2 and mini
HARDWARE: ESP8266 80MHz, 80KB RAM, 4MB Flash
PACKAGES:
 - framework-arduinoespressif8266 3.30001.210627 (3.0.1)
 - tool-esptool 1.413.0 (4.13)
 - tool-esptoolpy 1.30000.201119 (3.0.0)
 - toolchain-xtensa 2.100300.0 (10.3.0)

And no dependencies.

Can I say firstly @maxgerhardt how much I appreciate you spending time on this.
I am a long time lapsed embedded coder and have returned recent;y but I’m somewhat rusty :slight_smile:

Verbose compile output is thus:


Processing ut61e-wifi (platform: espressif8266; board: d1_mini; framework: arduino; lib_extra_dirs: ~/Documents/Arduino/libraries; monitor_speed: 115200; build_flags: -fexceptions)
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
CONFIGURATION: https://docs.platformio.org/page/boards/espressif8266/d1_mini.html
PLATFORM: Espressif 8266 (3.1.0) > WeMos D1 R2 and mini
HARDWARE: ESP8266 80MHz, 80KB RAM, 4MB Flash
PACKAGES: 
 - framework-arduinoespressif8266 3.30001.210627 (3.0.1) 
 - tool-esptool 1.413.0 (4.13) 
 - tool-esptoolpy 1.30000.201119 (3.0.0) 
 - toolchain-xtensa 2.100300.210717 (10.3.0)
xtensa-lx106-elf-g++ -o "/Users/peter/Documents/SRC/210724-193948-d1_mini/src/UT61EWiFiD1Mini.ino.cpp" -x c++ -fpreprocessed -dD -E "/var/folders/53/4zknrtqx1zb93lps1z2vymjr0000gn/T/tmp9ojodau8"
LDF: Library Dependency Finder -> http://bit.ly/configure-pio-ldf
LDF Modes: Finder ~ chain, Compatibility ~ soft
Found 51 compatible libraries
Scanning dependencies...
Dependency Graph
|-- <ut61e_display> (/Users/peter/Documents/SRC/210724-193948-d1_mini/lib/ut61e_display)
|-- <Adafruit NeoPixel> 1.8.4 (/Users/peter/Documents/Arduino/libraries/Adafruit_NeoPixel)
|-- <ESP8266WiFi> 1.0 (/Users/peter/.platformio/packages/framework-arduinoespressif8266/libraries/ESP8266WiFi)
|-- <PubSubClient> 2.8 (/Users/peter/Documents/Arduino/libraries/PubSubClient)
|-- <EspSoftwareSerial> 6.12.6 (/Users/peter/.platformio/packages/framework-arduinoespressif8266/libraries/SoftwareSerial)
Building in release mode
xtensa-lx106-elf-g++ -o .pio/build/ut61e-wifi/src/UT61EWiFiD1Mini.ino.cpp.o -c -fno-rtti -std=gnu++17 -fno-exceptions -fexceptions -Os -mlongcalls -mtext-section-literals -falign-functions=4 -U__STRICT_ANSI__ -D_GNU_SOURCE -ffunction-sections -fdata-sections -Wall -Werror=return-type -free -fipa-pta -DPLATFORMIO=50101 -DESP8266 -DARDUINO_ARCH_ESP8266 -DARDUINO_ESP8266_WEMOS_D1MINI -DF_CPU=80000000L -D__ets__ -DICACHE_FLASH -DARDUINO=10805 -DARDUINO_BOARD=\"PLATFORMIO_D1_MINI\" -DFLASHMODE_DIO -DLWIP_OPEN_SRC -DNONOSDK22x_190703=1 -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 -DVTABLES_IN_FLASH -DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 -Iinclude -Isrc -I/Users/peter/.platformio/packages/framework-arduinoespressif8266/libraries/SoftwareSerial/src -I/Users/peter/Documents/Arduino/libraries/PubSubClient/src -I/Users/peter/.platformio/packages/framework-arduinoespressif8266/libraries/ESP8266WiFi/src -I/Users/peter/Documents/Arduino/libraries/Adafruit_NeoPixel -Ilib/ut61e_display/src -I/Users/peter/.platformio/packages/framework-arduinoespressif8266/tools/sdk/include -I/Users/peter/.platformio/packages/framework-arduinoespressif8266/cores/esp8266 -I/Users/peter/.platformio/packages/toolchain-xtensa/include -I/Users/peter/.platformio/packages/framework-arduinoespressif8266/tools/sdk/lwip2/include -I/Users/peter/.platformio/packages/framework-arduinoespressif8266/variants/d1_mini src/UT61EWiFiD1Mini.ino.cpp
xtensa-lx106-elf-g++ -o .pio/build/ut61e-wifi/libb98/ut61e_display/ut61e_display.cpp.o -c -fno-rtti -std=gnu++17 -fno-exceptions -fexceptions -Os -mlongcalls -mtext-section-literals -falign-functions=4 -U__STRICT_ANSI__ -D_GNU_SOURCE -ffunction-sections -fdata-sections -Wall -Werror=return-type -free -fipa-pta -DPLATFORMIO=50101 -DESP8266 -DARDUINO_ARCH_ESP8266 -DARDUINO_ESP8266_WEMOS_D1MINI -DF_CPU=80000000L -D__ets__ -DICACHE_FLASH -DARDUINO=10805 -DARDUINO_BOARD=\"PLATFORMIO_D1_MINI\" -DFLASHMODE_DIO -DLWIP_OPEN_SRC -DNONOSDK22x_190703=1 -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 -DVTABLES_IN_FLASH -DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 -Ilib/ut61e_display/src -I/Users/peter/.platformio/packages/framework-arduinoespressif8266/tools/sdk/include -I/Users/peter/.platformio/packages/framework-arduinoespressif8266/cores/esp8266 -I/Users/peter/.platformio/packages/toolchain-xtensa/include -I/Users/peter/.platformio/packages/framework-arduinoespressif8266/tools/sdk/lwip2/include -I/Users/peter/.platformio/packages/framework-arduinoespressif8266/variants/d1_mini lib/ut61e_display/src/ut61e_display.cpp
xtensa-lx106-elf-ar rc .pio/build/ut61e-wifi/libb98/libut61e_display.a .pio/build/ut61e-wifi/libb98/ut61e_display/ut61e_display.cpp.o
xtensa-lx106-elf-ranlib .pio/build/ut61e-wifi/libb98/libut61e_display.a
xtensa-lx106-elf-g++ -o .pio/build/ut61e-wifi/firmware.elf -T eagle.flash.4m1m.ld -Os -nostdlib -Wl,--no-check-sections -Wl,-static -Wl,--gc-sections -Wl,-wrap,system_restart_local -Wl,-wrap,spi_flash_read -u app_entry -u _printf_float -u _scanf_float -u _DebugExceptionVector -u _DoubleExceptionVector -u _KernelExceptionVector -u _NMIExceptionVector -u _UserExceptionVector .pio/build/ut61e-wifi/src/UT61EWiFiD1Mini.ino.cpp.o -L.pio/build/ut61e-wifi -L.pio/build/ut61e-wifi/ld -L/Users/peter/.platformio/packages/framework-arduinoespressif8266/tools/sdk/lib -L/Users/peter/.platformio/packages/framework-arduinoespressif8266/tools/sdk/ld -L/Users/peter/.platformio/packages/framework-arduinoespressif8266/tools/sdk/lib/NONOSDK22x_190703 -Wl,--start-group .pio/build/ut61e-wifi/libb98/libut61e_display.a .pio/build/ut61e-wifi/lib768/libAdafruit_NeoPixel.a .pio/build/ut61e-wifi/lib7e9/libESP8266WiFi.a .pio/build/ut61e-wifi/lib6c3/libPubSubClient.a .pio/build/ut61e-wifi/lib54e/libSoftwareSerial.a .pio/build/ut61e-wifi/libFrameworkArduinoVariant.a .pio/build/ut61e-wifi/libFrameworkArduino.a -lhal -lphy -lpp -lnet80211 -lwpa -lcrypto -lmain -lwps -lbearssl -lespnow -lsmartconfig -lairkiss -lwpa2 -lm -lc -lgcc -llwip2-536-feat -lstdc++ -Wl,--end-group
/Users/peter/.platformio/packages/toolchain-xtensa/bin/../lib/gcc/xtensa-lx106-elf/10.3.0/../../../../xtensa-lx106-elf/bin/ld: /Users/peter/.platformio/packages/framework-arduinoespressif8266/tools/sdk/lib/libstdc++.a(math_stubs_long_double.o): in function `ceill':
/workdir/repo/gcc-gnu/libstdc++-v3/src/c++98/math_stubs_long_double.cc:76: multiple definition of `ceill'; /Users/peter/.platformio/packages/toolchain-xtensa/bin/../lib/gcc/xtensa-lx106-elf/10.3.0/../../../../xtensa-lx106-elf/lib/libm.a(lib_a-ceill.o):/workdir/repo/newlib/newlib/libm/common/ceill.c:38: first defined here
collect2: error: ld returned 1 exit status
*** [.pio/build/ut61e-wifi/firmware.elf] Error 1
=================================================================================================== [FAILED] Took 6.77 seconds ===================================================================================================
The terminal process "platformio 'run', '--verbose', '--environment', 'ut61e-wifi'" terminated with exit code: 1.

The trick is I’m not directly calling ceill() so it’s hard to trace in the first instance.
I suspect two libraries are somehow pulling it in from different places.
It would be good if I could see the whole library dependence tree, not just the ones I specify…

Next I guess I can try running that objdump command to see if I can find where it’s coming in.

Here it is has both flags because -fno-exceptions is the default and the platformio.ini doesn’t do

build_unflags = -fno-exceptions

(docs). You might want to add that just so that it’s clean.

Based on the dependency graph

And the linker command

The linked libraries are all clear.

I’ve expanded my original example to

[env]
monitor_speed = 115200
build_flags = -fexceptions

[env:ut61e-wifi]
platform = espressif8266
board = d1_mini
framework = arduino
lib_deps =
   knolleary/PubSubClient @ ^2.8
   adafruit/Adafruit NeoPixel @ 1.8.4

with code

#include <Arduino.h>
#include <math.h>
#include <tgmath.h>
#include <ESP8266WiFi.h>
#include <SoftwareSerial.h>
#include <PubSubClient.h>
#include <Adafruit_NeoPixel.h>

SoftwareSerial ser(6,7);
WiFiClient espClient;
PubSubClient client(espClient);

#define PIN        6 // On Trinket or Gemma, suggest changing this to 1
#define NUMPIXELS 16 // Popular NeoPixel ring size
Adafruit_NeoPixel pixels(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);

void setup() {
  WiFi.begin();
  Serial.begin(115200);
  ser.begin(115200);
  pixels.begin(); // INITIALIZE NeoPixel strip object (REQUIRED)
  long double res = (long double) Serial.parseFloat();
  res = ceill(res);
  Serial.print("Result: ");
  Serial.println((double) res);
}

void loop() {
  client.loop();
}

which now gives me the library dependency graph

Dependency Graph
|-- <PubSubClient> 2.8.0 (C:\Users\Max\temp\ceill_bug\.pio\libdeps\ut61e-wifi\PubSubClient)
|-- <Adafruit NeoPixel> 1.8.4 (C:\Users\Max\temp\ceill_bug\.pio\libdeps\ut61e-wifi\Adafruit NeoPixel)
|-- <ESP8266WiFi> 1.0 (C:\Users\Max\.platformio\packages\framework-arduinoespressif8266\libraries\ESP8266WiFi)
|-- <EspSoftwareSerial> 6.12.6 (C:\Users\Max\.platformio\packages\framework-arduinoespressif8266\libraries\SoftwareSerial)

and it still builds fine.

The only library which I cannot inspect is the

So I can only conclude that either:

  • my project doesn’t have the code required to trigger the bug (although I do have a call to ceill…)
  • the above library is causing the problem
  • your framework libraries are magically corrupted (remove /Users/peter/.platformio/packages/framework-arduinoespressif8266 and build again to get a fresh copy)
  • the toolchain on Mac behaves differently than on Windows (although we have the compiler version)

By now I can only help further when I either have the complete project code or at least the above display library.

@maxgerhardt I am happy to share the code if you’re happy to look at it!
It’s on GitHub - cabletie/210724-193948-d1_mini

It’s very much work in progress and please be kind I know there’s a lot to fix.

Also, I now have tested building it directly under Arduino and it works fine.

Alright, I also get the same compilation error and I’ve narrowed it down to this array in ut61e_display.cpp

function_dict_map_t  UT61E_DISP::DIAL_FUNCTION = {
    // (function, subfunction, unit)
    {0b0111011, {"voltage", RANGE_VOLTAGE, "V"}},
    {0b0111101, {"current", RANGE_CURRENT_AUTO_UA, "A"}}, //Auto μA Current / Auto μA Current / Auto 220.00A/2200.0A
    {0b0111111, {"current", RANGE_CURRENT_AUTO_MA, "A"}}, //Auto mA Current   Auto mA Current   Auto 22.000A/220.00A
    {0b0110000, {"current", RANGE_CURRENT_22A, "A"}}, //22 A current
    {0b0111001, {"current", RANGE_CURRENT_MANUAL, "A"}}, //Manual A Current
    {0b0110011, {"resistance", RANGE_RESISTANCE, "Ω"}},
    {0b0110101, {"continuity", RANGE_CONTINUITY, "Ω"}},
    {0b0110001, {"diode", RANGE_DIODE, "V"}},
    {0b0110010, {"frequency", RANGE_FREQUENCY, "Hz"}},
    {0b0110110, {"capacitance", RANGE_CAPACITANCE, "F"}},
    {0b0110100, {"temperature", RANGE_NULL, "deg"}},
    {0b0111110, {"ADP", RANGE_ADP, ""}}
};

if that is compiled out with #if 0..#endif the error does not appear, with it it does. I’ll why that is.

I’m curious - did you #if that out on a hunch? (experience?) :slight_smile:
It’s really quite awful - this code is ported (someone elses) from python and I wanted to get it working by replicating the functionality then try and simplify it - clean it up and make it smaller etc etc.
The unordered_map was the best I could find to implement the python dictionary…

BUT one step at a time!

No I started with #if 0 at the top of the library and in the sketch code removed all referenced to the library and it’s objects, then it compiled straight away. Then I slowly enabled parts of the library’s .cpp code again, moving the #if 0 more and more downwards, and enabling references to the objects again, until the compilation resulted in an error. The last thing I enabled then is the cause for the error.

I could recreate the problem in the most minimal way

[env:ut61e-wifi]
platform = espressif8266
board = d1_mini
framework = arduino

src\main.cpp:

#include <Arduino.h>
#include <math.h>
#include <tgmath.h>
#include <buggy_lib.h>

void setup() {
  long double res = (long double) Serial.parseFloat();
  res = ceill(res);
  Serial.print("Result: ");
  Serial.println((double) res);
  
  //make use of the global variable, otherwise we don't get that error.
  for(auto& x : RANGE_VOLTAGE) {
    Serial.println(x.first);
  }
}

void loop() {
}

lib\buggy_lib\buggy_lib.h:

#ifndef _BUGGY_LIB_H
#define _BUGGY_LIB_H

#include <cstdlib> // Needed for uint8_t
#include <string>
#include <unordered_map>
using std::unordered_map;
using std::string;

struct Range_Dict
{
		float value_multiplier;
		int dp_digit_position;
		string display_unit;
};

typedef unordered_map<uint8_t, Range_Dict> range_dict_map_t;

extern range_dict_map_t RANGE_VOLTAGE;

#endif /* _BUGGY_LIB_H */

lib\buggy_lib\buggy_lib.cpp:

#include "buggy_lib.h"

range_dict_map_t RANGE_VOLTAGE = {
    {0b0110000, {1e0, 4, "V"}},  //2.2000V
    {0b0110001, {1e0, 3, "V"}},  //22.000V
    {0b0110010, {1e0, 2, "V"}},  //220.00V
    {0b0110011, {1e0, 1, "V"}},  //2200.0V
    {0b0110100, {1e-3, 2,"mV"}}  //220.00mV
};

Results in the same

c:/users/max/.platformio/packages/toolchain-xtensa@2.100300.0/bin/../lib/gcc/xtensa-lx106-elf/10.3.0/../../../../xtensa-lx106-elf/bin/ld.exe: C:\Users\Max\.platformio\packages\framework-arduinoespressif8266\tools\sdk\lib\libstdc++.a(math_stubs_long_double.o): in function `ceill':
/workdir/repo/gcc-gnu/libstdc++-v3/src/c++98/math_stubs_long_double.cc:76: multiple definition of `ceill'; c:/users/max/.platformio/packages/toolchain-xtensa@2.100300.0/bin/../lib/gcc/xtensa-lx106-elf/10.3.0/../../../../xtensa-lx106-elf/lib\libm.a(lib_a-ceill.o):/workdir/repo/newlib/newlib/libm/common/ceill.c:38: first defined here
collect2.exe: error: ld returned 1 exit status

error.

I’ve also verified that this does indeed compile with the same library code and the Arduino IDE, using the same core version PlatformIO does (3.0.1, not the latest 3.0.2).

So now the problem is constricted to specifcally PlatformIO and a very small amount of code.

I’ll check whether copying the Arduino IDE’s compiler makes a difference, or whether I can spot a difference in the compiler commands between them.

An interesting thing is also that the error only occurrs when the call to ceill is there, otherwise it’s gone.
Changing the float to double in the struct definition of the library does not make a difference – I thought since the struct does 1e0 which is a double and then has to be downconverted to an int it would make a difference, but no.

So can we find out what is actually calling ceill given it’s not the code directly?
I am assuming that ceill is only being resolved by the linker because it’s actually called by something that gets used, not just because it’s in one of the .a libraries?

In libm, ceill has its own object file (lib_a-ceill.o) but in libstdc++.a it’s part of math_stubs_long_double.o.

I’m pretty sure I’m seeing the issue. Looking at Arduino IDE’s library link order

-lwpa2 -lstdc++ -lm -lc -lgcc

And PlatformIO

-lwpa2 -lm -lc -lgcc -llwip2-536-feat -lstdc++

Since the conflicting file is called math_stubs_long_double.cc these may be weak functions and thus depend on the link order to be substituted correctly. Let me test my assumptions by changing up the script that is responsible for that link order…

Yeah, that was it.

The Arduino IDE injects the library at a specific position in the linker order

compiler.c.elf.libs=-lhal -lphy -lpp -lnet80211 {build.lwip_lib} -lwpa -lcrypto -lmain -lwps -lbearssl -lespnow -lsmartconfig -lairkiss -lwpa2 {build.stdcpp_lib} -lm -lc -lgcc

While in the PlatformIO builder script they just append it to the end.

This causes libstdc++ to come after libm and cause this error.

You can locally find and open the file C:\Users\<user>\.platformio\packages\framework-arduinoespressif8266\tools

and starting at line 269, change the code to

if "PIO_FRAMEWORK_ARDUINO_ENABLE_EXCEPTIONS" in flatten_cppdefines:
    env.Append(
        CXXFLAGS=["-fexceptions"],
        #LIBS=["stdc++-exc"]
    )
    old_libs = env["LIBS"]
    old_libs.insert(len(env["LIBS"]) - 4, "stdc++-exc")
    env["LIBS"] = old_libs
else:
    env.Append(
        CXXFLAGS=["-fno-exceptions"],
#        LIBS=["stdc++"]
    )
    old_libs = env["LIBS"]
    old_libs.insert(len(env["LIBS"]) - 4, "stdc++")
    env["LIBS"] = old_libs

then recompile.

I will open an issue for this in the meantime.

1 Like

A fix is pending in Respect linking order of libraries by maxgerhardt · Pull Request #8263 · esp8266/Arduino · GitHub.

This will however take a while until it is merged and a new stable version is released – I recommend manual moidfications for now.

Hey @maxgerhardt you are brilliant.
Thank you again for this help it has saved me much pain! (And it looks like you found a bug to boot) nice work).

Thanks :slight_smile:

A small addendum after I looked over some code again:

actually I was wrong and the correct way to enable exceptions is per docs:

build_flags = -D PIO_FRAMEWORK_ARDUINO_ENABLE_EXCEPTIONS
; no build_unflags

This additionally triggers the build script (which was actually quoted above) to include the correct stdc++ library (libstdc++-exc.a instead of libstdc++.a), without that you might run into weird issues (like a catch() never being reached because it interally abort()s on every exception).

The

lib_extra_dirs = ~/Documents/Arduino/libraries

expression rely on the user having installed certain libraries in the Arduino IDE – if you want the sketch to be compilable stand-alone, remove that instruction and manage the dependencies purely using PlatformIO with lib_deps and the PlatformIO library registry.

lib_deps =
   knolleary/PubSubClient @ ^2.8
   adafruit/Adafruit NeoPixel @ 1.8.4

Also, the main source file is named UT61EWiFiD1Mini.ino. With .ino files, the IntelliSense in VSCode will be extremely limited and sometimes wrong. To get the intended experience, you should rename it to .cpp and fixup the function prototype declarations per FAQ. .cpp files still work in the Arduino IDE, but for Arduino IDE distribution you can also rename the file again to .ino, since every valid .cpp file is automatically a valid .ino file, but not the other way around.

Thanks again @maxgerhardt.
I have made those changes and it has cleaned things up a bit for me - through this and the reading I’ve now done I have a beter understanding also of the Arduino/.ino vs .cpp differences which is great.

One more thing - does the change to use
build_flags = -D PIO_FRAMEWORK_ARDUINO_ENABLE_EXCEPTIONS
fix the original problem or is the patch still required? I’m not sure I understood you 100% there.

You’ll still need the patch, with the original code, both the non-exception and exception code paths have the bug regarding the library linking order. Btw, I slightly redid the implementation of the patch in the PR to be better readable, see this file – but the patch posted above still works equivalently.