This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

Compiler/MSP430FR5989: Why is malloc and associated functions being pulled into my build?

Part Number: MSP430FR5989


Tool/software: TI C/C++ Compiler

I'm trying to reduce some memory usage on a project using the MSP430fr5989. The project is currently a mix of c and c++. Looking at the .map file shows a number of functions used from the rts430x_lc_ld_eabi_se.lib library's memory.c file. A few caught my eye and I cannot figure out why they are included at all (see below). Particularly, malloc is included which brings in aligned_alloc. alligned_alloc is one of the largest functions in my .text section. I'd love to be able to get rid of it as I am not intentionally doing any dynamic memory allocation.

Can someone help me figure out how to remove some of these functions or at least figure out why is calling them?

.text 0 00004400 000056d6
...
00005030 00000198 rts430x_lc_ld_eabi_se.lib : memory.c.obj (.text:aligned_alloc)
...
000054a2 0000013c rts430x_lc_ld_eabi_se.lib : memory.c.obj (.text:free)
...
00006516 000000ba rts430x_lc_ld_eabi_se.lib : memory.c.obj (.text:split)
000065d0 000000b8 : fs_mpy.asm.obj (.text)
...
00008182 00000046 rts430x_lc_ld_eabi_se.lib : memory.c.obj (.text:free_list_insert)
000081c8 00000046 : fs_tou.asm.obj (.text)
...
00008d30 0000002a rts430x_lc_ld_eabi_se.lib : memory.c.obj (.text:free_list_remove)
...
00009922 0000000c rts430x_lc_ld_eabi_se.lib : memory.c.obj (.text:malloc)

  • Dustin Lane said:
    Can someone help me figure out how to remove some of these functions or at least figure out why is calling them?

    The CCS "Stack Usage" view, and the call_graph utility, display a call graph of the program as part of determining the stack usage of the program.

    A side effect is that the call graph should show what is calling malloc and associated functions.

  • Thanks Chester,
    I was able to view the "Stack Usage" view in CCS, however, it does not show any use of malloc. Suspecting that it is used somewhere in the runtime support library, I ended up using the dis430 tool on the .out file. I eventually found that malloc and free are being used by the "dtor_list.c" file in the rts library.
    Does this mean that any use of C++ objects mandates that malloc is included in the build? It still seems unnecessary to me and really hurts the usage of C++ on small memory MCU's.
    For reference, I am using compiler version ti-cgt-msp430_18.12.1.LTS

    Also, I couldn't find a call_graph utility for the msp430, at least not in the compiler's bin directory, which is the only place I know to look. Do you know if it exists and if so, where I might find it?
  • Dustin Lane said:
    Also, I couldn't find a call_graph utility for the msp430, at least not in the compiler's bin directory, which is the only place I know to look. Do you know if it exists and if so, where I might find it?

    call_graph is part of the cg_xml scripts - http://software-dl.ti.com/ccs/non-esd/releases/other/applications_packages/cg_xml/index.htm 

  • Dustin Lane said:
    I eventually found that malloc and free are being used by the "dtor_list.c" file in the rts library.

    I haven't yet investigated, but do your C++ objects contain static (global) destructors?

    From a quick look, dtor_list.c uses malloc to create a list of destructors to call atexit. Removing atexit code from rt-library on msp430 is an old thread which says while you can't prevent the the call to the atexit-related destructor registration routine for each global object with a destructor, you could minimize the added code by adding to your project a replacement function that does nothing.

  • Thanks Chester.
    I was able to use the call_graph to confirm the usage of malloc by a method in the "dtors_list.c" file, which also happens to be the function with the deepest call stack. Specifically, that method is: "__add_destruction_to_list()".

    Do you have any insight on any possible ways to get around this? Or is the use of malloc an unavoidable cost in using the msp430's C++ compiler and run-time library?
  • Ah, sorry, I just missed your previous response.

    A number of the my C++ objects are created as singletons where the object is declared with the static keyword, eg:

    ModeShutdown* ModeShutdown::GetInstance()
    {
    static ModeShutdown instance;
    return &instance;
    }

    For all of my objects, I use the default destructor. I assume this means that these objects then have a static/global destructor, would you agree?
  • I found this other old thread that seems to be addressing the same issue:
    e2e.ti.com/.../1129174

    It ultimately led to an enhancement request for the compiler, which, even though it is over 5 years old still is open. Do you have any more visibility into the status of this enhancement request: SDSCM00049623?
  • Dustin Lane said:
    I assume this means that these objects then have a static/global destructor, would you agree?

    Yes, I agree.

    Dustin Lane said:
    Do you have any more visibility into the status of this enhancement request: SDSCM00049623?

    No.

    Given that embedded programs normally don't exit, and therefore don't need the static/global destructors to run, a work-around as suggested on a previous thread is to add a stub __cxa_atexit function to your project.

    With TI MSP430 compiler v18.12.1.LTS adding the following to the main cpp source file eliminated the calls to malloc from a test program which had a static/global destructor but which otherwise didn't use dynamic memory:

    typedef void *a_dso_handle;
    typedef void  (*a_cxa_dtor_ptr)(void*);
    
    extern "C" int __cxa_atexit(a_cxa_dtor_ptr    destruction_routine,
                                void             *object,
                                a_dso_handle      dso_handle)
    {
        return 0;
    }

    Since it replaces one private function in the run-time library, it may break following an update to the compiler.

  • Thanks Chester.
    I tried compiling with that snippet and I ended up getting a symbol redefinition error for __cxa_atexit() stating that it was first defined in main.cpp and then redefined in the rts file dtor_list.c.

    I'm not sure how to get around that redefinition, but regardless this unfortunately doesn't seem like a reliable solution going forward.
  • Dustin Lane said:
    I'm not sure how to get around that redefinition, but regardless this unfortunately doesn't seem like a reliable solution going forward.

    Since dtor_list.c has multiple atexit functions defining stubs for the following might help to remove the error for your code:

    typedef void *a_dso_handle;
    typedef void  (*a_cxa_dtor_ptr)(void*);
    
    extern "C" int __cxa_atexit(a_cxa_dtor_ptr    destruction_routine,
                                void             *object,
                                a_dso_handle      dso_handle)
    {
        return 0;
    }
    
    extern "C" int __cxa_ia64_exit(a_cxa_dtor_ptr    destruction_routine,
                                   void             *object,
                                   a_dso_handle      dso_handle)
    {
        return 0;
    }
    
    extern "C" int atexit(void (*function)(void))
    {
        return 0;
    }

    I agree that having to stub out run-time library functions isn't a reliable solution going forward.

  • Unfortunately, the first enhancement request filed in this matter, with ID SDSCM00049623, was never implemented.  So, I filed the similar entry CODEGEN-6090.  You are welcome to follow it with the SDOWP link below in my signature.

    As for a workaround, I agree with this statement from Chester Gillon ...

    Chester Gillon said:
    I agree that having to stub out run-time library functions isn't a reliable solution going forward.

    Unfortunately, that is the best workaround I have been able to find.

    Thanks and regards,

    -George

  • Thanks, George. I hope that this makes it into a compiler update some time in the near future. For now, I will just have to live with it.
  • The need for malloc is pretty much just a fact of life for C++ programs with static-scope (global) objects with non-trivial destructors. The C++ standard requires us to support an arbitrary number of such objects, which implies dynamic allocation of the destruction list. In theory, if the linker could figure out that the program never exits, it could omit exit/aexit etc, but the linker just doesn't have the infrastructure we'd need to do this.  In any case, what the linker would do if it could figure out the program never exits would be to stub out __cxa_atexit, so it wouldn't look much different than if you were to stub them out yourself.
    In summary: we won't be addressing this anytime soon.  Stub out __cxa_exit if you want to avoid dragging in malloc just for C++ destructors.