-
Notifications
You must be signed in to change notification settings - Fork 131
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Static object instances with C++ destructors depend on atexit and eat up RAM #72
Comments
Surprisingly there is an easy fix for this: __register_exitproc is a weak symbol and if you override it then it doesn't pull in the big data structures. So I put the following definition into my main.cpp file and I reclaim back about 400 bytes RAM:
@finneyj do you think that's the best approach? I can do it in the MicroPython code so it doesn't need to be part of the DAL. |
thanks @dpgeorge - looks good. We should consider if there would be any gains by putting this in the DAL side for other languages when we have a moment, but this sounds like a good approach - suggest you go ahead wit this for now. We're running on minutes ot hit other deadlines at the moment (keep the bug reports coming though!) |
Micro:bit programs never exit, so I'm sure all languages would be delighted to have the 400 bytes back. Seems like a no-brainer to me! |
True that main() never returns. Technically I can write code in app_main() that calls exit(), but not sure I can see a reason to ever do this though. I'd certainly like the extra 400 bytes, as to squeeze in the enormous S130 BLE stack I'm down to a heap of only 512 bytes at the moment, and regularly hit out-of-memory conditions. @dpgeorge, thanks for pointing this out. If I upgrade my toolchain does this result in overall more available memory? Is there any other compelling reason to upgrade my toolchain? |
Absolutely - we definitely all would like to see more memory! Apologies for any confusion. I was just trying to say that James and I have a time critical backlog still to work through, so testing this through on micropython then upstreaming if all seems well felt like an easy win. @mmoskal, @remay, if either of you guys have a few spare cycles and can try this out, measure how much memory it does free up and do a few basic smoke tests i'd love to see pull request. |
You'll only get about 140 bytes back if you're using a non-bleeding-edge version of the toolchain. Still, that's relatively a lot. |
@dpgeorge I've recently tried experimenting with this, and couldn't find the What are the steps to reproduce? |
It depends heavily on your compiler and C++ library. The functions may be called something else. Look through the .map file and find everything that's in the BSS. They will be named and you can see if there is anything suspiciously large. |
Hi @dpgeorge, thanks for that, I believe i have found my equivalent resident at exit overhead:
This is on GCC The way I managed to remove this symbol was to provide a barebones
This overrides the usual symbol, and doesn't perform the step that generates I've never really delved this much into GCC before, so i'm unable to conclude whether my solution is correct... I feel that having different solutions is a bad thing, and we should endeavour to find a solution that covers the general case. |
I don't think that's possible, due to these functions ( The alternative is to not use destructors, or have separate classes for objects that are statically allocated. |
@dpgeorge is a simpler option to just override I've been doing some investigation, and I noticed that the LPC platforms on mbed override this in their startup code. I've replicated this in a main.cpp, and it removes the 264 byte RAM overhead:
Does this work with your configuration? |
Nope. |
@dpgeorge Can you verify that your .bss section actually changes size based on the application of your symbol definition? Even though the symbol disappears from my .map file, the overall bss for the hex does not change. Here is my sample program:
With the extern'd symbols, my
And without:
|
Using the same example program that you have (from a fresh clone of microbit-samples) I get:
So, yes, there is a difference. A saving of nearly 500 bytes code and 308 bytes RAM. gcc version: arm-none-eabi-gcc (Arch Repository) 5.3.0 |
@jamesadevine @dpgeorge I wonder if the 'Arch Repository' part is the key here? Is your newlib configured differently there? I'm getting my toolchains from https://launchpad.net/gcc-arm-embedded - James, what about you? |
@jaustin same! |
The newlib package is automatically installed for me. Pacman reports: local/arm-none-eabi-newlib 2.4.0-1 |
@dpgeorge Here's a reply, courtesy of @jaustin
|
I recently upgraded my arm-none-eabi-gcc toolchain and now I have a new libc_nano.a library. With this new library the MicroPython port runs out of memory (again) because libc_nano.a includes a new 264 byte structure in the bss.
This new structure is for handling atexit behaviour. They (newlib devs) recently changed atexit so that it uses static RAM to register on-exit functions, instead of dynamically allocating with malloc. See here for background: https://sourceware.org/ml/newlib/2015/msg00936.html
atexit is used by the DAL because it has static object instances which have C++ destructors (eg ManagedString::EmptyString, MicroBitImage::EmtpyImage). When the program exits these destructors should be called (according to the C++ standard). Therefore when these static instances are initialised they register a function with atexit (cxa_atexit via __aeabi_atexit). The big 264 byte data structure in the bss is used to store these function pointers.
Note that even before this change there was 140 bytes used by atexit in the bss. So now there is 140+264 = way too much!
It seems this is a hard problem to solve/workaround. If possible it would be good to provide a dummy implementation of cxa_atexit, or __aeabi_atexit, which overrides the one from libc_nano.a.
Thoughts?
The text was updated successfully, but these errors were encountered: