EmbDev.net

Forum: ARM programming with GCC/GNU tools Setting up minimal C++ run-time environment


von Dan Q. (danq)


Rate this post
useful
not useful
I have recently installed WinARM 20070505.  Having compiled some test
code and simulated it with Insight, I am certain that things are working
correctly.

Given the following:

- I do not need the RTTI capability.
- I do not need dynamic memory (malloc,free,new,delete).
- I do not need the stream I/O functionality (fopen,iostream,etc.).

- I will need exception handling.
- I will need to code in ARM 32 bit mode (no thumb code).

My question is: "How can I configure the build/link so that the
FLASH/RAM space used is minimal?"

Currently, even the smallest test program generates a 40k+ image.  With
exceptions handling, it jumps 2k+.

So it seems to me that a lot of code is being linked in that I do not
need and there must be a way to strip it out.

Any suggestions/tips/examples would be greatly appreciated.

Cheers!

Daniel Quinz, P. Eng.

Acacetus Inc.

von Andreas Kaiser (Guest)


Rate this post
useful
not useful
This is what I use:

Compiler:
-ffunction-sections
-fdata-sections
-fno-builtin
-fno-rtti
-fno-exceptions
-fno-unwind-tables

Linker:
-nostartfiles

Be careful when using things from newlib (e.g. printf).

You need a linker script which takes care of C++ peculiarities, e.g. you
need entries *(.text.*) *(.data.*) *(.bss.*) in the respective sections,
a .ctors section for *(.ctors.*) and *(.ctors) and a dummy .dtors
sections for *(.dtors).

Also the startup initialization should call the C++ constructors.

von Dan Q. (danq)


Rate this post
useful
not useful
Andreas Kaiser wrote:
> This is what I use:
>
> Compiler:
> -ffunction-sections
> -fdata-sections
> -fno-builtin
> -fno-rtti
> -fno-exceptions
> -fno-unwind-tables

Thanks for the tip.  I added all of these except "-fno-exceptions" and
"-fno-unwind-tables" as I need exception handling.  There was no
difference in the FLASH memory, but the RAM usage went down only 8
bytes.

I have found that if I don't define the variable 'device_table_entry'
then I get an unresolved reference error to it during link.  The
functions that reference it are are:

_close_r , _read_r , _write_r , _ioctl_r

All of these are in the 'newlib_lpc' library.

Given that I make do explicit reference anywhere in my code to any of
these functions, nor do I use 'printf', etc., I do not see why these are
being linked
into the application.

I have checked the 'crt0.s' file that I use and there are no references
to these  functions in there either.

There must be a way to avoid linking in these functions if they are not
needed.

Any thoughts?

Dan

> Linker:
> -nostartfiles

Already doing this.

>
> Be careful when using things from newlib (e.g. printf).

I'm not using either (see above text on newlib linking).

> You need a linker script which takes care of C++ peculiarities, e.g. you
> need entries *(.text.*) *(.data.*) *(.bss.*) in the respective sections,
> a .ctors section for *(.ctors.*) and *(.ctors) and a dummy .dtors
> sections for *(.dtors).

Done. Works fine.

>
> Also the startup initialization should call the C++ constructors.

Done. Works fine.

von Martin T. (mthomas) (Moderator)


Rate this post
useful
not useful
Dan Quinz wrote:
> Andreas Kaiser wrote:
>> This is what I use:
>>
>> Compiler:
>> -ffunction-sections
>> -fdata-sections
>> -fno-builtin
>> -fno-rtti
>> -fno-exceptions
>> -fno-unwind-tables
>
> Thanks for the tip.  I added all of these except "-fno-exceptions" and
> "-fno-unwind-tables" as I need exception handling.  There was no
> difference in the FLASH memory, but the RAM usage went down only 8
> bytes.

Make sure to add the linker-option -gc-sections when using
-ffunction-sections and -fdata-sections. If you use the compiler
frontend (arm-elf-gcc) for linking - recommended - add this option:
-Wl,--gc-sections. If you already have some optione passed to the linker
(for exampel already -Wl entry in CFLAGS) just add --gc-sections (I
usualy use: CFLAGS += -Wl,-Map=$(TARGET).map,--cref,--gc-sections)

> I have found that if I don't define the variable 'device_table_entry'
> then I get an unresolved reference error to it during link.  The
> functions that reference it are are:
>
> _close_r , _read_r , _write_r , _ioctl_r
>
> All of these are in the 'newlib_lpc' library.

The newlib-lpc provides the syscalls for the newlib and so indirectly
for the libstdc++.a. newlib-lpc needs the device_table. The use has to
provide it. I'm not sure why you get an error about "device_table_entry"
since this is basicly just a struct defined in dev_ctrl.h.

> Given that I make do explicit reference anywhere in my code to any of
> these functions, nor do I use 'printf', etc., I do not see why these are
> being linked
> into the application.

As far as I know the C++ stdlib depends on stdio-functions but I'm not
sure if this can be disabled somehow. A map-file should provide more
information which functions of the library depend on others. The main
reason for the large binary size might be that the C++ stdlib "pulls in"
the complete stdio-support including floating-point (IRC this is around
30kBytes for "plain" newlib without C++).

> I have checked the 'crt0.s' file that I use and there are no references
> to these  functions in there either.

Maybe the dependencies are "hidden" and from another library like
libstdc++.a.

> There must be a way to avoid linking in these functions if they are not
> needed.

Maybe there is some options like the ones for RTTI but I have not found
it yet. Maybe the Toolchain (esp. the C++ stdlib) has to be rebuild with
special options.

Sorry, a lot of "maybes" and no real solution but hopefully some useful
hints.

Martin Thomas

von Martin T. (mthomas) (Moderator)


Rate this post
useful
not useful
Check Section "Reducing the Overhead of C++" from
http://www.embedded.com/columns/technicalinsights/201001729

von Andreas Kaiser (Guest)


Rate this post
useful
not useful
> Given that I make do explicit reference anywhere in my code to any of
> these functions, nor do I use 'printf', etc., I do not see why these are
> being linked into the application.

Temporarily remove/rename the newlib. The linker should tell you.

von Dan Q. (danq)


Rate this post
useful
not useful
Thanks Martin,

I added the options and recompiled.  The resulting code reduction was
about 4500 bytes.  Little to no effect on the RAM usage (10 bytes), but
I am happier.

I will continue to research a bit more into what the newlib_lpc library
is doing as it seems this is the culprit for the external references.

Cheers!

Dan


Martin Thomas wrote:
> Dan Quinz wrote:
>> Andreas Kaiser wrote:
>>> This is what I use:
>>>
>>> Compiler:
>>> -ffunction-sections
>>> -fdata-sections
>>> -fno-builtin
>>> -fno-rtti
>>> -fno-exceptions
>>> -fno-unwind-tables
>>
>> Thanks for the tip.  I added all of these except "-fno-exceptions" and
>> "-fno-unwind-tables" as I need exception handling.  There was no
>> difference in the FLASH memory, but the RAM usage went down only 8
>> bytes.
>
> Make sure to add the linker-option -gc-sections when using
> -ffunction-sections and -fdata-sections. If you use the compiler
> frontend (arm-elf-gcc) for linking - recommended - add this option:
> -Wl,--gc-sections. If you already have some optione passed to the linker
> (for exampel already -Wl entry in CFLAGS) just add --gc-sections (I
> usualy use: CFLAGS += -Wl,-Map=$(TARGET).map,--cref,--gc-sections)
>
>> I have found that if I don't define the variable 'device_table_entry'
>> then I get an unresolved reference error to it during link.  The
>> functions that reference it are are:
>>
>> _close_r , _read_r , _write_r , _ioctl_r
>>
>> All of these are in the 'newlib_lpc' library.
>
> The newlib-lpc provides the syscalls for the newlib and so indirectly
> for the libstdc++.a. newlib-lpc needs the device_table. The use has to
> provide it. I'm not sure why you get an error about "device_table_entry"
> since this is basicly just a struct defined in dev_ctrl.h.
>
>> Given that I make do explicit reference anywhere in my code to any of
>> these functions, nor do I use 'printf', etc., I do not see why these are
>> being linked
>> into the application.
>
> As far as I know the C++ stdlib depends on stdio-functions but I'm not
> sure if this can be disabled somehow. A map-file should provide more
> information which functions of the library depend on others. The main
> reason for the large binary size might be that the C++ stdlib "pulls in"
> the complete stdio-support including floating-point (IRC this is around
> 30kBytes for "plain" newlib without C++).
>
>> I have checked the 'crt0.s' file that I use and there are no references
>> to these  functions in there either.
>
> Maybe the dependencies are "hidden" and from another library like
> libstdc++.a.
>
>> There must be a way to avoid linking in these functions if they are not
>> needed.
>
> Maybe there is some options like the ones for RTTI but I have not found
> it yet. Maybe the Toolchain (esp. the C++ stdlib) has to be rebuild with
> special options.
>
> Sorry, a lot of "maybes" and no real solution but hopefully some useful
> hints.
>
> Martin Thomas

von Dan Q. (danq)


Rate this post
useful
not useful
Thanks again Martin.

I read this article before and I have added the dummy replacements for
malloc/free/new/delete mad the atexit func.  The net effect is a code
reduction of about 100 bytes, so I'm still not near to removing the
"bottom of the iceberg".

As stated before, I will continue to dig into the libraries to try to
get insight as to what exactly is being linked in and where.

Thanks for your help.

Cheers!

Dan


Martin Thomas wrote:
> Check Section "Reducing the Overhead of C++" from
> http://www.embedded.com/columns/technicalinsights/201001729

von Dan Q. (danq)


Rate this post
useful
not useful
Thanks to all for your feedback.  I apprecite it!

Cheers!

Dan

von Jim K. (ancaritha)


Rate this post
useful
not useful
Dan Quinz wrote:
> I read this article before and I have added the dummy replacements for
> malloc/free/new/delete mad the atexit func.  The net effect is a code
> reduction of about 100 bytes, so I'm still not near to removing the
> "bottom of the iceberg".


The article helped me reduce my code a little bit as well, but I'd also
like to get some more out of it.  Is there anyway to stop it from
pulling in an entire library, or at least make it intelligently use my
function and not include it from the library?  For example

From my map file:

0x534
c:/winarm/bin/../lib/gcc/arm-elf/4.1.2/../../../../arm-elf/lib/thumb/int 
erwork\libc.a(lib_a-mallocr.o)
                0x000178a0                _malloc_r

If I attempt to compile the following to override the library function:
extern "C" void *_malloc_r(struct _reent *, size_t) {return (void *)0; }

I get this error:
1>c:/winarm/bin/../lib/gcc/arm-elf/4.1.2/../../../../arm-elf/lib/thumb/i 
nterwork\libc.a(lib_a-mallocr.o):
In function `_malloc_r':
1>mallocr.c:(.text+0x0): multiple definition of `_malloc_r'
1>.out/Reduced_Newlib.o:../../../../src/HwDrivers/ARM/Reduced_Newlib.cpp 
:31:
first defined here
1>c:/winarm/bin/../lib/gcc/arm-elf/4.1.2/../../../../arm-elf/bin/ld.exe:
Warning: size of symbol `_malloc_r' changed from 4 in
.out/Reduced_Newlib.o to 1332 in
c:/winarm/bin/../lib/gcc/arm-elf/4.1.2/../../../../arm-elf/lib/thumb/int 
erwork\libc.a(lib_a-mallocr.o)


I am not using any sort of malloc, or free or anything of that sort in
my code, and it would be great if it stopped including these
libraries...

von Jim K. (ancaritha)


Rate this post
useful
not useful
Jim Kaz wrote:
> If I attempt to compile the following to override the library function:
> extern "C" void *_malloc_r(struct _reent *, size_t) {return (void *)0; }

Well, I'm not entirely sure what I changed, but while overriding other
functions, I decided to try _malloc_r again to see if I could get it
working at all.  The following works.  Looks similar to what I posted
before huh?  Yea, I dunno?  shrug

extern "C" void *_malloc_r(struct _reent *, size_t) {return (void *)0; }

von Jim K. (ancaritha)


Rate this post
useful
not useful
Dan Quinz wrote:
> I read this article before and I have added the dummy replacements for
> malloc/free/new/delete mad the atexit func.  The net effect is a code
> reduction of about 100 bytes, so I'm still not near to removing the
> "bottom of the iceberg".


After playing around with my code some more, here are some more things
that you can override:

extern "C" void *_malloc_r(struct _reent *, size_t) {return (void *)0; }
extern "C" void _free_r(struct _reent *, void*) {}

extern "C" void *_realloc_r(struct _reent *, void*, size_t) {return
(void *)0; };
extern "C" void *realloc(void *, size_t) {return (void *)0; };

extern "C" void *_calloc_r(struct _reent *, size_t, size_t) {return
(void *)0; };

These functions should entirely remove malloc.o from your map file and
will drop your flash usage by another couple kilobytes, and should drop
your RAM by about a kilobyte.

Additionally, if you don't use any string manipulation:
extern "C" int _vfprintf_r(struct _reent *, FILE*, const char*, void*)
{return 0;}

That frees up about 14K of flash, maybe 100 bytes of RAM.


I'm looking for a way to remove alloc.o, which means I need to override
__cxa_free_exception and _cxa_allocate_exception, but I'm having
difficulty in finding their declarations.  This will free up a few
hundred bytes of flash and around a K or 2 of RAM.


Thanks for posting that article Martin!  Its been a big help.  I never
knew how to override the functions that got linked in, and this showed
me exactly how to do it.  Thanks!

von Jim K. (ancaritha)


Rate this post
useful
not useful
Something I came across.  The article does __aeabi_atexit, where as I
think we want to use __cxa_atexit, since we are using GCC.  In addition,
we need to use the -fuse-cxa-atexit flag in the compiler.  I have yet to
actually get this to work though, since I keep getting the following
error:

1>.out/HwMemSystem.o: In function
`__static_initialization_and_destruction_0':
1>../../../../src/MemSystem/HwMemSystem.cpp:145: undefined reference to
`__dso_handle'


Additionally, adding the -fno-enforce-eh-specs flag seems like it should
disabled the 'eh_' library includes, which would include eh_alloc, thus
freeing up about 2K of RAM, but it doesn't seem to do that.  The output
file is exactly the same size, sigh.


Hopefully some of my digging will be useful to you Dan.

von Dan Q. (danq)


Rate this post
useful
not useful
Thanks Jim!

Your redefinitions for the memory allocation routined helped quite a
bit!  I dropped the flash down by 4228 bytes and the ram by 1040.

There was no effect when I redefined the vprintf function though.

I also experimented with removing the global 'device_table_entry' and
get the following linker errors:

Code:

#if 0

const struct device_table_entry * device_table[] =
{
  & com1 ,  // stdin
  & com1 ,  // stdout
  & com1 ,  // stderr
            //
  0
} ;

#endif

c:/winarm.20070505/bin/../lib/gcc/arm-elf/4.1.2/../../../../arm-elf/lib\ 
libnewlib-lpc.a(_close_r.o):
In function `_close_r':
C:\WinARM\utils\newlib_lpc_rel5a_src/_close_r.c:71: undefined reference
to `device_table'
c:/winarm.20070505/bin/../lib/gcc/arm-elf/4.1.2/../../../../arm-elf/lib\ 
libnewlib-lpc.a(_read_r.o):
In function `_read_r':
C:\WinARM\utils\newlib_lpc_rel5a_src/_read_r.c:84: undefined reference
to `device_table'
c:/winarm.20070505/bin/../lib/gcc/arm-elf/4.1.2/../../../../arm-elf/lib\ 
libnewlib-lpc.a(_write_r.o):
In function `_write_r':
C:\WinARM\utils\newlib_lpc_rel5a_src/_write_r.c:103: undefined reference
to `device_table'
c:/winarm.20070505/bin/../lib/gcc/arm-elf/4.1.2/../../../../arm-elf/lib\ 
libnewlib-lpc.a(_ioctl_r.o):
In function `_ioctl_r':
C:\WinARM\utils\newlib_lpc_rel5a_src/_ioctl_r.c:75: undefined reference
to `device_table'

So it eppears there is something in one of the libraries that is causing
the stdio stuff to be linked in.  I don't use any of it explicitly, so
it must be coming from the libraries somewhere.

Thanks for sharing your digging.

Cheers!

Dan



Jim Kaz wrote:
> Something I came across.  The article does __aeabi_atexit, where as I
> think we want to use __cxa_atexit, since we are using GCC.  In addition,
> we need to use the -fuse-cxa-atexit flag in the compiler.  I have yet to
> actually get this to work though, since I keep getting the following
> error:
>
> 1>.out/HwMemSystem.o: In function
> `__static_initialization_and_destruction_0':
> 1>../../../../src/MemSystem/HwMemSystem.cpp:145: undefined reference to
> `__dso_handle'
>
>
> Additionally, adding the -fno-enforce-eh-specs flag seems like it should
> disabled the 'eh_' library includes, which would include eh_alloc, thus
> freeing up about 2K of RAM, but it doesn't seem to do that.  The output
> file is exactly the same size, sigh.
>
>
> Hopefully some of my digging will be useful to you Dan.

Please log in before posting. Registration is free and takes only a minute.
Existing account
Do you have a Google/GoogleMail account? No registration required!
Log in with Google account
No account? Register here.