From 596f66fd0d3bccfb067ced43a5169bf433b5e19f Mon Sep 17 00:00:00 2001 From: Alexander Mandera <alexander@mandera.eu> Date: Sun, 21 May 2023 15:45:41 +0200 Subject: [PATCH] Fix error when using C++ virtual methods --- .gitignore | 1 + ch32v003fun/ch32v003fun.c | 36 +++++++++++++++++++ examples/cpp_virtual_methods/Makefile | 26 ++++++++++++++ examples/cpp_virtual_methods/README.md | 29 +++++++++++++++ .../cpp_virtual_methods.cpp | 30 ++++++++++++++++ examples/cpp_virtual_methods/example.cpp | 30 ++++++++++++++++ examples/cpp_virtual_methods/example.h | 25 +++++++++++++ 7 files changed, 177 insertions(+) create mode 100644 examples/cpp_virtual_methods/Makefile create mode 100644 examples/cpp_virtual_methods/README.md create mode 100644 examples/cpp_virtual_methods/cpp_virtual_methods.cpp create mode 100644 examples/cpp_virtual_methods/example.cpp create mode 100644 examples/cpp_virtual_methods/example.h diff --git a/.gitignore b/.gitignore index b562640..12fb2f8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ .pio .vscode +.idea *.elf *.hex *.lst diff --git a/ch32v003fun/ch32v003fun.c b/ch32v003fun/ch32v003fun.c index 056f3cd..98d6bac 100644 --- a/ch32v003fun/ch32v003fun.c +++ b/ch32v003fun/ch32v003fun.c @@ -660,6 +660,10 @@ mini_pprintf(int (*puts)(char*s, int len, void* buf), void* buf, const char *fmt Copyright 2023 Charles Lohr, under the MIT-x11 or NewBSD licenses, you choose. */ +#ifdef CPLUSPLUS +// Method to call the C++ constructors +void __libc_init_array(void); +#endif int main() __attribute__((used)); void SystemInit( void ) __attribute__((used)); @@ -802,6 +806,12 @@ asm volatile( bne a1, a2, 1b\n\ 2:\n" ); +#ifdef CPLUSPLUS + asm volatile( + "call %0 \n\t" // Call __libc_init_array function + : : "i" (__libc_init_array) : ); +#endif + SETUP_SYSTICK_HCLK // set mepc to be main as the root app. @@ -973,3 +983,29 @@ void DelaySysTick( uint32_t n ) while( ((int32_t)( SysTick->CNT - targend )) < 0 ); } +// C++ Support + +#ifdef CPLUSPLUS +// This is required to allow pure virtual functions to be defined. +extern void __cxa_pure_virtual() { while (1); } + +// These magic symbols are provided by the linker. +extern void (*__preinit_array_start[]) (void) __attribute__((weak)); +extern void (*__preinit_array_end[]) (void) __attribute__((weak)); +extern void (*__init_array_start[]) (void) __attribute__((weak)); +extern void (*__init_array_end[]) (void) __attribute__((weak)); + +void __libc_init_array(void) +{ + size_t count; + size_t i; + + count = __preinit_array_end - __preinit_array_start; + for (i = 0; i < count; i++) + __preinit_array_start[i](); + + count = __init_array_end - __init_array_start; + for (i = 0; i < count; i++) + __init_array_start[i](); +} +#endif \ No newline at end of file diff --git a/examples/cpp_virtual_methods/Makefile b/examples/cpp_virtual_methods/Makefile new file mode 100644 index 0000000..749b3b5 --- /dev/null +++ b/examples/cpp_virtual_methods/Makefile @@ -0,0 +1,26 @@ +all : flash + +TARGET:=cpp_virtual_methods +TARGET_EXT:=cpp + +ADDITIONAL_C_FILES+=example.cpp + +include ../../ch32v003fun/ch32v003fun.mk + +# Removing compiler optimization to small file size +# because it optimizes the virtual functions out +# which are tested here. +CFLAGS:= \ + -g -flto -ffunction-sections \ + -static-libgcc \ + -march=rv32ec \ + -mabi=ilp32e \ + -I/usr/include/newlib \ + -I$(CH32V003FUN) \ + -nostdlib \ + -I. -Wall + +CFLAGS+=-fno-rtti -DSTDOUT_UART -DCPLUSPLUS + +flash : cv_flash +clean : cv_clean \ No newline at end of file diff --git a/examples/cpp_virtual_methods/README.md b/examples/cpp_virtual_methods/README.md new file mode 100644 index 0000000..14f14ba --- /dev/null +++ b/examples/cpp_virtual_methods/README.md @@ -0,0 +1,29 @@ +# C++ Virtual Methods Example + +This example tests the usage of classes with virtual methods in C++. + +Without the usage of `__libc_init_array`, the virtual table of the class instance `Example` is not initialized correctly: + +``` +vtable for 'ExampleClass' @ 0x0 (subobject @ 0x20000004): +[0]: 0x11e0006f +[1]: 0x0 <InterruptVectorDefault()> +``` + +In this case, a call to a virtual method will result to an invalid call +and the program won't work as expected. +Here, the program will also print out `Begin example`, but does not print any values. +It seems like the MCU resets here. + +In other environments like with the Arduino Core, the hard fault handler is called +and the program gets into an infinite loop. + +With the macro `CPLUSPLUS`, an implemention auf `__libc_init_array` is called on startup. + +Then the virtual table is initialized correctly and new values are printed as expected: + +``` +vtable for 'ExampleClass' @ 0x212c (subobject @ 0x20000004): +[0]: 0x1fb0 <ExampleClass::doNewLine()> +[1]: 0x1fda <ExampleClass::printValue(int)> +``` \ No newline at end of file diff --git a/examples/cpp_virtual_methods/cpp_virtual_methods.cpp b/examples/cpp_virtual_methods/cpp_virtual_methods.cpp new file mode 100644 index 0000000..fccdcb8 --- /dev/null +++ b/examples/cpp_virtual_methods/cpp_virtual_methods.cpp @@ -0,0 +1,30 @@ +/* + * Example for using virtual methods in C++ + * 05/21/2023 A. Mandera + */ + +// Could be defined here, or in the processor defines. +#define SYSTEM_CORE_CLOCK 48000000 +#define APB_CLOCK SYSTEM_CORE_CLOCK + +#include "ch32v003fun.h" +#include "example.h" +#include <stdio.h> + +int main() { + SystemInit48HSI(); + + // Setup UART @ 115200 baud + SetupUART(UART_BRR); + Delay_Ms(100); + + printf("Begin example\n"); + + // Initialize variable in example class + Example.begin(); + + while (true) { + Example.doPrint(10); + Delay_Ms(1000); + } +} \ No newline at end of file diff --git a/examples/cpp_virtual_methods/example.cpp b/examples/cpp_virtual_methods/example.cpp new file mode 100644 index 0000000..19560d8 --- /dev/null +++ b/examples/cpp_virtual_methods/example.cpp @@ -0,0 +1,30 @@ +/* + * Example for using virtual methods in C++ + * 05/21/2023 A. Mandera + */ + +#include "example.h" +#include "stdio.h" + +ExampleClass Example; + +void Print::doPrint(int i) { + printValue(i); + doNewLine(); +} + +void ExampleClass::begin() { + this->_value = 1; +} + +int ExampleClass::value() { + return this->_value++; +} + +void ExampleClass::doNewLine() { + printf("\n"); +} + +void ExampleClass::printValue(int i) { + printf("Value: %d", this->value() + i); +}; \ No newline at end of file diff --git a/examples/cpp_virtual_methods/example.h b/examples/cpp_virtual_methods/example.h new file mode 100644 index 0000000..97c46f4 --- /dev/null +++ b/examples/cpp_virtual_methods/example.h @@ -0,0 +1,25 @@ +/* + * Example for using virtual methods in C++ + * 05/21/2023 A. Mandera + */ + +#pragma once + +class Print { +public: + void doPrint(int); + virtual void doNewLine(void) = 0; + virtual void printValue(int) = 0; +}; + +class ExampleClass : public Print { +public: + void begin(); + int value(); + void doNewLine(void) override; + void printValue(int) override; +private: + int _value; +}; + +extern ExampleClass Example; \ No newline at end of file -- GitLab