How to debug collect2 link error? (in Arduino_MKRIoTCarrier example sketch)

I’m using the sample sketch Custom_Sensitivity provided with the Arduino_MKRIoTCarrier library. When building, I’m getting collect2: error: ld returned 1 exit status

I’m struggling to pinpoint where the error is coming from or how to fix it. In the Arduino IDE this sample sketch compiles with no errors. I’m fairly new to PlatformIO so any suggestions or leads would be greatly appreciated.

My build output:

CONFIGURATION: https://docs.platformio.org/page/boards/atmelsam/mkrwifi1010.html
PLATFORM: Atmel SAM (6.2.0) > Arduino MKR WiFi 1010
HARDWARE: SAMD21G18A 48MHz, 32KB RAM, 256KB Flash
DEBUG: Current (atmel-ice) External (atmel-ice, blackmagic, jlink)
PACKAGES: 
 - framework-arduino-samd 1.8.11 
 - framework-cmsis 1.40500.0 (4.5.0) 
 - framework-cmsis-atmel 1.2.2 
 - toolchain-gccarmnoneeabi 1.70201.0 (7.2.1)
LDF: Library Dependency Finder -> http://bit.ly/configure-pio-ldf
LDF Modes: Finder ~ chain, Compatibility ~ soft
Found 25 compatible libraries
Scanning dependencies...
Dependency Graph
|-- <Arduino_MKRIoTCarrier> 1.0.1
|   |-- <Wire> 1.0
|   |-- <Arduino_BQ24195> 0.9.1
|   |   |-- <Wire> 1.0
|   |-- <Arduino_APDS9960> 1.0.3
|   |   |-- <Wire> 1.0
|   |-- <Arduino_LPS22HB> 1.0.1
|   |   |-- <Wire> 1.0
|   |-- <Arduino_LSM6DS3> 1.0.0
|   |   |-- <SPI> 1.0
|   |   |-- <Wire> 1.0
|   |-- <SPI> 1.0
|   |-- <Arduino_HTS221> 1.0.0
|   |   |-- <Wire> 1.0
|   |-- <Arduino_MCHPTouch> 1.1.0
|   |-- <SD> 0.0.0-alpha+sha.041f788250
|   |   |-- <SPI> 1.0
|   |-- <Adafruit GFX Library> 1.10.10
|   |   |-- <SPI> 1.0
|   |   |-- <Adafruit BusIO> 1.8.1
|   |   |   |-- <Wire> 1.0
|   |   |   |-- <SPI> 1.0
|   |   |-- <Wire> 1.0
|   |-- <Adafruit ST7735 and ST7789 Library> 1.7.3
|   |   |-- <Adafruit GFX Library> 1.10.10
|   |   |   |-- <SPI> 1.0
|   |   |   |-- <Adafruit BusIO> 1.8.1
|   |   |   |   |-- <Wire> 1.0
|   |   |   |   |-- <SPI> 1.0
|   |   |   |-- <Wire> 1.0
|   |   |-- <SPI> 1.0
|   |-- <Adafruit DotStar> 1.1.4
|   |   |-- <SPI> 1.0
|-- <Adafruit BusIO> 1.8.1
|   |-- <Wire> 1.0
|   |-- <SPI> 1.0
Building in release mode
Compiling .pio/build/mkrwifi1010/src/main.cpp.o
In file included from .pio/libdeps/mkrwifi1010/Arduino_MKRIoTCarrier/src/Arduino_MKRIoTCarrier.h:37:0,
                 from src/main.cpp:3:
.pio/libdeps/mkrwifi1010/Arduino_MKRIoTCarrier/src/Arduino_MKRIoTCarrier_Qtouch.h:27:13: warning: '_available' defined but not used [-Wunused-variable]
 static bool _available = false;
             ^~~~~~~~~~
Linking .pio/build/mkrwifi1010/firmware.elf
.pio/build/mkrwifi1010/lib339/libArduino_MCHPTouch.a(touch.c.o): In function `touch_sensors_init':
touch.c:(.text.touch_sensors_init+0x12): undefined reference to `touch_selfcap_sensors_init_with_rs_table'
touch.c:(.text.touch_sensors_init+0x38): undefined reference to `touch_selfcap_sensor_config'
touch.c:(.text.touch_sensors_init+0x56): undefined reference to `touch_selfcap_sensor_config'
touch.c:(.text.touch_sensors_init+0x74): undefined reference to `touch_selfcap_sensor_config'
touch.c:(.text.touch_sensors_init+0x92): undefined reference to `touch_selfcap_sensor_config'
touch.c:(.text.touch_sensors_init+0xb0): undefined reference to `touch_selfcap_sensor_config'
touch.c:(.text.touch_sensors_init+0xbc): undefined reference to `touch_selfcap_sensors_calibrate'
.pio/build/mkrwifi1010/lib339/libArduino_MCHPTouch.a(touch.c.o): In function `touch_sensors_measure':
touch.c:(.text.touch_sensors_measure+0x16): undefined reference to `touch_selfcap_sensors_measure'
collect2: error: ld returned 1 exit status
*** [.pio/build/mkrwifi1010/firmware.elf] Error 1
========================== [FAILED] Took 5.62 seconds =======================

From the build output I got the impression the error was coming from undefined reference to touch_selfcap_sensors_X, however, I opened that library and using VS Code’s Go to Definition I can consistently find the references to those functions. I also tried renaming the file to touch.cpp (as suggested in other discussions), but still got the same error.

My main.cpp:

#include <Arduino.h>
// Not in original example, added to satisfy dependency error
#include <Adafruit_I2CDevice.h>

#include "Arduino_MKRIoTCarrier.h"
MKRIoTCarrier carrier;

unsigned int threshold = 98;

void setup() {
  Serial.begin(9600);
  while (!Serial);

  carrier.begin();
  carrier.Buttons.updateConfig(threshold);
}

void loop() {
  carrier.Buttons.update();
  
  if (carrier.Buttons.getTouch(TOUCH0)) {
    Serial.println("touching 0");
  }
  if (carrier.Buttons.getTouch(TOUCH1)) {
    Serial.println("touching 1");
  }
}

And my platform.ini:

[env:mkrwifi1010]
platform = atmelsam
board = mkrwifi1010
framework = arduino
lib_deps = arduino-libraries/Arduino_MKRIoTCarrier@^1.0.1

If it helps at all, the “top-level” library Arduino_MKRIoTCarrier can be found in this GitHub Repo and the Arduino_MCHPTouch library which contains the file touch.c is from this GitHub repo. :pray:

1 Like

The missing functions are in the precompiled libsamd21_qt_gcc.a file.

The source code does not contain an implementation of these function, just the declarations of them, but it links them in via the .a file, which it also references here.

PlatformIO does not properly emulate that ldflags and precompiled behavior (I will open an issue for this), so one has to do it manually for now.

You should add

build_flags =
    -L"$PROJECT_LIBDEPS_DIR/$PIOENV/Arduino_MCHPTouch/src/cortex-m0plus"
    -lsamd21_qt_gcc

(docs) to the platformio.ini and retry.

1 Like

Issue is open in Process precompiled and ldflags properties of library.properties · Issue #3994 · platformio/platformio-core · GitHub btw.

Thank you very much @maxgerhardt for the fix, the reference to the docs, and the PR. Adding the build_flags produced a successful build. :tada:

For future reference, how did you identify that the missing functions were in the precompiled libsamd21_qt_gcc.a file? There is no mention of that file name anywhere in touch.c or in touch_api_SAMD.h?

Yes and there don’t needs to be a mention of those things in the code. The linking of arbitrary .a files, which can provide arbitrary functions, is a layer above that. I can reference a function abc() in a code file and link it in at the last stage via libsomefancyname.a without ever referencing that file name in the code.

The thing that makes it clear that that has to be in that libsamd21_qt_gcc.a file is

  • the functions are not implemented in any code in that libraries
  • function is only declared in a header of the local library (so the function is expected to come from someone within that library folder)
  • the library.properties clearly references the library file by linking with -lsamd21_qt_gcc, but PlatformIO doesn’t in its final linker command (Advanced → Verbose Build). This is a smoking gun.
  • the pure existance of the .a file screams that it wants to be linked in the project. At this point one doesn’t even have to look at the file to guess that that is the missing puzzle piece.
  • one can also just download and inspect the file itself. The nm tool of the ARM toolchain will list all symbols of the library file, for each object file it is composed of. (One can also nicely see all other internal functions). A snippet of the output shows…
>C:\Users\Max\.platformio\packages\toolchain-gccarmnoneeabi\bin\arm-none-eabi-nm libsamd21_qt_gcc.a

[...]
touch_api_selfcap_set1.o:
[..]
00000248 T touch_selfcap_sensor_config
000003b4 T touch_selfcap_sensor_get_delta
00000300 T touch_selfcap_sensors_calibrate
00000048 T touch_selfcap_sensors_init_with_rs_table
00000370 T touch_selfcap_sensors_measure

that those symbols (which are functions in ths case) which are reported as “undefined reference to…” when not linking in that .a file, are implemented right in that .a file, coming from the touch_api_selfcap_set1.o file.

Thanks for the clear explanation. Much appreciated.

As a reference my platformio.ini file

[env:mkrwifi1010]
platform = atmelsam
board = mkrwifi1010
framework = arduino
build_flags =
-L"$PROJECT_LIBDEPS_DIR/$PIOENV/Arduino_MCHPTouch/src/cortex-m0plus"
-lsamd21_qt_gcc
lib_deps =
arduino-libraries/WiFiNINA @ ^1.8.12
knolleary/PubSubClient @ ^2.8
bblanchon/ArduinoJson @ ^6.18.2
arduino-libraries/Arduino_MKRIoTCarrier@^1.0.2
adafruit/Adafruit BusIO@^1.8.3

I was getting these errors
mkrwifi1010\Adafruit GFX Library\Adafruit_GrayOLED.h:30:10: fatal error: Adafruit_I2CDevice.h: No such file or directory

ibf5a\Adafruit GFX Library\Adafruit_GrayOLED.cpp.o] Error 1
==================================== [FAILED]

This seems like a different kind of error and should probably be in it’s own thread for traceability. However, if you include your code I might be able to help.
I noticed that Adafruit_GrayOLED and Adafruit_I2CDevice are not in your lib_deps list. Have you tried explicitly installing those libraries?

Thank you very much, my code is working with the above ini file. Thanks for the offer.

Meanwhile, I am using Arduino MKR 1010 WIFI with Arduino MKR IoT Carrier

That’s great! Just in case I run into the same problem, how did you solve it?

I am using Visual Studio Code for coding Arduino with PlatformIO extension. I needed to include only the library Arduino_MKRIoTCarrier but including it did not fix the problem above pasted. And I end up in here adding the above build_flags and also adding Adafruit BusIO this library dependency fixed it. It’s all about settings, now I can use #include <Arduino_MKRIoTCarrier.h> without any problem. Thanks for the follow-up. Hope this helps.

1 Like