1

I wanted to write a piece of code in C for my Stellaris launchpad just to turn on the onboard LED by keeping the library usage to minimum. To my surprise, the compiled code was around 800 bytes in size. So to check what was put in to the compiled code by the compiler, I checked the assembly code using a dissambler. It had a lot of code which I didn't write the C code for. I would like to know what those codes are for and how did it enter the compiler setting. I am trying to learn how a compiler behaves and what behind-the-scenes things the compiler is doing. Please help me.

This is my C program:

#include "inc/hw_memmap.h"
#include "inc/hw_types.h"
#include "driverlib/rom.h"
#include "driverlib/sysctl.h"

#define GPIOFBASE   0x40025000
#define GPIOCLK     *((volatile unsigned long *)(0x400FE000 + 0x608))
#define FDIR        *((volatile unsigned long *)(GPIOFBASE + 0x400))
#define FDEN        *((volatile unsigned long *)(GPIOFBASE + 0x51C))
#define FDATA       *((volatile unsigned long *)(GPIOFBASE + 0x3FF))

void main(void) {

    ROM_SysCtlClockSet(SYSCTL_SYSDIV_4 | SYSCTL_USE_PLL | SYSCTL_XTAL_16MHZ | SYSCTL_OSC_MAIN);

    GPIOCLK |= (1<<5);
    FDIR    |=  0xE;
    FDEN    |=  0xE;
    FDATA   |=  0xE;

while (1);

}

The only API call I used was to set the Clock setting using a Onchip ROM library. Please check the dissambly code at this pastebin: (The main: is at 0x190.)

http://pastebin.com/wNNsBdtT

caf
  • 233,326
  • 40
  • 323
  • 462
Akhil P Oommen
  • 215
  • 4
  • 11
  • 2
    There's no such thing as `void main` in C. – Cubic Dec 02 '12 at 20:41
  • if I put 'int main()', does that will change the resultant output in any way in this code? – Akhil P Oommen Dec 02 '12 at 20:43
  • Are you compiling with optimisation enabled/disabled ? Debug symbols enabled ? Stack frames always generated ? – Paul R Dec 02 '12 at 20:46
  • Check for either crt0.s or crt0.c; somebody needs to setup the stack, clear bss and initialize uart/other interrupt vectors in a hosted implementation too. – Aki Suihkonen Dec 02 '12 at 20:46
  • main is an arbitrary name, not required at all for C based embedded (bare metal) applications and you can certainly define it any way you like. If you are using an operating system that is a different story. – old_timer Dec 02 '12 at 20:53
  • 1
    the compiler/linker may see the word main() and add extra stuff, use some other name (and your own bootstrap) to avoid that problem. github.com/dwelch67 has many bare metal arm/thumb microcontroller examples in particular how to get from reset/boot to the first C function. – old_timer Dec 02 '12 at 20:55
  • 1
    Just to clarify the terminology here: a *hosted* implementation is the usual sort that runs on an OS. One like this that runs on "bare metal" is a *freestanding* implementation. – Jerry Coffin Dec 02 '12 at 21:07
  • @PaulR this output is with the optimization disabled. Setting optimization level to 4 doesn't change the output much, saved only a few out of 800 bytes. – Akhil P Oommen Dec 02 '12 at 21:12
  • @H2CO3, you mean a freestanding implementation, as we have it here, no? – Jens Gustedt Dec 02 '12 at 21:15
  • @JensGustedt Of course I do. I was mixing up the two. Sorry for that. I'm removing that comment. –  Dec 02 '12 at 21:16
  • @JerryCoffin there are lot of repositories at that page. Do you mean the Rasp pi bare metal code posted there? – Akhil P Oommen Dec 02 '12 at 21:22
  • 1
    How are you measuring the size? Is this the code size reported by the linker or are you just looking at the output object file size? That is likley to be misleading in the extreme. – Clifford Dec 02 '12 at 21:37
  • The linker is not printing the sizes in the console output. Which parameter should I pass for that? But obviously there are lot of assembly for which I didn't write the C code. – Akhil P Oommen Dec 02 '12 at 21:57
  • In addition to the explanation of initialization code, if you use any provided libraries at all, you may want to be sure that the unused code in them is being stripped out of the final result and only the needed parts retained. For example, using the LD that comes with gcc, you would want the --gc-sections option. – Chris Stratton Dec 02 '12 at 22:12
  • The toolchain that comes with the Stellaris IDE uses TM470 compiler, not gcc. – Akhil P Oommen Dec 02 '12 at 22:17
  • @Cubic - C99 provides leeway for implementation-defined forms of main, although I don't know whether it applies only to certain kinds of implementation (ie. hosted vs. freestanding). – detly Dec 02 '12 at 22:19
  • @Cubic You are wrong. [Read this](http://stackoverflow.com/questions/5296163/why-is-the-type-of-the-main-function-in-c-and-c-left-to-the-user-to-define/5296593#5296593). The OP has labelled this as a freestanding system, so the definition of main() is perfectly fine. – Lundin Dec 03 '12 at 07:47
  • However, the presence of \ inside an #include is undefined behavior (C11 6.4.7/3). – Lundin Dec 03 '12 at 07:51
  • @AkhilPOommen regarding how to get the liker to report code size - normally you simply instruct it to generate a map file. The file will not only contain the code segment size, but also provide information about exactly what is linked and how bit each object code module linked is. – Clifford Dec 03 '12 at 22:14
  • @PaulR I checked the size of the output bin file. Its still around 800 bytes. I guess the bin file is the ultimate minimum output without any debug symbols or anything of that sorts. – Akhil P Oommen Dec 04 '12 at 07:08
  • @dwelch I read your stuff on the github repo. Its really fantastic. I was actually looking for something like that. Thanks for pointing me there. Things are making lot of sense to me now. Maybe because I come from electronics background, I am missing all these basic fundamental things. – Akhil P Oommen Dec 04 '12 at 07:12
  • I appreciate the feedback, thanks. I am also from an electronics background and self taught programmer. – old_timer Dec 04 '12 at 15:21
  • @dwelch I would like to know more about what you posted in Baremetal directory in raspb repo. I want to know more about things like compilers, linkers, elf format, linker scripts etc. I can understand the architecture and working of a microcontrollers from its datasheet. Now I want to how the tool we use like compilers etc works or behave. Can you point me to some online resources. Like you did, resources that discuss with practical examples would be better. – Akhil P Oommen Dec 09 '12 at 20:03

1 Answers1

7

The additional code will be CPU initialisation and C runtime initialisation. The source code for this start-up is probably provided with your compiler. In GCC for example it is normally called crt0.s

Depending on your CPU and memory it will probably require some initialisation to set the correct clock frequency, memory timing etc. On top of that the C runtime requires static data initialisation and stack initialisation. If C++ is supported additional code is necessary to call the constructors of any static objects.

Cortex-M devices like Stellaris are designed to run C code with minimum overhead, and it is possible to essentially start C code from reset, however the default start-up state is often not what you want to run your application is since for example this is likley to run at a lower and less accurate clock frequency.


Added 06Dec2012:

Your start-up code is almost certainly provided by the CMSIS. The CMSIS folder will contain CoreSupport and DeviceSupport folders containing start-up code. You can copy this code (or the relevant parts of it) to your project, modify it as necessary and link it in place of the provided code. The CMSIS is frequently updated, so there is an argument for doing that in any case.

Your build log and/or map file are useful for determine which CMSIS components are linked.

Clifford
  • 88,407
  • 13
  • 85
  • 165
  • I want to do everything on my own. Can you tell me how I can disable these startup code. I am using Stellarisware IDE, which is based on Eclipse Indigo. Is it possible to write ourself these startup code in C? – Akhil P Oommen Dec 02 '12 at 21:45
  • 2
    Yes, most compilers do have an option to skip the usual startup code. On gcc I believe it is something like "-nostartfiles" – Chris Stratton Dec 02 '12 at 22:08
  • 2
    Such an option is sometimes also referred to as "minimal startup" versus "standard startup". Be aware that if you disable such an option, the code can no longer rely on initialization of static/global variables. Meaning that you must set all values of static/global variables in runtime, you can no longer write `int my_global = 123;`. – Lundin Dec 03 '12 at 07:53
  • 1
    Your toolchain may provide an option to automate this, but essentially you simply replace or modify the compiler supplied start-up code with your own and link that instead. – Clifford Dec 03 '12 at 22:10
  • The startup code is doing important stuff, initialising the device and getting everything to a "safe" stable starting state. You need to read & understand what it's doing before you remove it. For a very small micro where you're running a few lines of ASM you can probably live without it, but on more complex ones I would be VERY wary of just removing it. – John U Dec 05 '12 at 10:14
  • The IDE you are using is irrelevant to code generation. I have updated the answer. – Clifford Dec 06 '12 at 09:43