This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

Cortex-M0: What's included in the binary?

Hi,

I have two M0 projects.  The first project has only one assembly file startup.s.  The second project has one assembly file and one C file.  When I checked the memory map file of the second project (shown below), there are some extra object codes included in the final binary in addition to startup.o and a.o.  I presume they are related to C libararies.  Can someone describe what they are for?  Are these libaries required?  If not required do we have a way to exclude them?

Thanks a lot.

--- Memory map of project 1: startup.s ----

Memory Map of the image

  Image Entry point : 0x00000041

  Load Region LR_1 (Base: 0x00000000, Size: 0x0000015c, Max: 0xffffffff, ABSOLUTE)

    Execution Region ER_RO (Base: 0x00000000, Size: 0x0000015c, Max: 0xffffffff, ABSOLUTE)

    Base Addr    Size         Type   Attr      Idx    E Section Name        Object

    0x00000000   0x00000040   Data   RO            3    RESET               startup.o
    0x00000040   0x0000011c   Code   RO            4  * .text               startup.o

--- Memory map of project 2: startup.s & a.c ---

Memory Map of the image

  Image Entry point : 0x0000020d

  Load Region LR_1 (Base: 0x00000000, Size: 0x0000027c, Max: 0xffffffff, ABSOLUTE)

    Execution Region ER_RO (Base: 0x00000000, Size: 0x00000274, Max: 0xffffffff, ABSOLUTE)

    Base Addr    Size         Type   Attr      Idx    E Section Name        Object

    0x00000000   0x0000004c   Data   RO           20    RESET               startup.o
    0x0000004c   0x00000008   Code   RO           30  * !!!main             c_p.l(__main.o)
    0x00000054   0x0000003c   Code   RO          195    !!!scatter          c_p.l(__scatter.o)
    0x00000090   0x0000001a   Code   RO          197    !!handler_copy      c_p.l(__scatter_copy.o)
    0x000000aa   0x00000002   PAD
    0x000000ac   0x0000001c   Code   RO          199    !!handler_zi        c_p.l(__scatter_zi.o)
    0x000000c8   0x00000006   Code   RO           68    .ARM.Collect$$libinit$$00000000  c_p.l(libinit.o)
    0x000000ce   0x00000000   Code   RO           77    .ARM.Collect$$libinit$$00000006  c_p.l(libinit2.o)
    0x000000ce   0x00000000   Code   RO           79    .ARM.Collect$$libinit$$00000008  c_p.l(libinit2.o)
    0x000000ce   0x00000000   Code   RO           81    .ARM.Collect$$libinit$$0000000A  c_p.l(libinit2.o)
    0x000000ce   0x00000000   Code   RO           84    .ARM.Collect$$libinit$$0000000D  c_p.l(libinit2.o)
    0x000000ce   0x00000000   Code   RO           86    .ARM.Collect$$libinit$$0000000F  c_p.l(libinit2.o)
    0x000000ce   0x00000000   Code   RO           88    .ARM.Collect$$libinit$$00000011  c_p.l(libinit2.o)
    0x000000ce   0x00000000   Code   RO           90    .ARM.Collect$$libinit$$00000013  c_p.l(libinit2.o)
    0x000000ce   0x00000000   Code   RO           92    .ARM.Collect$$libinit$$00000015  c_p.l(libinit2.o)
    0x000000ce   0x00000000   Code   RO           94    .ARM.Collect$$libinit$$00000017  c_p.l(libinit2.o)
    0x000000ce   0x00000000   Code   RO           96    .ARM.Collect$$libinit$$00000019  c_p.l(libinit2.o)
    0x000000ce   0x00000000   Code   RO           98    .ARM.Collect$$libinit$$0000001B  c_p.l(libinit2.o)
    0x000000ce   0x00000000   Code   RO          100    .ARM.Collect$$libinit$$0000001D  c_p.l(libinit2.o)
    0x000000ce   0x00000000   Code   RO          102    .ARM.Collect$$libinit$$0000001F  c_p.l(libinit2.o)
    0x000000ce   0x00000000   Code   RO          104    .ARM.Collect$$libinit$$00000021  c_p.l(libinit2.o)
    0x000000ce   0x00000000   Code   RO          108    .ARM.Collect$$libinit$$00000028  c_p.l(libinit2.o)
    0x000000ce   0x00000000   Code   RO          110    .ARM.Collect$$libinit$$0000002A  c_p.l(libinit2.o)
    0x000000ce   0x00000000   Code   RO          112    .ARM.Collect$$libinit$$0000002C  c_p.l(libinit2.o)
    0x000000ce   0x00000000   Code   RO          114    .ARM.Collect$$libinit$$0000002E  c_p.l(libinit2.o)
    0x000000ce   0x00000002   Code   RO          115    .ARM.Collect$$libinit$$0000002F  c_p.l(libinit2.o)
    0x000000d0   0x00000002   Code   RO          136    .ARM.Collect$$libshutdown$$00000000  c_p.l(libshutdown.o)
    0x000000d2   0x00000000   Code   RO          150    .ARM.Collect$$libshutdown$$00000003  c_p.l(libshutdown2.o)
    0x000000d2   0x00000000   Code   RO          153    .ARM.Collect$$libshutdown$$00000006  c_p.l(libshutdown2.o)
    0x000000d2   0x00000000   Code   RO          156    .ARM.Collect$$libshutdown$$00000009  c_p.l(libshutdown2.o)
    0x000000d2   0x00000000   Code   RO          158    .ARM.Collect$$libshutdown$$0000000B  c_p.l(libshutdown2.o)
    0x000000d2   0x00000000   Code   RO          161    .ARM.Collect$$libshutdown$$0000000E  c_p.l(libshutdown2.o)
    0x000000d2   0x00000002   Code   RO          162    .ARM.Collect$$libshutdown$$0000000F  c_p.l(libshutdown2.o)
    0x000000d4   0x00000000   Code   RO           32    .ARM.Collect$$rtentry$$00000000  c_p.l(rtentry.o)
    0x000000d4   0x00000000   Code   RO           35    .ARM.Collect$$rtentry$$00000002  c_p.l(rtentry2.o)
    0x000000d4   0x00000004   Code   RO           52    .ARM.Collect$$rtentry$$00000005  c_p.l(rtentry5.o)
    0x000000d8   0x00000000   Code   RO           37    .ARM.Collect$$rtentry$$00000009  c_p.l(rtentry2.o)
    0x000000d8   0x00000004   Code   RO           38    .ARM.Collect$$rtentry$$0000000A  c_p.l(rtentry2.o)
    0x000000dc   0x00000000   Code   RO           40    .ARM.Collect$$rtentry$$0000000C  c_p.l(rtentry2.o)
    0x000000dc   0x00000008   Code   RO           41    .ARM.Collect$$rtentry$$0000000D  c_p.l(rtentry2.o)
    0x000000e4   0x00000004   Code   RO           53    .ARM.Collect$$rtentry$$00002716  c_p.l(rtentry5.o)
    0x000000e8   0x00000002   Code   RO           73    .ARM.Collect$$rtexit$$00000000  c_p.l(rtexit.o)
    0x000000ea   0x00000000   Code   RO          119    .ARM.Collect$$rtexit$$00000002  c_p.l(rtexit2.o)
    0x000000ea   0x00000004   Code   RO          120    .ARM.Collect$$rtexit$$00000003  c_p.l(rtexit2.o)
    0x000000ee   0x00000006   Code   RO          121    .ARM.Collect$$rtexit$$00000004  c_p.l(rtexit2.o)
    0x000000f4   0x00000118   Code   RO            1    .text               a.o
    0x0000020c   0x00000024   Code   RO           21  * .text               startup.o
    0x00000230   0x00000006   Code   RO           28    .text               c_p.l(heapauxi.o)
    0x00000236   0x0000000c   Code   RO           60    .text               c_p.l(exit.o)
    0x00000242   0x00000002   PAD
    0x00000244   0x0000000c   Code   RO          128    .text               c_p.l(sys_exit.o)
    0x00000250   0x00000002   Code   RO          139    .text               c_p.l(use_no_semi.o)
    0x00000252   0x00000000   Code   RO          141    .text               c_p.l(indicate_semi.o)
    0x00000252   0x00000002   PAD
    0x00000254   0x00000020   Data   RO          193    Region$$Table       anon$$obj.o

  • I always avoid things like "printf". printf uses fprintf and snprintf. fprintf needs fwrite, and fwrite means including all file I/O, including fopen, fread, fclose, fseek, etc. Usually it means I get a huge library, so instead, I either roll my own low-level routine that just sends a character over the UART for instance, and then another routine, which takes a string and send the string's characters one-by-one using the other routine.

    Looking at the above, I see some exit and libshutdown references; that's completely redundant, because you never really exit your program on a microcontroller anyway - you would usually stay in a 'forever' while-loop. (Remember to put a __WFI(); in the loop, to save power, if you can).

    The standard C library probably also wants malloc and free, two things that are not appropriate for most Cortex-M0 (unless they're combined with a Cortex-M4 for instance, like the NXP LPC43xx or LPC541xx; these have plenty of space).

    So what I'd do in your place, would be to take a look at newlib nano. It's a smaller version of the standard C library, which is much more useful on smaller devices.

    But I'd also try to avoid using functions that need the C-library for as long as possible.

    -But if you at some point already need the standard library, then using it will not cost much extra. If you already must have printf, just in one place, keep using it, because you might be able to save space, when compared to rolling your own.

    There are other library functions, which takes up a lot of space. For instance if you're using math multiplication or division. Mulitplication isn't really that bad, but dividing a 32-bit value by a 32-bit value producing a remainder and a quotient will generate a medium sized subroutine.

    If you attempt to use floating point, then you'll get huge and very slow code. Avoid floating point, at almost all cost.

    Normally, you'll need only integer math, it's also often much more precise (32-bit floating point only has 24-bit precision for instance).

  • jensbauer wrote:

    I always avoid things like "printf". printf uses fprintf and snprintf. fprintf needs fwrite, and fwrite means including all file I/O, including fopen, fread, fclose, fseek, etc. Usually it means I get a huge library, so instead, I either roll my own low-level routine that just sends a character over the UART for instance, and then another routine, which takes a string and send the string's characters one-by-one using the other routine.

    Does using ARM Microlib help in reducing the size?

  • I do not know the ARM Microlib, but from the sound of the name, I would expect so.

    You can try building with the normal libraries and then switch to ARM Microlib and see what the difference is.

    Making a simple printf program will probably show a significant difference, but malloc alone is probably also a good library function to test with.

  • There is something here. Will try it out sometime.

    Thanks,

  • Hi wshen,

    I think the 2nd case includes another startup file which had been provided by the compiler. Are the procedure of compiling for both projects same? If you use GCC, how about adding the '-nostdlib' option to the command line?

    Best regards,
    Yasuhiko Koumoto.

  • It turns out that main() function will include these C library initialization functions to be included as part of __rt_lib_init automatically.  So if your program doesn't have main() function, such C functions won't be included.

  • Well, would be good to know more about your sources, compiler and settings. Overall, your binary isn't very large, so the library calls aren't consuming much memory. When using GCC (you didn't say which compiler you're using) you typically have the memory initialisation at the beginning, in startup.s like below. This called from your reset handler and subsequently jumps to your main code.

    Using a regular printf would be very heavy, as others have mentioned. From your code size ( Size: 0x0000027c, ) I doubt you're using it. I'm not familiar with the ARM micro library (but with newlib) and there are useful free small printf implementations around.

    000004f0 <Default_Reset_Handler>:

    {

      /* Initialize data and bss */

      unsigned long *pulDest;

      /* Zero fill the bss segment. */

      for(pulDest = &_sbss; pulDest < &_ebss; )

         4f0:    4809          ldr    r0, [pc, #36]    ; (518 <Default_Reset_Handler+0x28>)

         4f2:    4b0a          ldr    r3, [pc, #40]    ; (51c <Default_Reset_Handler+0x2c>)

      * @param  None

      * @retval None

      */

      // must be optimized to speed up clearing bss

    __attribute__ ((no_instrument_function)) __attribute__((optimize("O2"))) void Default_Reset_Handler(void)

    {

         4f4:    b510          push    {r4, lr}

      /* Initialize data and bss */

      unsigned long *pulDest;

      /* Zero fill the bss segment. */

      for(pulDest = &_sbss; pulDest < &_ebss; )

         4f6:    4298          cmp    r0, r3

         4f8:    d20b          bcs.n    512 <Default_Reset_Handler+0x22>

         4fa:    43c1          mvns    r1, r0

         4fc:    2200          movs    r2, #0

      {

        *(pulDest++) = 0;

         4fe:    2400          movs    r4, #0

         500:    18cb          adds    r3, r1, r3

         502:    089b          lsrs    r3, r3, #2

         504:    3301          adds    r3, #1

         506:    009b          lsls    r3, r3, #2

         508:    1881          adds    r1, r0, r2

         50a:    3204          adds    r2, #4

         50c:    600c          str    r4, [r1, #0]

    {

      /* Initialize data and bss */

      unsigned long *pulDest;

      /* Zero fill the bss segment. */

      for(pulDest = &_sbss; pulDest < &_ebss; )

         50e:    429a          cmp    r2, r3

         510:    d1fa          bne.n    508 <Default_Reset_Handler+0x18>

      /* Setup the microcontroller system. */

      //SystemInit();

      /* Call the application's entry point.*/

      main();

         512:    f7ff fe1b     bl    14c <main>

      // main shouldn't return

      while (1);

         516:    e7fe          b.n    516 <Default_Reset_Handler+0x26>

         518:    00008e58     .word    0x00008e58

         51c:    0000a374     .word    0x0000a374

  • In fact, it's not the job of the 'main()' function, but the job of the RESET_Handler to initialize the C-libraries.

    Such initialization code is normally put in crt.c or startup.c (or crt.s or startup.s).

    Some code from the C-libraries might be included, even when unused (eg. never called), when you use GCC.

    On Cortex-M0, it's often preferred to omit such code, as it's huge compared to the size of the program space available.