Compiler/linker fail - virtual destructors

I’m having trouble getting some simple code to compile or link - I get different behavior with the online IDE compared to building on the command line. (I’m actually using NetBeans, configured to use the existing makefile. I managed to build the default tinker application no problem.)

Here’s the app code:

class A 
{
public:
    virtual int f()=0;
    virtual ~A() {}
};

class B : A
{
public:    
    virtual int f() { return 1; }    

};

B b;

void setup()  {}

void loop()    {         }

When I compile this on the command line, I get these errors:

Building target: core-firmware.elf
Invoking: ARM GCC C++ Linker
r:/sdk/spark/tools/gcc/bin/arm-none-eabi-g++ -g3 -gdwarf-2 -Os -mcpu=cortex-m3 -mthumb  -I../inc -I../../core-common-lib/CMSIS/Include -I../../core-common-lib/CMSIS/Device/ST/STM32F10x/Include -I../../core-common-lib/STM32F10x_StdPeriph_Driver/inc -I../../core-common-lib/STM32_USB-FS-Device_Driver/inc -I../../core-common-lib/CC3000_Host_Driver -I../../core-common-lib/SPARK_Firmware_Driver/inc -I../../core-communication-lib/lib/tropicssl/include -I../../core-communication-lib/src -I. -ffunction-sections -Wall -fmessage-length=0 -MD -MP -MF core-firmware.elf.d -DUSE_STDPERIPH_DRIVER -DSTM32F10X_MD -DDFU_BUILD_ENABLE  obj/src/application.o obj/src/main.o obj/src/newlib_stubs.o obj/src/spark_utilities.o obj/src/spark_wiring.o obj/src/spark_wiring_i2c.o obj/src/spark_wiring_interrupts.o obj/src/spark_wiring_ipaddress.o obj/src/spark_wiring_network.o obj/src/spark_wiring_print.o obj/src/spark_wiring_servo.o obj/src/spark_wiring_spi.o obj/src/spark_wiring_stream.o obj/src/spark_wiring_string.o obj/src/spark_wiring_tcpclient.o obj/src/spark_wiring_tcpserver.o obj/src/spark_wiring_udp.o obj/src/spark_wiring_usartserial.o obj/src/spark_wiring_usbserial.o obj/src/spark_wlan.o obj/src/stm32_it.o obj/src/usb_desc.o obj/src/usb_endp.o obj/src/usb_istr.o obj/src/usb_prop.o obj/src/wifi_credentials_reader.o obj/startup/startup_stm32f10x_md.o --output core-firmware.elf -T../linker/linker_stm32f10x_md_dfu.ld -nostartfiles -Xlinker --gc-sections  -L../../core-common-lib/build -lcore-common-lib -L../../core-communication-lib/build -lcore-communication-lib -Wl,-Map,core-firmware.map
r:/sdk/spark/tools/gcc/bin/../lib/gcc/arm-none-eabi/4.8.3/../../../../arm-none-eabi/bin/ld.exe: core-firmware.elf section `.text' will not fit in region `FLASH'
r:/sdk/spark/tools/gcc/bin/../lib/gcc/arm-none-eabi/4.8.3/../../../../arm-none-eabi/bin/ld.exe: region `FLASH' overflowed by 34296 bytes
r:/sdk/spark/tools/gcc/bin/../lib/gcc/arm-none-eabi/4.8.3/../../../../arm-none-eabi/lib/armv7-m\libg.a(lib_a-fstatr.o): In function `_fstat_r':
fstatr.c:(.text._fstat_r+0xe): undefined reference to `_fstat'
r:/sdk/spark/tools/gcc/bin/../lib/gcc/arm-none-eabi/4.8.3/../../../../arm-none-eabi/lib/armv7-m\libg.a(lib_a-writer.o): In function `_write_r':
writer.c:(.text._write_r+0x10): undefined reference to `_write'
r:/sdk/spark/tools/gcc/bin/../lib/gcc/arm-none-eabi/4.8.3/../../../../arm-none-eabi/lib/armv7-m\libg.a(lib_a-closer.o): In function `_close_r':
closer.c:(.text._close_r+0xc): undefined reference to `_close'
r:/sdk/spark/tools/gcc/bin/../lib/gcc/arm-none-eabi/4.8.3/../../../../arm-none-eabi/lib/armv7-m\libg.a(lib_a-isattyr.o): In function `_isatty_r':
isattyr.c:(.text._isatty_r+0xc): undefined reference to `_isatty'
r:/sdk/spark/tools/gcc/bin/../lib/gcc/arm-none-eabi/4.8.3/../../../../arm-none-eabi/lib/armv7-m\libg.a(lib_a-lseekr.o): In function `_lseek_r':
lseekr.c:(.text._lseek_r+0x10): undefined reference to `_lseek'
r:/sdk/spark/tools/gcc/bin/../lib/gcc/arm-none-eabi/4.8.3/../../../../arm-none-eabi/lib/armv7-m\libg.a(lib_a-readr.o): In function `_read_r':
readr.c:(.text._read_r+0x10): undefined reference to `_read'
collect2.exe: error: ld returned 1 exit status
make: *** [core-firmware.elf] Error 1

In short

  • the .text section won’t fit into flash - some 34K too large
  • and lots of undefined stream functions (lseek, read, etc…)

I can make the error disappear by commenting out the virtual destructor,

//        virtual ~A() {}

When I paste the same code in the online IDE and hit “verify”, I get

../786c930e31b1b7ebd0009388e661faed7df3ac721ec8fcb9d9b5f2de4ee1/the_user_app.cpp:3:15: error: 'virtual' outside class declaration
make: *** [../786c930e31b1b7ebd0009388e661faed7df3ac721ec8fcb9d9b5f2de4ee1/the_user_app.o] Error 1

I hit this problem when trying to port the Arduino-based fermentation controller brewpi onto the spark. I got the same error, because we use virtual descructors and pure virtual methods in a polymorphic class hierarchy. The arduino code compiles down to 25K, so I was surprised when the linker complained the code was 130K too big to fit into flash!

It looks like a bug to me - can anyone else reproduce this error?

My ARM gcc version is

arm-none-eabi-gcc (GNU Tools for ARM Embedded Processors) 4.8.3 20131129 (release) [ARM/embedded-4_8-branch revision 205641]

Thanks!
mat.

1 Like

Hi @mdma,

Thank you for posting your findings, that does look like it could be a bug. I’ll look into this and post my findings back in this thread when it’s fixed.

Thanks,
David

I think this may have something to do with the __cxa_pure_virtual() function. The default definition is to call writestr(“a pure virtual function was called”), which maps to fputs() to stderr. I think that explains where all the extra stream functions come from.

Adding

extern "C" void __cxa_pure_virtual() { while (1); }

to application.cpp fixes the linker problem, but not the compiler issue in the online IDE. (I’m not using the Online IDE, so that’s less pressing issue for me.)

I think the Online IDE is a syntax issue - while the command line build is a linker issue, linking in the standard implementation of __cxa_pure_virtual(). The firmware should provide it’s own implementation of that function, e.g. one that sets the LED to a specific color.

1 Like

This is exactly right. I was submitting a pull request to resolve this but you beat me to the post on the forum!

1 Like

Don’t let me put you off the pull request! I’m sure the team would be glad to have this issue resolved properly, rather than my one line hack in user code.

1 Like