eldarkg / emdr1986x-std-per-lib

Milandr MCU 1986x Standard Peripherals Library. Mirror:

Home Page:https://code.launchpad.net/~eldar/emdr1986x-std-per-lib/+git/emdr1986x-std-per-lib

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Startup doesn't work properly with C++

Amomum opened this issue · comments

If this library is not supposed to be used with C++ at all, that's not a problem. Still, here's the problem:

If I create a global object, it's constructor should be called before main. Unfortunately, in startup_MDR32F9Qx.s there is no mention of that. Therefore global constructors are not called at all. That's bad.

If we go and look into startup files for stm32, for example (distributed with cubeMx), we will see something like this:

Reset_Handler:
<...>
  ldr r3, = _ebss
  cmp r2, r3
  bcc FillZerobss

/* Call the clock system intitialization function.*/
    bl  SystemInit
/* Call static constructors */
    bl __libc_init_array <----------------------------- here it is!
/* Call the application's entry point.*/
  bl main
  bx lr

To make this work we should:

  • change startup accordingly;

  • remove -nostartfiles from linker flags (because __libc_init_array will call __init and it must come from somewhere). This will make total code size bigger; unfortunately, I don't know what can be done about that;

  • add -mthumb to linker flags (because by default it will link startfiles compiled into arm code, not thumb).

@Amomum
You should to delete -D__START=main from compiler flags and you should to use arm-none-eabi-g++. And you should to add --specs=nano.specs to linker flags.

add -mthumb to linker flags (because by default it will link startfiles compiled into arm code, not thumb).

It is showed in example (see CFLAGS).

Can you look this on Launchpad?
Look at example of gcc-arm-embedded (on Linux /usr/share/gcc-arm-embedded/samples/src/cpp/). It use same startup and linker files.
Give me know about your researches.

Well, now I made static ctors work with the following flags:

gcc:
-c -fmessage-length=0 -mcpu=cortex-m3 -mthumb -fdata-sections -ffunction-sections -Wcast-align -Wcast-qual -Wvla -Wshadow -Wsuggest-attribute=const -Wmissing-format-attribute -Wuninitialized -Winit-self -Wdouble-promotion -Wno-unused-local-typedefs
g++:
-c -fmessage-length=0 -mcpu=cortex-m3 -mthumb -fdata-sections -ffunction-sections -fno-rtti -fno-exceptions -fno-threadsafe-statics -Wcast-align -Wcast-qual -Wvla -Wshadow -Wsuggest-attribute=const -Wmissing-format-attribute -Wuninitialized -Winit-self -Wdouble-promotion -Wstrict-aliasing -Weffc++ -Wno-unused-local-typedefs
link:
-mthumb --specs=nano.specs -Wl,--gc-sections -T "${ProjDirPath}/src/Startup/STM32F100XB_FLASH.ld" -ffreestanding -Wl,-defsym,__dso_handle=0 -Wl,-Map=output.map

(not sure if dso_handle is required though)

And as you can see, I took linker script from stm32f100 since it seem to have compatible flash/ram size and origin. And I used startup file for it as well.

I'm not really familiar with gcc linker script and asm syntax so I didn't dare editing existing files but I'm not aware of any sufficient diffirencies between milandr and stm in that matter.

@Amomum I don't understand have you try to compile these mdr pack files with my suggestions.

@Amomum just add in linker script
`
. = ALIGN(4);
/* preinit data /
PROVIDE_HIDDEN (__preinit_array_start = .);
KEEP(
(.preinit_array))
PROVIDE_HIDDEN (__preinit_array_end = .);

	. = ALIGN(4);
	/* init data */
	PROVIDE_HIDDEN (__init_array_start = .);
	KEEP(*(SORT(.init_array.*)))
	KEEP(*(.init_array))
	PROVIDE_HIDDEN (__init_array_end = .);

	. = ALIGN(4);
	/* finit data */
	PROVIDE_HIDDEN (__fini_array_start = .);
	KEEP(*(SORT(.fini_array.*)))
	KEEP(*(.fini_array))
	PROVIDE_HIDDEN (__fini_array_end = .);

and place constructors call
/* Iterate over all the init routines. */
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] ();
}

/* Run all the cleanup routines. */
void
__libc_fini_array (void)
{
size_t count;
size_t i;

count = __fini_array_end - __fini_array_start;
for (i = count; i > 0; i--)
__fini_array_start[i-1] ();

}`

@legath

just add in linker script
`
. = ALIGN(4);
/* preinit data /
PROVIDE_HIDDEN (__preinit_array_start = .);
KEEP((.preinit_array))
PROVIDE_HIDDEN (__preinit_array_end = .);

. = ALIGN(4);
/* init data /
PROVIDE_HIDDEN (__init_array_start = .);
KEEP(
(SORT(.init_array.)))
KEEP(
(.init_array))
PROVIDE_HIDDEN (__init_array_end = .);

. = ALIGN(4);
/* finit data /
PROVIDE_HIDDEN (__fini_array_start = .);
KEEP(
(SORT(.fini_array.)))
KEEP(
(.fini_array))
PROVIDE_HIDDEN (__fini_array_end = .);

It is exist already here.

and place constructors call

$ arm-none-eabi-objdump -tC /usr/arm-none-eabi/lib/thumb/v7-m/libg.a | less
lib_a-init.o:     file format elf32-littlearm

SYMBOL TABLE:
00000000 l    df *ABS*  00000000 init.c
00000000 l    d  .text  00000000 .text
00000000 l    d  .data  00000000 .data
00000000 l    d  .bss   00000000 .bss
00000000 l    d  .text.__libc_init_array        00000000 .text.__libc_init_array
00000000 l    d  .debug_frame   00000000 .debug_frame
00000000 l    d  .ARM.attributes        00000000 .ARM.attributes
00000000 g     F .text.__libc_init_array        00000050 __libc_init_array <------------------------------------------
00000000         *UND*  00000000 _init
00000000  w      *UND*  00000000 __preinit_array_end
00000000  w      *UND*  00000000 __preinit_array_start
00000000  w      *UND*  00000000 __init_array_end
00000000  w      *UND*  00000000 __init_array_start

lib_a-fini.o:     file format elf32-littlearm

SYMBOL TABLE:
00000000 l    df *ABS*  00000000 fini.c
00000000 l    d  .text  00000000 .text
00000000 l    d  .data  00000000 .data
00000000 l    d  .bss   00000000 .bss
00000000 l    d  .text.__libc_fini_array        00000000 .text.__libc_fini_array
00000000 l    d  .debug_frame   00000000 .debug_frame
00000000 l    d  .ARM.attributes        00000000 .ARM.attributes
00000000 g     F .text.__libc_fini_array        00000034 __libc_fini_array <------------------------------------------
00000000         *UND*  00000000 _fini
00000000  w      *UND*  00000000 __fini_array_end
00000000  w      *UND*  00000000 __fini_array_start
$ arm-none-eabi-objdump -tC /usr/arm-none-eabi/lib/thumb/v7-m/crt0.o | less
/usr/arm-none-eabi/lib/thumb/v7-m/crt0.o:     file format elf32-littlearm

SYMBOL TABLE:
00000000 l    d  .text  00000000 .text
00000000 l    d  .data  00000000 .data
00000000 l    d  .bss   00000000 .bss
00000000 l    d  .ARM.extab     00000000 .ARM.extab
00000000 l    d  .ARM.exidx     00000000 .ARM.exidx
00000000 l    d  .ARM.attributes        00000000 .ARM.attributes
00000000 g     F .text  00000000 _mainCRTStartup
00000000 g     F .text  00000000 _start              <--------------------------------------------------------------
00000000         *UND*  00000000 memset
00000000  w      *UND*  00000000 atexit
00000000         *UND*  00000000 __libc_init_array   <------------------ linked here from libg.a --------
00000000         *UND*  00000000 main
00000000         *UND*  00000000 exit
00000000  w      *UND*  00000000 __stack
00000000  w      *UND*  00000000 hardware_init_hook
00000000  w      *UND*  00000000 software_init_hook
00000000         *UND*  00000000 __bss_start__
00000000         *UND*  00000000 __bss_end__
00000000  w      *UND*  00000000 __libc_fini_array   <------------------ linked here from libg.a --------

It is the same if use nano spec:

$ arm-none-eabi-objdump -tC /usr/arm-none-eabi/lib/thumb/v7-m/libg_nano.a | less
lib_a-init.o:     file format elf32-littlearm

SYMBOL TABLE:
00000000 l    df *ABS*  00000000 init.c
00000000 l    d  .text  00000000 .text
00000000 l    d  .data  00000000 .data
00000000 l    d  .bss   00000000 .bss
00000000 l    d  .text.__libc_init_array        00000000 .text.__libc_init_array
00000000 l    d  .debug_frame   00000000 .debug_frame
00000000 l    d  .ARM.attributes        00000000 .ARM.attributes
00000000 g     F .text.__libc_init_array        00000048 __libc_init_array       <--------------------------
00000000         *UND*  00000000 _init
00000000  w      *UND*  00000000 __preinit_array_start
00000000  w      *UND*  00000000 __preinit_array_end
00000000  w      *UND*  00000000 __init_array_start
00000000  w      *UND*  00000000 __init_array_end

lib_a-fini.o:     file format elf32-littlearm

SYMBOL TABLE:
00000000 l    df *ABS*  00000000 fini.c
00000000 l    d  .text  00000000 .text
00000000 l    d  .data  00000000 .data
00000000 l    d  .bss   00000000 .bss
00000000 l    d  .text.__libc_fini_array        00000000 .text.__libc_fini_array
00000000 l    d  .debug_frame   00000000 .debug_frame
00000000 l    d  .ARM.attributes        00000000 .ARM.attributes
00000000 g     F .text.__libc_fini_array        00000028 __libc_fini_array      <-----------------------------------
00000000         *UND*  00000000 _fini
00000000  w      *UND*  00000000 __fini_array_start
00000000  w      *UND*  00000000 __fini_array_end
$ cat /usr/arm-none-eabi/lib/thumb/v7-m/nano.specs 
%rename link                nano_link
%rename link_gcc_c_sequence                nano_link_gcc_c_sequence
%rename cpp_unique_options		nano_cpp_unique_options

*cpp_unique_options:
-isystem =/include/newlib-nano %(nano_cpp_unique_options)

*nano_libc:
-lc_nano

*nano_libgloss:
%{specs=rdimon.specs:-lrdimon_nano} %{specs=nosys.specs:-lnosys}

*link_gcc_c_sequence:
%(nano_link_gcc_c_sequence) --start-group %G %(nano_libc) %(nano_libgloss) --end-group

*link:
%(nano_link) %:replace-outfile(-lc -lc_nano) %:replace-outfile(-lg -lg_nano) %:replace-outfile(-lrdimon -lrdimon_nano) %:replace-outfile(-lstdc++ -lstdc++_nano) %:replace-outfile(-lsupc++ -lsupc++_nano)

*lib:
%{!shared:%{g*:-lg_nano} %{!p:%{!pg:-lc_nano}}%{p:-lc_p}%{pg:-lc_p}}

Right. Sorry for such a long pause.
So, I had a few subtle (and stupid) problems with my settings. So, that's what I did:
I created this simple main.cpp where I create a global instance of class A and inside it's constructor I turn the led on:

#include "MDR32Fx.h"

void led_on()
{
    MDR_RST_CLK->PER_CLOCK |= (1<<24);
    MDR_PORTD->ANALOG |= (1<<10);
    MDR_PORTD->OE = 1<<10;
    MDR_PORTD->PWR |= (1<<20);

    MDR_PORTD->RXTX |= 1<<10;
}

class B
{};

class A : B
{
public:
    A()
    {
        led_on();
    }

    virtual void foo()
    {}

    virtual ~A()
    {}
};

A a;

int main(void)
{   
    while(1)
    {        
    }
    return 0;
}

Then I took your startup file and your linker script and was able to compile and link them like this:

arm-none-eabi-g++ -c -fmessage-length=0 -mcpu=cortex-m3 -mthumb -fdata-sections -ffunction-sections -fno-rtti -fno-exceptions -fno-threadsafe-statics -ICMSIS main.cpp -o main.o

arm-none-eabi-g++ -mcpu=cortex-m3 -mthumb -O0 -ffunction-sections -fdata-sections -pipe -DUSE_MDR1986VE9x -D__STARTUP_CLEAR_BSS -D__NO_SYSTEM_INIT -c startup_MDR32F9Qx.S -o startup.o

..\arm-none-eabi-g++ -mcpu=cortex-m3 -mthumb -O0 -ffunction-sections -fdata-sections -pipe -T "MDR32F9Qx.ld" --specs=nosys.specs -Wl,--gc-sections -ffreestanding -Wl,-Map=example.map main.o startup.o -o example.elf

And it worked! So, I was wrong in my initial proposal for fix, because I forgot to add -mcpu=cortex-m3 to linker and despite -mthumb it kept linking the wrong version of crt0, which only by some luck still called main. And I thought the problem was in the linker script.

It turns out that even nano.specs are not necessary.

So, to make this work we should slightly change recommendations in the readme.md:

  • -D__START=main should be removed from compiler call
  • -nostartfiles should be removed from linker call
  • example for g++ call should be added

@Amomum nano.specs need if you want to use more compact (and maybe more speedy) standard library for embedded systems (with some functionally limitations).
Can you create a pull request with this recomendations?