Arduino 2.3.4 Teensy3.5 Teensyduino 1.57.3: build ok Teensyduino >= 1.58.2: (.text._write_r+0x14): undefined reference to `_write'

phijun

Member
Arduino 2.3.4 Teensy3.5. My project doesn't build any longer starting Teensyduino 1.58.2.

Teensyduino 1.57.3: builds
Teensyduino 1.58.2: (.text._write_r+0x14): undefined reference to `_write'

It builds with 1.59.0 for Teensy 4.0 but same error for 3.5.

Huh ?
 
There is not much anyone can do to help you unless you show the code that is not compiling. You can look at the announcements for TeensyDuino 1.58 and 1.59 to see what has changed. Some of them are significant, such as new versions of GCC, etc.
 
Here is the winner: snprintf

#include <stdio.h>
void setup() {
static char badaboum[64];
(void)snprintf(badaboum, 32, "%03d%03d", 10, 20);
}
void loop() {
}


Teensy 3.5 Teensyduino 1.57.3: builds
Teensy 3.5 Teensyduino 1.59.0: (.text._write_r+0x14): undefined reference to `_write'
Teensy 4.0 Teensyduino 1.59.0:builds
 
We do have the _write() function for Teensy 4.x in Print.cpp at line 93 and for Teensy 3.x in Print.cpp at line 87.

I don't know why it's getting used on Teensy 4 but gives this error on Teensy 3. Maybe someone better with C++ might have some idea?

As an immediate workaround, you could just copy that function into your program.

Code:
#include <stdio.h>
void setup() {
  static char badaboum[64];
  (void)snprintf(badaboum, 32, "%03d%03d", 10, 20);
}
void loop() {
}

extern "C" {
__attribute__((weak))
int _write(int file, char *ptr, int len)
{
        if (file >= 0 && file <= 2) file = (int)&Serial;
        return ((class Print *)file)->write((uint8_t *)ptr, len);
}
}
 
Maybe this error is a case of the linker trying to be too smart about deleting unused stuff? For example, just adding any use of Serial.printf() makes this program compile on Teensy 3.5.

Code:
void setup() {
  Serial.printf("test");  // delete this line for compile error on Teensy 3.5
  static char badaboum[64];
  (void)snprintf(badaboum, 32, "%03d%03d", 10, 20);
}
void loop() {
}

On Teensy 3.x we put much more effort into optimizing for small code size, because Teensy LC had only 62K flash and Teensy 3.0 had only 128K. For Teensy 4.x much less work went into small code size, since we have 2MB.

My best guess is we have something inside the core library for Teensy 4.x that always references code that causes the linker to know it has to include this _write() function. And before the toolchain upgrade of 1.58 (update gcc 5.4 to 11.3), the older linker probably wasn't as aggressive about removing "unused" code.
 
Just to be realistic, all Teensy 3.x products are discontinued. Unless someone else comes up with a good fix, the long-term solution may be to simply use version 1.57.

But whether a good fix is even possible is uncertain. If code is added that forces more stuff to be compiled into all programs, fixing this seldom encountered error might come at the cost of extra code size that makes fewer programs feasible on Teensy LC.

To be clear, I'm not planning to put more time into this specific issue. But if someone suggests a fix, I'll certainly consider it. Main factor will be whether it balloons code size on Teensy LC.
 
Last edited:
I don't understand why snprintf would be referencing _write, it doesn't output anything to a file descriptor.
 
Pretty sure it's not abort() pulling in _write(). Tried this and still get the error on Teensy 3.5.

Code:
void setup() {
  static char badaboum[64];
  (void)snprintf(badaboum, 32, "%03d%03d", 10, 20);
}
void loop() {
}

extern "C" {
__attribute__((weak))
void abort(void)
{
        while (1) asm ("WFI");
}
}
 
No hardware needed to reproduce the problem, as it's a compile error. Just click Verify in Arduino IDE.

I tried looking at objdump disassembly (on one of the cases which does compile). Looks like _write() gets called from _write_r(). Also looks like __swrite() is the only thing calling _write_r(). But I can't find anything in the assembly dump that's calling or referencing __swrite(). No idea why the linker is putting that in.

Very mysterious...
 
_write_r() is the re-entrant version of _write(). I have never seen the _swrite() that I can remember. I'm curious and will try it out...
 
If I add:

Code:
  Serial.printf("%s",badaboum);

in setup() It compiles? Did not try print() or println()...

Edit: Also compiles using smallest code...
 
Last edited:
Back
Top