0

I'm trying to build googletest on an ARM M33 with the gcc-arm-none-eabi-9-2019-q4-major toolchain using QEMU. I have a simple C program which calls printf() etc. via semihosting but now I'm trying to incorporate some simple C++, namely #include <iostream>. As soon as I do this however I'm faced with the following errors:

/usr/bin/gcc-arm-none-eabi-9-2019-q4-major/bin/../lib/gcc/arm-none-eabi/9.2.1/../../../../arm-none-eabi/bin/ld: ../../cpp/call_gtest.o: in function `__static_initialization_and_destruction_0':
/usr/bin/gcc-arm-none-eabi-9-2019-q4-major/arm-none-eabi/include/c++/9.2.1/iostream:77: undefined reference to `__dso_handle'
/usr/bin/gcc-arm-none-eabi-9-2019-q4-major/bin/../lib/gcc/arm-none-eabi/9.2.1/../../../../arm-none-eabi/bin/ld: /usr/bin/gcc-arm-none-eabi-9-2019-q4-major/bin/../lib/gcc/arm-none-eabi/9.2.1/../../../../arm-none-eabi/lib/thumb/v8-m.main+fp/hard/libstdc++.a(cxx11-ios_failure.o): in function `(anonymous namespace)::__io_category_instance()':
cxx11-ios_failure.cc:(.text._ZN12_GLOBAL__N_122__io_category_instanceEv+0x38): undefined reference to `__dso_handle'
/usr/bin/gcc-arm-none-eabi-9-2019-q4-major/bin/../lib/gcc/arm-none-eabi/9.2.1/../../../../arm-none-eabi/bin/ld: /usr/bin/gcc-arm-none-eabi-9-2019-q4-major/bin/../lib/gcc/arm-none-eabi/9.2.1/../../../../arm-none-eabi/lib/thumb/v8-m.main+fp/hard/libstdc++.a(system_error.o): in function `_GLOBAL__sub_I__ZSt20__throw_system_errori':
system_error.cc:(.text.startup._GLOBAL__sub_I__ZSt20__throw_system_errori+0x1c): undefined reference to `__dso_handle'
/usr/bin/gcc-arm-none-eabi-9-2019-q4-major/bin/../lib/gcc/arm-none-eabi/9.2.1/../../../../arm-none-eabi/bin/ld: /usr/bin/gcc-arm-none-eabi-9-2019-q4-major/bin/../lib/gcc/arm-none-eabi/9.2.1/../../../../arm-none-eabi/lib/thumb/v8-m.main+fp/hard/libg.a(lib_a-fini.o): in function `__libc_fini_array':
fini.c:(.text.__libc_fini_array+0x20): undefined reference to `_fini'
/usr/bin/gcc-arm-none-eabi-9-2019-q4-major/bin/../lib/gcc/arm-none-eabi/9.2.1/../../../../arm-none-eabi/bin/ld: ../out/kernel.elf: hidden symbol `__dso_handle' isn't defined
/usr/bin/gcc-arm-none-eabi-9-2019-q4-major/bin/../lib/gcc/arm-none-eabi/9.2.1/../../../../arm-none-eabi/bin/ld: final link failed: bad value
collect2: error: ld returned 1 exit status
make: *** [../..//include.mk:72: ../out/kernel.elf] Error 1

I've tried compiling with the -fno-use-cxa-atexit from this post with no luck. I'm definately not compiling with the -nostdlib flag.

My linker script (taken from CMSIS):

/******************************************************************************
 * @file     gcc_arm.ld
 * @brief    GNU Linker Script for Cortex-M based device
 * @version  V2.2.0
 * @date     16. December 2020
 ******************************************************************************/
/*
 * Copyright (c) 2009-2020 Arm Limited. All rights reserved.
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Licensed under the Apache License, Version 2.0 (the License); you may
 * not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an AS IS BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*
 *-------- <<< Use Configuration Wizard in Context Menu >>> -------------------
 */

/*---------------------- Flash Configuration ----------------------------------
  <h> Flash Configuration
    <o0> Flash Base Address <0x0-0xFFFFFFFF:8>
    <o1> Flash Size (in Bytes) <0x0-0xFFFFFFFF:8>
  </h>
  -----------------------------------------------------------------------------*/
/* See https://www.google.com/url?sa=t&rct=j&q=&esrc=s&source=web&cd=&ved=2ahUKEwiY67e2meD8AhUKQ0EAHSwmBSIQFnoECCAQAQ&url=https%3A%2F%2Fdocumentation-service.arm.com%2Fstatic%2F5ed11469ca06a95ce53f8ed7%3Ftoken%3D&usg=AOvVaw0o2b4qMG6MiKjhd_STNKqR
*/
__ROM_BASE = 0x10000000;
__ROM_SIZE = 200M;

/*--------------------- Embedded RAM Configuration ----------------------------
  <h> RAM Configuration
    <o0> RAM Base Address    <0x0-0xFFFFFFFF:8>
    <o1> RAM Size (in Bytes) <0x0-0xFFFFFFFF:8>
  </h>
 -----------------------------------------------------------------------------*/
/* See https://www.google.com/url?sa=t&rct=j&q=&esrc=s&source=web&cd=&ved=2ahUKEwiY67e2meD8AhUKQ0EAHSwmBSIQFnoECCAQAQ&url=https%3A%2F%2Fdocumentation-service.arm.com%2Fstatic%2F5ed11469ca06a95ce53f8ed7%3Ftoken%3D&usg=AOvVaw0o2b4qMG6MiKjhd_STNKqR
*/
__RAM_BASE = 0x38000000;
__RAM_SIZE = 2048K;

/*--------------------- Stack / Heap Configuration ----------------------------
  <h> Stack / Heap Configuration
    <o0> Stack Size (in Bytes) <0x0-0xFFFFFFFF:8>
    <o1> Heap Size (in Bytes) <0x0-0xFFFFFFFF:8>
  </h>
  -----------------------------------------------------------------------------*/
__STACK_SIZE = 0x00100000;
__HEAP_SIZE  = 0x00000100;

/*
 *-------------------- <<< end of configuration section >>> -------------------
 */

/* ARMv8-M stack sealing:
   to use ARMv8-M stack sealing set __STACKSEAL_SIZE to 8 otherwise keep 0
 */
__STACKSEAL_SIZE = 0;


MEMORY
{
  FLASH (rx)  : ORIGIN = __ROM_BASE, LENGTH = __ROM_SIZE
  RAM   (rwx) : ORIGIN = __RAM_BASE, LENGTH = __RAM_SIZE
}

/* Linker script to place sections and symbol values. Should be used together
 * with other linker script that defines memory regions FLASH and RAM.
 * It references following symbols, which must be defined in code:
 *   Reset_Handler : Entry of reset handler
 *
 * It defines following symbols, which code can use without definition:
 *   __exidx_start
 *   __exidx_end
 *   __copy_table_start__
 *   __copy_table_end__
 *   __zero_table_start__
 *   __zero_table_end__
 *   __etext
 *   __data_start__
 *   __preinit_array_start
 *   __preinit_array_end
 *   __init_array_start
 *   __init_array_end
 *   __fini_array_start
 *   __fini_array_end
 *   __data_end__
 *   __bss_start__
 *   __bss_end__
 *   __end__
 *   end
 *   __HeapLimit
 *   __StackLimit
 *   __StackTop
 *   __stack
 *   __StackSeal      (only if ARMv8-M stack sealing is used)
 */
ENTRY(Reset_Handler)

SECTIONS
{
  .text :
  {
    KEEP(*(.vectors))
    *(.text*)

    KEEP(*(.init))
    KEEP(*(.fini))

    /* .ctors */
    *crtbegin.o(.ctors)
    *crtbegin?.o(.ctors)
    *(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors)
    *(SORT(.ctors.*))
    *(.ctors)

    /* .dtors */
    *crtbegin.o(.dtors)
    *crtbegin?.o(.dtors)
    *(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors)
    *(SORT(.dtors.*))
    *(.dtors)

    *(.rodata*)

    KEEP(*(.eh_frame*))
  } > FLASH

  /*
   * SG veneers:
   * All SG veneers are placed in the special output section .gnu.sgstubs. Its start address
   * must be set, either with the command line option �--section-start� or in a linker script,
   * to indicate where to place these veneers in memory.
   */
/*
  .gnu.sgstubs :
  {
    . = ALIGN(32);
  } > FLASH
*/
  .ARM.extab :
  {
    *(.ARM.extab* .gnu.linkonce.armextab.*)
  } > FLASH

  __exidx_start = .;
  .ARM.exidx :
  {
    *(.ARM.exidx* .gnu.linkonce.armexidx.*)
  } > FLASH
  __exidx_end = .;

  .copy.table :
  {
    . = ALIGN(4);
    __copy_table_start__ = .;

    LONG (__etext)
    LONG (__data_start__)
    LONG ((__data_end__ - __data_start__) / 4)

    /* Add each additional data section here */
/*
    LONG (__etext2)
    LONG (__data2_start__)
    LONG ((__data2_end__ - __data2_start__) / 4)
*/
    __copy_table_end__ = .;
  } > FLASH

  .zero.table :
  {
    . = ALIGN(4);
    __zero_table_start__ = .;
    /* Add each additional bss section here */
/*
    LONG (__bss2_start__)
    LONG ((__bss2_end__ - __bss2_start__) / 4)
*/
    __zero_table_end__ = .;
  } > FLASH

  /**
   * Location counter can end up 2byte aligned with narrow Thumb code but
   * __etext is assumed by startup code to be the LMA of a section in RAM
   * which must be 4byte aligned
   */
  __etext = ALIGN (4);

  .data : AT (__etext)
  {
    __data_start__ = .;
    *(vtable)
    *(.data)
    *(.data.*)

    . = 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 = .);

    KEEP(*(.jcr*))
    . = ALIGN(4);
    /* All data end */
    __data_end__ = .;

  } > RAM

  /*
   * Secondary data section, optional
   *
   * Remember to add each additional data section
   * to the .copy.table above to asure proper
   * initialization during startup.
   */
/*
  __etext2 = ALIGN (4);

  .data2 : AT (__etext2)
  {
    . = ALIGN(4);
    __data2_start__ = .;
    *(.data2)
    *(.data2.*)
    . = ALIGN(4);
    __data2_end__ = .;

  } > RAM2
*/

  .bss :
  {
    . = ALIGN(4);
    __bss_start__ = .;
    *(.bss)
    *(.bss.*)
    *(COMMON)
    . = ALIGN(4);
    __bss_end__ = .;
  } > RAM AT > RAM

  /*
   * Secondary bss section, optional
   *
   * Remember to add each additional bss section
   * to the .zero.table above to asure proper
   * initialization during startup.
   */
/*
  .bss2 :
  {
    . = ALIGN(4);
    __bss2_start__ = .;
    *(.bss2)
    *(.bss2.*)
    . = ALIGN(4);
    __bss2_end__ = .;
  } > RAM2 AT > RAM2
*/

  .heap (COPY) :
  {
    . = ALIGN(8);
    __end__ = .;
    PROVIDE(end = .);
    . = . + __HEAP_SIZE;
    . = ALIGN(8);
    __HeapLimit = .;
  } > RAM

  .stack (ORIGIN(RAM) + LENGTH(RAM) - __STACK_SIZE - __STACKSEAL_SIZE) (COPY) :
  {
    . = ALIGN(8);
    __StackLimit = .;
    . = . + __STACK_SIZE;
    . = ALIGN(8);
    __StackTop = .;
  } > RAM
  PROVIDE(__stack = __StackTop);

  /* ARMv8-M stack sealing:
     to use ARMv8-M stack sealing uncomment '.stackseal' section
   */
/*
  .stackseal (ORIGIN(RAM) + LENGTH(RAM) - __STACKSEAL_SIZE) (COPY) :
  {
    . = ALIGN(8);
    __StackSeal = .;
    . = . + 8;
    . = ALIGN(8);
  } > RAM
*/

  /* Check if data + heap + stack exceeds RAM limit */
  ASSERT(__StackLimit >= __HeapLimit, "region RAM overflowed with stack")
  ASSERT(__StackTop <= ORIGIN(RAM) + LENGTH(RAM), "RAM overflowed")
}

CFLAGS:

CFLAGS += -mcpu=$(PROCESSOR_NAME) \
          -g \
          -nostartfiles -ffreestanding \
          -mthumb \
          -ffunction-sections \
          -Wl,--gc-sections \
          -mfloat-abi=hard -Wextra --specs=rdimon.specs -fno-use-cxa-atexit

The C++ code is called from C:

#include <stdio.h>
#include "call_gtest.h"
// #include <iostream>

using namespace namespace1;

    AAA::AAA() {
    }

    int AAA::sayHi(void) {
        printf("This is working\n");
        // std::cout << "This is not working."<< std::endl;
        return 1;
    }

where the entry point is _start():

void _start(void)
{
    initialise_monitor_handles(); // required for semihosting


    int a = AAA_sayHi();

    exit_qemu("Program exiting.\n");

    while (1)
    {
        __NOP();
    }
}

The startup script is provided by CMSIS_5.

Update: I've managed to get rid of the undefined reference to '__dso_handle' error by explicitly linking crtbegin.o and crtend.o to my image (following the instructions here). This leaves me with the undefined reference to '_fini' error.

Whilst I can get rid of the error by inserting

    /* Include the list of initialization functions sorted. */
  .init_array :
  {
      crti.o(.init_array)
      KEEP (*(SORT(EXCLUDE_FILE(crti.o crtn.o) .init_array.*)))
      KEEP (*(EXCLUDE_FILE(crti.o crtn.o) .init_array))
      crtn.o(.init_array)
  }

  /* Include the list of termination functions sorted. */
  .fini_array :
  {
      crti.o(.fini_array)
      KEEP (*(SORT(EXCLUDE_FILE(crti.o crtn.o) .fini_array.*)))
      KEEP (*(EXCLUDE_FILE(crti.o crtn.o) .fini_array))
      crtn.o(.fini_array)
  }

into my linker script (I have no idea why this satisfies the linker) my call to std::cout crashes with a handfault.

I've also tried writing my own crti.c and crtn.c however the linker appears to ignore them.

cberk1
  • 35
  • 6
  • Maybe this [Where is __dso_handle defined?](https://stackoverflow.com/questions/34308720/where-is-dso-handle-defined) ? – Richard Critten Mar 08 '23 at 17:30
  • Are you compiling with -specs=nano.specs? ` etc. via semihosting` Please post all your compiler flags and a sample program. – KamilCuk Mar 08 '23 at 17:50
  • Check your compiler documentation. Usually the embedded systems compilers provide a low level function interface to implement so you can then use the entire I/O system. – Thomas Matthews Mar 08 '23 at 19:54
  • Thanks for your replies. @Richard I've already read that post (actually linked in my OP) and tried the obvious stuff. It did mention that using gcc ARM cross compiler may be problematic but doesn't go into any detail. – cberk1 Mar 09 '23 at 12:49
  • Please see the amended OP with cflags and additional source code @KamilCuk. Thanks for your time. – cberk1 Mar 09 '23 at 12:50

1 Answers1

0

I appear to have got something working now.

An per the edit, the first error, namely undefined reference to '__dso_handle' was solved by manually compiling the crtbegin.o and crtend.o like so:

Makefile:

CRTBEGIN_OBJ:=$(shell $(CC) $(CFLAGS) -print-file-name=crtbegin.o)
CRTEND_OBJ:=$(shell $(CC) $(CFLAGS) -print-file-name=crtend.o)

and listing these as dependancies for the image (see here). That left me with the undefined reference to '_fini' error. To solve this, I had to manually call the __libc_init_array() and __libc_fini_array() functions (declared as extern) in my _start function, where the _init and _fini were defined as stubs (do nothing). I did not have to alter the linker script from CMSIS_5 at all.

cberk1
  • 35
  • 6