GP32 Using C++ With Gp32 - Constructors? Crt0.o?


slaanesh

Certified Guru
Joined
Nov 9, 2005
Messages
1,995
Age
54
Location
Melbourne, Australia
Website
www.slaanesh.net
I seem to be having some problems with constructors in C++. It looks like they are not firing at all.
I'm using the EABI SDL++ libraries with devkitARM release 26 toolchain.
Linking with -nostartfiles -specs=gp32.specs crt0.o
The gp32.specs is from devkitARM.

Sample Code:

Code:
const string dataPath = "/gpmm/kuklomen/";

ifstream* openDataFile(string relPath)
{
    ifstream* f = new ifstream;
GpPrintf("%s", dataPath.c_str());
    f->open((dataPath+relPath).c_str(), ios::binary);
    return f;
}

GpPrintf is my debugging function which lets me print as per printf-like statements.
The above debug is printing (null) when I am clearly expecting it to be "/gpmm/kuklomen" (yes, I'm trying to port Kuklomenos).
There seem to be other bits of code that are exhibiting similar behavior - values are not initialized.

It seems like the .init_array list of constructor functions is NOT being called.
What am I doing wrong? Comments and help very welcome.
I know there are workarounds for the above example, but I'd like to get a real fix for this (if indeed this is the problem).
I'm thinking it's something in crt0.o which is built from the following EABI SDL++ file.
The official GPSDK calls something like __libc_init_array() but there's nothing like this here.
And as far as I can see, the EABI SDL++ libc.a does not have a __libc_init_array() - though some other equivalent maybe. Dunno.


Code:
@////////////////////////////////////////////////////////////////////////////@
@/ crt0.S                                                                   /@
@////////////////////////////////////////////////////////////////////////////@

        .TEXT
        .CODE   32
        .ALIGN

        .GLOBAL         _start
        .GLOBAL     atexit
        .GLOBAL     exit

    .EXTERN     main    @ user entry point
    .EXTERN     x_gp32_SetPrintBase
    .EXTERN     x_gp32_ShutdownGraphics
    .EXTERN     x_gp32_SetLcdBuffer
    .EXTERN     x_gp32_init_timer
    .EXTERN     x_gp32_initFramebuffer
    .EXTERN     SmcInit


_start:

        b start_vector


        @ AXF addresses
_text_start:
        .word   __text_start
_ro_end:
        .word   __ro_end
_data_start:
        .word   __data_start
        .word   __bss_end
_bss_start:
        .word   __bss_start
_bss_end:
        .word   __bss_end

        @ GamePark magic sequence
        .word   0x44450011
        .word   0x44450011
        .word   0x01234567
        .word   0x12345678
        .word   0x23456789
        .word   0x34567890
        .word   0x45678901
        .word   0x56789012
        .word   0x23456789
        .word   0x34567890
        .word   0x45678901
        .word   0x56789012
        .word   0x23456789
        .word   0x34567890
        .word   0x45678901
        .word   0x56789012

start_vector:
                mrc p15, 0, R5, c1, c0, 0               @ read control register
                bic r5,r5,#2                            @ align control OFF
                orr r5,r5,#0x1000
                orr r5,r5,#4
                mcr p15, 0, R5, c1, c0, 0               @ write control register


                ldr sp, =0x0c78fffc

                bl      SmcInit

@               mov     r0, #8
@               bl      x_gp32_SetGraphicsMode

                mov     r1, #8                          @ BPP
                mov     r2, #60                         @ refresh rate
                mov     r0, #208666624                  @ framebuffer base
                add     r0, r0, #737280                 @ framebuffer address
                bl      x_gp32_initFramebuffer

                mov     r0, #208666624
                add     r0, r0, #737280
                bl      x_gp32_SetLcdBuffer

                mov     r0, #208666624
                add     r0, r0, #737280
                bl      x_gp32_SetPrintBase

                bl      x_gp32_init_timer

                bl      main @ jump to entry point


exit:
                bl      x_gp32_ShutdownGraphics
                ldr     lr,=0x00000000

@ dummy function to keep gcc happy

atexit:
                mov     pc,lr

        .POOL
        .END
 
Quick update (still need help):

I've created my own __libc_init_array() trying to duplicate newlib's functionality.

Code:
extern void (*__init_array_start []) (void) __attribute__((weak));
extern void (*__init_array_end []) (void) __attribute__((weak));

__libc_init_array(void)
{
 int i;
 int count = __init_array_end - __init_array_start;
 for (i = 0; i < count; i++)
     GpPrintf("_init %d/%d : %x", i+1, count, __init_array_start[i]);
 for (i = 0; i < count; i++)
   __init_array_start[i] ();
}

Here's the MAP file generated on linking:

Code:
.init_array     0x0c0e6fe4       0x38
 *(.init_array)
 .init_array    0x0c0e6fe4        0x4 conffile.o
 .init_array    0x0c0e6fe8        0x4 data.o
 .init_array    0x0c0e6fec        0x4 geom.o
 .init_array    0x0c0e6ff0        0x4 keybindings.o
 .init_array    0x0c0e6ff4        0x4 main.o
 .init_array    0x0c0e6ff8        0x4 menu.o
 .init_array    0x0c0e6ffc        0x4 settings.o
 .init_array    0x0c0e7000        0x4 sound.o
 .init_array    0x0c0e7004        0x4 SDL_gfxPrimitivesDirty.o
 .init_array    0x0c0e7008        0x4 /devkitarm/bin/../lib/gcc/arm-eabi/4.4.0/../../../../arm-eabi/lib\libstdc++.a(system_error.o)
 .init_array    0x0c0e700c        0x4 /devkitarm/bin/../lib/gcc/arm-eabi/4.4.0/../../../../arm-eabi/lib\libstdc++.a(locale-inst.o)
 .init_array    0x0c0e7010        0x4 /devkitarm/bin/../lib/gcc/arm-eabi/4.4.0/../../../../arm-eabi/lib\libstdc++.a(eh_alloc.o)
 .init_array    0x0c0e7014        0x4 /devkitarm/bin/../lib/gcc/arm-eabi/4.4.0/../../../../arm-eabi/lib\libstdc++.a(codecvt.o)
 .init_array    0x0c0e7018        0x4 /devkitarm/bin/../lib/gcc/arm-eabi/4.4.0/../../../../arm-eabi/lib\libstdc++.a(ctype.o)
                0x0c0e701c                PROVIDE (__init_array_end, .)

However it doesn't work.

I have two loops; the first is just printing out debugging information.
The debug info successfully shows me that there are 14 functions to execute however everything dies horribly after the very first actual call (presumably to conffile.o's constructor).

I don't know why.

Is this the correct section?

Here's the .ctors section from the same MAP file:
Code:
.ctors          0x0c1019a0        0x0
 *crtbegin.o(.ctors)
 *(EXCLUDE_FILE(*crtend.o) .ctors)
 *(SORT(.ctors.*))
 *(.ctors)
                0x0c1019a0                . = ALIGN (0x4)

Which seems to be empty. Unless I am reading it wrong.

Here's a arm-eabi-objdump of .init_array.

Code:
Contents of section .init_array:
 c0e6fe4 f02b000c 5443000c 6448000c c097000c  .+..TC..dH......
 c0e6ff4 4473020c c89f020c 98ec020c 14f3020c  Ds..............
 c0e7004 906b030c 104f070c bcf1070c a0a6090c  .k...O..........
 c0e7014 d0d0090c 74d6090c                    ....t...

This matches my 14 expected function pointers. The first one points to 0x0c002bf0.
Here's a dis-assembly of what is at that location.
It seems to die somewhere in here.

Any suggestions? Am I doing the right thing trying to execute these?
Blah.

Code:
 c002bf0:       e92d4007        push    {r0, r1, r2, lr}
 c002bf4:       e59f0058        ldr     r0, [pc, #88]   ; 0xc002c54
 c002bf8:       eb01be3c        bl      0xc0724f0
 c002bfc:       e59f1054        ldr     r1, [pc, #84]   ; 0xc002c58
 c002c00:       e59f2054        ldr     r2, [pc, #84]   ; 0xc002c5c
 c002c04:       e59f0048        ldr     r0, [pc, #72]   ; 0xc002c54
 c002c08:       eb025d8d        bl      0xc09a244
 c002c0c:       e59f004c        ldr     r0, [pc, #76]   ; 0xc002c60
 c002c10:       e59f104c        ldr     r1, [pc, #76]   ; 0xc002c64
 c002c14:       e28d2004        add     r2, sp, #4      ; 0x4
 c002c18:       eb0253dd        bl      0xc097b94
 c002c1c:       ea000000        b       0xc002c24
 c002c20:       eb025f29        bl      0xc09a8cc
 c002c24:       e59f103c        ldr     r1, [pc, #60]   ; 0xc002c68
 c002c28:       e59f202c        ldr     r2, [pc, #44]   ; 0xc002c5c
 c002c2c:       e59f002c        ldr     r0, [pc, #44]   ; 0xc002c60
 c002c30:       eb025d83        bl      0xc09a244
 c002c34:       e59f0030        ldr     r0, [pc, #48]   ; 0xc002c6c
 c002c38:       ebfffe8c        bl      0xc002670
 c002c3c:       e59f102c        ldr     r1, [pc, #44]   ; 0xc002c70
 c002c40:       e59f2014        ldr     r2, [pc, #20]   ; 0xc002c5c
 c002c44:       e59f0020        ldr     r0, [pc, #32]   ; 0xc002c6c
 c002c48:       eb025d7d        bl      0xc09a244
 c002c4c:       e8bd400e        pop     {r1, r2, r3, lr}
 c002c50:       e12fff1e        bx      lr
 c002c54:       0c0f6028        stceq   0, cr6, [pc], {40}
 c002c58:       0c072138        stfeqs  f2, [r7], {56}
 c002c5c:       0c0f975c        stceq   7, cr9, [pc], {92}
 c002c60:       0c0f602c        stceq   0, cr6, [pc], {44}
 c002c64:       0c0d22e8        sfmeq   f2, 4, [sp], {232}
 c002c68:       0c0970e0        stceq   0, cr7, [r9], {224}
 c002c6c:       0c0f6030        stceq   0, cr6, [pc], {48}
 c002c70:       0c003b9c        stceq   11, cr3, [r0], {156}
 
slaanesh said:
Quick update (still need help):

I've created my own __libc_init_array() trying to duplicate newlib's functionality.

Code:
extern void (*__init_array_start []) (void) __attribute__((weak));
 extern void (*__init_array_end []) (void) __attribute__((weak));
 
 __libc_init_array(void)
 {
  int i;
  int count = __init_array_end - __init_array_start;
  for (i = 0; i < count; i++)
      GpPrintf("_init %d/%d : %x", i+1, count, __init_array_start[i]);
  for (i = 0; i < count; i++)
    __init_array_start[i] ();
 }


the __init_array_start, __init_array_end and __libc_init_array signatures sound suspicious.

shouldn't they be :
Code:
extern "C" void (*__init_array_start []) (void);
 extern "C" void (*__init_array_end []) (void);
extern "C" void __libc_init_array(void) { ... }

"weak" version symbols are defined inside a lib so a programmer can supersede them with his/her "strong" version symbols (no "weak" attribute). Here you want to use them, so i'm not sure you need to specifiy the attribute.
 
Last edited by a moderator:
Thanks for your reply.

I tried changing these, but there is no difference. It's still crashing when executing the first function in the .init_array.

Are these the actual constructors?

I suspect the problem is something else.

I've been doing some research and found the use of crti.o, crtbegin.o and the matching crtend.o, crtn.o.
For example, crtbegin.o defines __dso_handle. Without crtbegin.o I get the following:

Code:
conffile.cc:(.text+0x7d4): undefined reference to `__dso_handle'
data.o: In function `global constructors keyed to fontSmall':

Previously I was (naively) defining:

Code:
void *__dso_handle = NULL

But this was just a work around to get it to compile.

Any other suggestions?
 
slaanesh said:
I tried changing these, but there is no difference. It's still crashing when executing the first function in the .init_array.
wait... are you saying your code was crashing ? you should have told it the first time.

I have a question :

what is your link script ? it must needs specify where the constructors are stored.
Code:
    CONSTRUCTORS
          This command ties up C++ style constructor and destructor records.
          The details of the constructor representation vary from one object format to another, but usually lists of constructors and destructors appear as special sections.
          The CONSTRUCTORS command specifies where  the linker is to place the data from these sections, relative to the rest of the linked output.
          Constructor data is marked by the symbol __CTOR_LIST__ at the start, and __CTOR_LIST_END  at the end;
          destructor data is bracketed similarly, between __DTOR_LIST__ and __DTOR_LIST_END.
          (The  compiler must arrange to actually run this code; GNU C++ calls constructors from a subroutine __main, which it inserts automatically into  the startup code for main, and destructors from _exit.)

EDIT: note also you need to call "__main" (gcc's) instead of "main" (yours). The same for "_exit". Normally "__main" will call a user "main" function, that is yours.
 
Last edited by a moderator:
Ah yes, the order of constructors can also lead to a crash if one is depending another one which is not constructed first. Not sure if this is the case for you.
 
Thanks again for your reply.

See my link script below.

There is no mention of __CTOR_LIST__ or __CTOR_LIST_END__

I have very little experience with editing the link script.
Presumable it goes somewhere near the .init_array entries. I'm unfamiliar with the format.

Also, I can't find any reference to __main in my toolchain. The only thing I found was in libgcc.a an entry to __main.o. Here's a dis-assembly.

Dis-assembly of relevant libgcc.a
Code:
_trampoline.o:     file format elf32-littlearm

__main.o:     file format elf32-littlearm

_absvsi2.o:     file format elf32-littlearm

cat gp32.ld script:
Code:
OUTPUT_FORMAT("elf32-littlearm", "elf32-bigarm", "elf32-littlearm")
OUTPUT_ARCH(arm)
ENTRY(_start)

MEMORY
  {
  ram : ORIGIN = 0xc000000, LENGTH = 8M
  }

SECTIONS
{
  .init           :
  {
    __text_start = . ;
    KEEP (*(.init))
    . = ALIGN(4);  /* REQUIRED. LD is flaky without it. */
  } >ram = 0xff
  .plt            : { *(.plt) } >ram = 0xff

 
 	.text :   /* ALIGN (4): */
	{
		*(.text .stub .text.* .gnu.linkonce.t.*)
		KEEP (*(.text.*personality*))
		/* .gnu.warning sections are handled specially by elf32.em.  */
		*(.gnu.warning)
		*(.glue_7t) *(.glue_7) *(.vfp11_veneer)
		. = ALIGN(4);  /* REQUIRED. LD is flaky without it. */
  } >ram = 0xff
  .fini           :
  {
    KEEP (*(.fini))
  } >ram =0xff

  __text_end = . ;

  .rodata :
  {
    *(.rodata)
    *all.rodata*(*)
    *(.roda)
    *(.rodata.*)
    *(.gnu.linkonce.r*)
    SORT(CONSTRUCTORS)
    . = ALIGN(4);   /* REQUIRED. LD is flaky without it. */
  } >ram = 0xff

  .ARM.extab   : { *(.ARM.extab* .gnu.linkonce.armextab.*) } >ram
   __exidx_start = .;
  .ARM.exidx   : { *(.ARM.exidx* .gnu.linkonce.armexidx.*) } >ram
   __exidx_end = .;
  /* Ensure the __preinit_array_start label is properly aligned.  We
     could instead move the label definition inside the section, but
     the linker would then create the section even if it turns out to
     be empty, which isn't pretty.  */
  . = ALIGN(32 / 8);
  PROVIDE (__preinit_array_start = .);
  .preinit_array     : { KEEP (*(.preinit_array)) } >ram = 0xff
  PROVIDE (__preinit_array_end = .);
  PROVIDE (__init_array_start = .);
  .init_array     : { KEEP (*(.init_array)) } >ram = 0xff
  PROVIDE (__init_array_end = .);
  PROVIDE (__fini_array_start = .);
  .fini_array     : { KEEP (*(.fini_array)) } >ram = 0xff
  PROVIDE (__fini_array_end = .);
  .ctors :
  {
    /* gcc uses crtbegin.o to find the start of the constructors, so
       we make sure it is first.  Because this is a wildcard, it
       doesn't matter if the user does not actually link against
       crtbegin.o; the linker won't look for a file to match a
       wildcard.  The wildcard also means that it doesn't matter which
       directory crtbegin.o is in.  */
    KEEP (*crtbegin.o(.ctors))
    KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors))
    KEEP (*(SORT(.ctors.*)))
    KEEP (*(.ctors))
    . = ALIGN(4);   /* REQUIRED. LD is flaky without it. */
  } >ram = 0

  .dtors :
  {
    KEEP (*crtbegin.o(.dtors))
    KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors))
    KEEP (*(SORT(.dtors.*)))
    KEEP (*(.dtors))
    . = ALIGN(4);   /* REQUIRED. LD is flaky without it. */
  } >ram = 0

  .eh_frame :
  {
    KEEP (*(.eh_frame))
    . = ALIGN(4);   /* REQUIRED. LD is flaky without it. */
  } >ram = 0

  .gcc_except_table :
  {
    *(.gcc_except_table)
    . = ALIGN(4);   /* REQUIRED. LD is flaky without it. */
  } >ram = 0
  .jcr            : { KEEP (*(.jcr)) } >ram = 0
  .got            : { *(.got.plt) *(.got) } >ram = 0
  __ro_end = . ;

  .data ALIGN(4) :
  {
    __data_start = ABSOLUTE(.);
    *(.data)
    *(.data.*)
    *(.gnu.linkonce.d*)
    CONSTRUCTORS
    . = ALIGN(4);  /* REQUIRED. LD is flaky without it. */
  } >ram = 0xff

  __data_end  =  . ;

  .bss ALIGN(4) :
  {
   __bss_start = ABSOLUTE(.);
   /* __bss_start__ = ABSOLUTE(.); */
   *(.dynbss)
   *(.gnu.linkonce.b*)
   *(.bss*)
   *(COMMON)
   . = ALIGN(4);    /* REQUIRED. LD is flaky without it. */
  __bss_end = ABSOLUTE(.) ;
  } > ram

  __end__ = __bss_end;
  end = __bss_end;	/* for x_gp32/syscalls.c */

  /* Stabs debugging sections.  */
  .stab 0 : { *(.stab) }
  .stabstr 0 : { *(.stabstr) }
  .stab.excl 0 : { *(.stab.excl) }
  .stab.exclstr 0 : { *(.stab.exclstr) }
  .stab.index 0 : { *(.stab.index) }
  .stab.indexstr 0 : { *(.stab.indexstr) }
  .comment 0 : { *(.comment) }
  /* DWARF debug sections.
     Symbols in the DWARF debugging sections are relative to the beginning
     of the section so we begin them at 0.  */
  /* DWARF 1 */
  .debug          0 : { *(.debug) }
  .line           0 : { *(.line) }
  /* GNU DWARF 1 extensions */
  .debug_srcinfo  0 : { *(.debug_srcinfo) }
  .debug_sfnames  0 : { *(.debug_sfnames) }
  /* DWARF 1.1 and DWARF 2 */
  .debug_aranges  0 : { *(.debug_aranges) }
  .debug_pubnames 0 : { *(.debug_pubnames) }
  /* DWARF 2 */
  .debug_info     0 : { *(.debug_info) }
  .debug_abbrev   0 : { *(.debug_abbrev) }
  .debug_line     0 : { *(.debug_line) }
  .debug_frame    0 : { *(.debug_frame) }
  .debug_str      0 : { *(.debug_str) }
  .debug_loc      0 : { *(.debug_loc) }
  .debug_macinfo  0 : { *(.debug_macinfo) }
  /* SGI/MIPS DWARF 2 extensions */
  .debug_weaknames 0 : { *(.debug_weaknames) }
  .debug_funcnames 0 : { *(.debug_funcnames) }
  .debug_typenames 0 : { *(.debug_typenames) }
  .debug_varnames  0 : { *(.debug_varnames) }
  .stack 0x80000 : { _stack = .; *(.stack) }
  /* These must appear regardless of  .  */
}
 
Back
Top