Undefined reference to `_write'

AlbertAssis

New member
Hello!
I'm trying to use a lib of a LCD module, which was writen in Cpp (https://www.waveshare.com/wiki/1.47inch_LCD_Module) .
While compiling it to Arduino Uno board it works fine, but using Teensy 3.2 board the Arduino IDE gives the error below:

c:/program files (x86)/arduino/hardware/tools/arm/bin/../lib/gcc/arm-none-eabi/5.4.1/../../../../arm-none-eabi/lib/armv7e-m\libc.a(lib_a-writer.o): In function `_write_r':
writer.c:(.text._write_r+0x12): undefined reference to `_write'

What could be the source of this error?

Thank you fo all in advance!
 
I'm trying to use a lib of a LCD module, which was writen in Cpp (https://www.waveshare.com/wiki/1.47inch_LCD_Module) .
While compiling it to Arduino Uno board it works fine, but using Teensy 3.2 board the Arduino IDE gives the error below:

c:/program files (x86)/arduino/hardware/tools/arm/bin/../lib/gcc/arm-none-eabi/5.4.1/../../../../arm-none-eabi/lib/armv7e-m\libc.a(lib_a-writer.o): In function `_write_r':
writer.c:(.text._write_r+0x12): undefined reference to `_write'

What could be the source of this error?

Hard to say, but probably the library you are using does not support Teensy.
 
Not sure what software you are using. Did not see anything in their .7z file that matched this display

But it looks like this display uses an ST7789 display chip, so you might try the ST7789_t3 (part of ST7735_t3) directory...
And see if it at all works.

We might need to add something to handle the case where it does not use the full buffers... But could be a starting point.
Ditto for the Adafruit library, might check to see if they have support

Actually Adafruit sells a display like this, so their driver should work now:
https://www.adafruit.com/product/5393
 
@Paul - Here is the link to "LcmZimo" that contains the driver for Arduino:
https://www.waveshare.com/wiki/1.47inch_LCD_Module

It's located at the bottom of the page and will start a download of 'LCD_Module_code.7z'.

I checked it out and found a printf in DeBug.h. Tried to compile for T3.2 and got the same compile error as stated above.
Recompiled for T4.0/T4.1 and the compile did not fail. I know that there is a weak definition to '_write()' in 'Print.cpp' for T4.x and T3.x.

As a test I added same:
Code:
extern "C" {
__attribute__((weak))
int _write(int file, char *ptr, int len)
{
	((class Print *)file)->write((uint8_t *)ptr, len);
	return len;
}
to the end of 'LCD_Driver.cpp'. It compiled without the error. Not sure why it's not seeing the 'Print.cpp' definition of '_write()' for Teensy3 core. Don't have that particular display so could not test compiled code.

EDIT: I tested the Arduino sub folder code.
 
Last edited:
I also stumbled over this weird thing some time ago. https://forum.pjrc.com/threads/65469-EncoderTool-Problems?p=264072&viewfull=1#post264072
It could be fixed by moving the definition of _write from 'print.cpp' to 'setup.c'. There even is a comment from Paul in setup.c stating:
Code:
// syscall functions need to be in the same C file as the entry point "ResetVector"
// otherwise the linker will discard them in some cases.

Somehow this never got fixed, maybe now?

I also ended up working around the issue by adding
Code:
#if defined(KINETISK)
extern "C" {
int _write(int file, char* ptr, int len)
{
    ((class Print*)file)->write((uint8_t*)ptr, len);
    return len;
}
}
#endif
to my libraries.

IIRC, another fix was to call Serial.print() (not printf) in setup which magically fixed it.
 
[...or `Serial.printf()`.]

Here's how to implement your own `_write()`. Note that the one defined by the Teensyduino core assumes all file descriptors are `Print*` pointers, which is why regular `printf()` to `stdout` doesn't work. Here's a version that will work to send to any `Print*` of your choosing, including to `&Serial` (i.e. just `Serial`, but the code works with pointers).

Code:
#include <unistd.h>
#include <cerrno>
#undef errno
extern int errno;
// ^^^^ Add these 4 lines to your includes

// Set this to something of your choosing.
Print *volatile stdPrint = &Serial;

extern "C" {
int _write(int file, const void *buf, size_t len) {
  if (len == 0) {
    return 0;
  }

  Print *out;

  // Send both stdout and stderr to stdPrint
  if (file == STDOUT_FILENO || file == STDERR_FILENO) {
    out = stdPrint;
  } else if (file == STDIN_FILENO) {
    errno = EBADF;
    return -1;
  } else {
    out = (Print *)file;
  }

  if (out == nullptr) {
    return len;
  }
  return out->write((const uint8_t *)buf, len);
}
}  // extern "C"

This is not defined as "weak", and so overrides Teensyduino's version because that version is defined as "weak".

This also works with `fprintf()` and any of the other file descriptor-having output functions. And yes, including for files, or anything else that implements the `Print` interface (or, heck, any interface you want (eg. `File`), not just `Print`), but I'll leave that as an exercise to the reader. It's also possible to implement other stdio and system functions related to the internals; think "newlib".
 
Note that when there’s two or more weak function definitions for the same function, you don’t know which the linker chooses. That’s why I made my example non-weak. It does two things: overrides Teensyduino’s weak version and provides the definition that gets used.

Note 1: If you use wwatson’s example, don’t include the “weak” line.

Note 2: You can put my example code anywhere, including in your main program file: there’s no need to alter the library. The linker will find it.
 
...
As a test I added same:
Code:
extern "C" {
__attribute__((weak))
int _write(int file, char *ptr, int len)
{
	((class Print *)file)->write((uint8_t *)ptr, len);
	return len;
}
to the end of 'LCD_Driver.cpp'. It compiled without the error. Not sure why it's not seeing the 'Print.cpp' definition of '_write()' for Teensy3 core. Don't have that particular display so could not test compiled code.
...

Dear wwatson, appending your code to the end of 'LCD_Driver.cpp' it worked fine.
I have compiled without error and transfered to T3.2. It is working well!
Thank you so much
 
When adding your own _write() definition, don’t include the “weak” line because otherwise you don’t really know which gets chosen by the linker, yours or Teensyduino’s.

[Repeated here because my original note was an edit and may not have gotten picked up.]
 
Last edited:
Note that when there’s two or more weak function definitions for the same function, you don’t know which the linker chooses. That’s why I made my example non-weak. It does two things: overrides Teensyduino’s weak version and provides the definition that gets used.
...
Note 2: You can put my example code anywhere, including in your main program file: there’s no need to alter the library. The linker will find it.

Dear shawn,
I have inserted your solution to my code and it worked fine!
I really appreciate your support!
Kind regards
 
I've added the check for stdout, stderr so printf() can "just work".

https://github.com/PaulStoffregen/cores/commit/0bdfd22c52099831e72983c3237ad183ce51e02f

However, the "undefined reference to _write" is still an issue on Teensy 3.2 if the program has no usage of any Print class members. For example:

Code:
void setup() {
  Serial.begin(9600);
  //Serial.println("setup begin"); // won't compile on Teensy 3.x without this line
  printf("Hello World\n");
}

void loop() {
}
 
However, the "undefined reference to _write" is still an issue on Teensy 3.2 if the program has no usage of any Print class members.

Last time I checked, I could fix it by moving the definiton of _write from print.cpp to mk20dx128.c. See here for details: https://forum.pjrc.com/threads/65469-EncoderTool-Problems?p=264073&viewfull=1#post264073

In the T4 core you even have a comment (startup.c) which states:
// syscall functions need to be in the same C file as the entry point "ResetVector"
// otherwise the linker will discard them in some cases
 
Yes, we can be sure the linker will not ignore the compile unit with the reset vector. But moving code that requires .cpp compilation to a .c file isn't really an option.

Substantially changing how we build for Teensy 3.x at this point (12 years after the release of Teensy 3.0 and as the Teensy 3.x products are well into the "mature" phase of their life cycle) would only make sense for a really serious issue.
 
Back
Top