Enabling RTTI (-frtti) causes undefined reference to `__dynamic_cast'

Hello I’m working on a program (or a redesign of a program) and I was trying to improve my c++ while doing so, eventually I ran into a situation where using dynamic casting would be ideal, to cut it short here’s a minimal reproducible example:

class Base{
public:
	void virtual Function_To_Force_Class_To_Be_Polymorphic(void);
	char cBase;
};

class Derived1 : public Base{
public:
	char cDerived1;
};

class Derived2 : public Base{
public:
	char cDerived2_1;
	char cDerived2_2;
};

void Function(Base *baseClassPointerArgument){
	Derived1 *baseClassPointerTemporal = 0x0000;
	baseClassPointerTemporal = dynamic_cast<Derived1 *> (baseClassPointerArgument);
	if( baseClassPointerTemporal != 0x0000){
		Serial.println("class is of type Derived1");
		Serial.println( baseClassPointerTemporal->cBase );
		Serial.println( baseClassPointerTemporal->cDerived1 );
	}
	else{
		Serial.println("class is not of type Derived1");
		Serial.println( baseClassPointerArgument->cBase );
	}
};

//------------------------------------------------------------------------------------------------------------------------
void setup(){
	Derived1 d1;
	d1.cBase = 'a';
	d1.cDerived1 = 'b';
	Function(&d1);
}

generates the following errors:

C:\Users\Santiago\AppData\Local\Temp\ccpnyaCO.ltrans0.ltrans.o: In function main': <artificial>:(.text.startup+0x1ba): undefined reference to typeinfo for Base’
:(.text.startup+0x1bc): undefined reference to typeinfo for Base' <artificial>:(.text.startup+0x1c2): undefined reference to __dynamic_cast’
C:\Users\Santiago\AppData\Local\Temp\ccpnyaCO.ltrans0.ltrans.o:(.rodata+0x63): undefined reference to vtable for __cxxabiv1::__si_class_type_info' C:\Users\Santiago\AppData\Local\Temp\ccpnyaCO.ltrans0.ltrans.o:(.rodata+0x69): undefined reference to vtable for __cxxabiv1::__class_type_info’
C:\Users\Santiago\AppData\Local\Temp\ccpnyaCO.ltrans0.ltrans.o:(.rodata+0x6d): undefined reference to vtable for __cxxabiv1::__si_class_type_info' C:\Users\Santiago\AppData\Local\Temp\ccpnyaCO.ltrans0.ltrans.o:(.rodata+0x73): undefined reference to vtable for __cxxabiv1::__class_type_info’
C:\Users\Santiago\AppData\Local\Temp\ccpnyaCO.ltrans0.ltrans.o:(.rodata+0x77): undefined reference to vtable for __cxxabiv1::__si_class_type_info' C:\Users\Santiago\AppData\Local\Temp\ccpnyaCO.ltrans0.ltrans.o:(.rodata+0x7d): undefined reference to vtable for __cxxabiv1::__si_class_type_info’
C:\Users\Santiago\AppData\Local\Temp\ccpnyaCO.ltrans0.ltrans.o:(.rodata+0x83): undefined reference to vtable for __cxxabiv1::__si_class_type_info' C:\Users\Santiago\AppData\Local\Temp\ccpnyaCO.ltrans0.ltrans.o:(.rodata+0x87): undefined reference to typeinfo for Base’
C:\Users\Santiago\AppData\Local\Temp\ccpnyaCO.ltrans0.ltrans.o:(.rodata+0x8d): undefined reference to `Base::Function_To_Force_Class_To_Be_Polymorphic()’

platformIO.ini:

[env:uno]
platform = atmelavr
board = megaatmega2560
framework = arduino
upload_port = COM7

build_flags =
	-I/Users/Santiago/Documents/Projects/pulse_commander/headers
	
	-Wno-reorder		; To ignore warnings about reordering on list initializers.
	-Wno-pointer-arith	; To ignore warnings about casting void pointers, used on a couple of subroutines.
	-Werror=parentheses	; I can't remember why this is here.
	-Winline			; To force the compiler to warn when a function marked inline has not been inlined.
	-frtti				; To allow dynamic casting

This code doesn’t compile normally on a desktop G++ too though?

#include <iostream>
using namespace std;

class Base{
public:
	void virtual Function_To_Force_Class_To_Be_Polymorphic(void);
	char cBase;
};

class Derived1 : public Base{
public:
	char cDerived1;
};

class Derived2 : public Base{
public:
	char cDerived2_1;
	char cDerived2_2;
};

void Function(Base *baseClassPointerArgument){
	Derived1 *baseClassPointerTemporal = 0x0000;
	baseClassPointerTemporal = dynamic_cast<Derived1 *> (baseClassPointerArgument);
	if( baseClassPointerTemporal != 0x0000){
		printf("class is of type Derived1\n");
		printf("%c\n", baseClassPointerTemporal->cBase );
		printf("%c\n", baseClassPointerTemporal->cDerived1 );
	}
	else{
		printf("class is not of type Derived1\n");
		printf("%c\n", baseClassPointerArgument->cBase );
	}
};

int main()
{
	Derived1 d1;
	d1.cBase = 'a';
	d1.cDerived1 = 'b';
	Function(&d1);

    return 0;
}

and

C:\Users\Max>g++ -o poly -frtti poly.cpp
C:\Users\Max\AppData\Local\Temp\cciI7SQq.o:poly.cpp:(.rdata$_ZTV8Derived1[_ZTV8Derived1]+0x10): undefined reference to `Base::Function_To_Force_Class_To_Be_Polymorphic()'
C:\Users\Max\AppData\Local\Temp\cciI7SQq.o:poly.cpp:(.rdata$.refptr._ZTV4Base[.refptr._ZTV4Base]+0x0): undefined reference to `vtable for Base'
collect2.exe: error: ld returned 1 exit status

When I change the virtual function to have a default implementation

	void virtual Function_To_Force_Class_To_Be_Polymorphic(void) { }

it compiles and executes

C:\Users\Max>g++ -o poly -frtti poly.cpp

C:\Users\Max>g++ -o poly -frtti poly.cpp && poly.exe
class is of type Derived1
a
b

but it does not compile for AVR, even with the latest toolchain available in PlatformIO (AVR-GCC 7.3.0 instead of a 5.4.0)

platform_packages = 
    toolchain-atmelavr@~2.70300.0
Linking .pio\build\uno\firmware.elf
C:\Users\Max\AppData\Local\Temp\ccQ3NED0.ltrans0.ltrans.o: In function `main':
<artificial>:(.text.startup+0x10c): undefined reference to `__dynamic_cast'
C:\Users\Max\AppData\Local\Temp\ccQ3NED0.ltrans0.ltrans.o:(.rodata+0x7): undefined reference to `vtable for __cxxabiv1::__class_type_info'
C:\Users\Max\AppData\Local\Temp\ccQ3NED0.ltrans0.ltrans.o:(.rodata+0x13): undefined reference to `vtable for __cxxabiv1::__si_class_type_info'
C:\Users\Max\AppData\Local\Temp\ccQ3NED0.ltrans0.ltrans.o:(.rodata+0x2a): undefined reference to `vtable for __cxxabiv1::__si_class_type_info'
C:\Users\Max\AppData\Local\Temp\ccQ3NED0.ltrans0.ltrans.o:(.rodata+0x52): undefined reference to `vtable for __cxxabiv1::__si_class_type_info'
C:\Users\Max\AppData\Local\Temp\ccQ3NED0.ltrans0.ltrans.o:(.rodata+0x58): undefined reference to `vtable for __cxxabiv1::__class_type_info'
collect2.exe: error: ld returned 1 exit status

…but if I compile the code for ESP8266 with the Xtensa-LX106 toolchain

[env:esp8266]
platform = espressif8266
board = nodemcuv2
framework = arduino
build_flags =
    -frtti

Checking size .pio\build\esp8266\firmware.elf
Building .pio\build\esp8266\firmware.bin
Advanced Memory Usage is available via "PlatformIO Home > Project Inspect"
RAM:   [===       ]  33.0% (used 27068 bytes from 81920 bytes)
Flash: [===       ]  25.1% (used 261796 bytes from 1044464 bytes)
Creating BIN file ".pio\build\esp8266\firmware.bin" using "C:\Users\Max\.platformio\packages\framework-arduinoespressif8266\bootloaders\eboot\eboot.elf" and ".pio\build\esp8266\firmware.elf"

it compiles. So it’s obviously a problem of the toolchain.

AVR-GCC is known for lacking support for many C++ features and the STL.

I’ll also try with a AVR-GCC 10 version from AVR-GCC 12.1.0 for Windows 32 and 64 bit | Zak’s Electronics Blog ~* but I don’t have much hope.

Nope, fails too, with a really recent AVR-GCC 10.1.0 toolchain

Linking .pio\build\uno\firmware.elf
c:/users/max/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/10.1.0/../../../../avr/bin/ld.exe: C:\Users\Max\AppData\Local\Temp\firmware.elf.uMl3AP.ltrans0.ltrans.o: in function `main':
<artificial>:(.text.startup+0x162): undefined reference to `__dynamic_cast'
c:/users/max/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/10.1.0/../../../../avr/bin/ld.exe: C:\Users\Max\AppData\Local\Temp\firmware.elf.uMl3AP.ltrans0.ltrans.o:(.rodata+0x17): undefined reference to `_ZTVN10__cxxabiv117__class_type_infoE'
c:/users/max/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/10.1.0/../../../../avr/bin/ld.exe: C:\Users\Max\AppData\Local\Temp\firmware.elf.uMl3AP.ltrans0.ltrans.o:(.rodata+0x23): undefined reference to `_ZTVN10__cxxabiv120__si_class_type_infoE'
c:/users/max/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/10.1.0/../../../../avr/bin/ld.exe: C:\Users\Max\AppData\Local\Temp\firmware.elf.uMl3AP.ltrans0.ltrans.o:(.rodata+0x3a): undefined reference to `_ZTVN10__cxxabiv120__si_class_type_infoE'
c:/users/max/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/10.1.0/../../../../avr/bin/ld.exe: C:\Users\Max\AppData\Local\Temp\firmware.elf.uMl3AP.ltrans0.ltrans.o:(.rodata+0x40): undefined reference to `_ZTVN10__cxxabiv120__si_class_type_infoE'
c:/users/max/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/10.1.0/../../../../avr/bin/ld.exe: C:\Users\Max\AppData\Local\Temp\firmware.elf.uMl3AP.ltrans0.ltrans.o:(.rodata+0x46): undefined reference to `_ZTVN10__cxxabiv117__class_type_infoE'
collect2.exe: error: ld returned 1 exit status

Please file a bug in https://gcc.gnu.org/bugzilla/buglist.cgi?component=target&product=gcc&resolution=---.

This is a AVR-GCC issue.

1 Like

I was afraid it would be something like that, i suppose for the moment I’ll go around the issue, thanks for the help.

Though why should I file a bug with gcc if it’s avr specific? Shouldn’t I contact atmel for this?

I’ve also seen AVR target bugs being filed there, since it is GCC with the AVR specific extensions but still still largely GCC territory.

This “no good STL / C++” support issue has been around since forever though. But maybe change will come…

1 Like