RTTI
Run Time Type Information is also one of the core features of conventional C++. It allows retrieval of the object type information (using typeid operator) as well as checking the inheritance hierarchy (using dynamic_cast) at run time. The RTTI is available only when there is a polymorphic behaviour, i.e. the classes have at least one virtual function.
Let's try to analyse the generated code when RTTI is in use. The test_cpp_rtti application in embxx_on_rpi project contains the code listed below.
struct SomeClass
{
virtual void someFunc();
};
Somewhere in *.cpp file:
void SomeClass::someFunc()
{
}
Somewhere in main
function:
SomeClass someClass;
someClass.someFunc();
Let's open the listing file and see what's going on in there. The address of SomeClass::someFunc()
seems to be 0x8300
:
00008300 <_ZN9SomeClass8someFuncEv>:
8300: e12fff1e bx lr
The virtual table for SomeClass
class must be somewhere in .rodata
section and contain address of SomeClass::someFunc()
, i.e. it must have 0x8300 value inside:
Disassembly of section .rodata:
...
00009c10 <_ZTV9SomeClass>:
9c10: 00000000 andeq r0, r0, r0
9c14: 00009c04 andeq r9, r0, r4, lsl #24
9c18: 00008300 andeq r8, r0, r0, lsl #6
9c1c: 00000000 andeq r0, r0, r0
It is visible that compiler added some more entries to the virtual table in addition to the single virtual function we implemented. The address 0x9c04 is also located in .rodata
section. It is some type related table:
00009c04 <_ZTI9SomeClass>:
9c04: 00009c28 andeq r9, r0, r8, lsr #24
9c08: 00009bf8 strdeq r9, [r0], -r8
9c0c: 00000000 andeq r0, r0, r0
Both 0x9c28 and 0x9bf8 are addresses in .rodata*
section(s). The 0x9bf8 address seems to contain some data:
00009bf8 <_ZTS9SomeClass>:
9bf8: 6d6f5339 stclvs 3, cr5, [pc, #-228]! ; 9b1c <strcmp+0x180>
9bfc: 616c4365 cmnvs ip, r5, ror #6
9c00: 00007373 andeq r7, r0, r3, ror r3
After a closer look we may decode this data to be "9SomeClass" ascii string.
Address 0x9c28 is in the middle of some type related information table:
00009c20 <_ZTVN10__cxxabiv117__class_type_infoE>:
9c20: 00000000 andeq r0, r0, r0
9c24: 00009c50 andeq r9, r0, r0, asr ip
9c28: 00009dc0 andeq r9, r0, r0, asr #27
9c2c: 00009de4 andeq r9, r0, r4, ror #27
9c30: 0000a114 andeq sl, r0, r4, lsl r1
9c34: 0000a11c andeq sl, r0, ip, lsl r1
9c38: 00009e40 andeq r9, r0, r0, asr #28
9c3c: 00009d48 andeq r9, r0, r8, asr #26
9c40: 00009e10 andeq r9, r0, r0, lsl lr
9c44: 00009e94 muleq r0, r4, lr
9c48: 00009dac andeq r9, r0, ip, lsr #27
9c4c: 00000000 andeq r0, r0, r0
How these tables are used by the compiler is of little interest to us. What is interesting is a code size overhead. Lets check the size of the binary image. With my compiler it is a bit more than 13KB.
For some bare metal platforms it may be undesirable or even impossible to have this amount of extra binary code added to the binary image. The GNU compiler (gcc
) provides an ability to disable RTTI by using -no-rtti
option. Let's check the virtual table of SomeClass
class when this option is used:
Disassembly of section .rodata:
00008320 <_ZTV9SomeClass>:
...
8328: 00008300 andeq r8, r0, r0, lsl #6
832c: 00000000 andeq r0, r0, r0
The virtual table looks much simpler now with single pointer to the SomeClass::someFunc()
virtual function. There is no extra code size overhead needed to maintain type information. If the application above is compiled without exceptions (using -fno-exceptions
and -fno-unwind-tables
) as well as without RTTI support (using -no-rtti
) the binary image size will be about 1.3KB which is much better.
However, if -no-rtti
option is used, the compiler won't allow usage of typeid operator as well as dynamic_cast. In this case the developer needs to come up with other solutions to differentiate between objects of different types (but having the same 'ancestor') at run time. There are multiple idioms that can be used, such as using simple C-like approach of switch
-ing on some type enumerator member, or using polymorphic behaviour of the objects to perform double dispatch.
CONCLUSION: Disabling Run Time Type Information (RTTI) in addition to eliminating exception handling is very common in bare metal C++ development. It allows to save about 10KB of space overhead in final binary image.
Last updated
Was this helpful?