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.

Remove unused functions/variables from the executable

Other Parts Discussed in Thread: MSP430F1611, MSP430WARE

Dear all,

I'm using CCS v5 and targeting an MSP430F1611. Looking at the .map file and the size of the produced image, I've noticed that a lot of unused things are being built into the executable. 

The way I'm building my application is by putting each specific piece of code in its own .h/.c file (e.g., drivers). Thus I've got a lot of .obj that are included by the linker into the final image, even when that code is not used at all in the current executable I'm building.

For instance, with a main.c that does not include any header and a completely empty main() function, I get a 6Kb image that includes some of the .obj from my source code tree, and all the global variables (all declared static in their own source file) from all these .obj files. 

Here's an excerpt from the .map file I get:

.bss       0    00001100    000006f0     UNINITIALIZED
                  00001100    0000024e     at45db321dApp.obj (.bss)
                  0000134e    00000206     timers.obj (.bss)
                  00001554    00000068     tasks.obj (.bss)
                  000015bc    0000004b     sx1231Device.obj (.bss)

There are a lot more of them, but you get the idea: There is no reason to include code from these files since my main() is just an empty function. RAM usage is around 1.7 Kb, and comes from all the individual static variables declared in the source files. These variables are accessed only by the functions in these files (hence the static) which are not called from anywhere (empty main()).

I've tried building in release mode, with all kinds of -O flags. I've also tried using the --gen_func_subsections flag.

Any help would be appreciated.

  • I have never used CCS. It will take me a long time to download the ~1GB compressed file. And I only have 128MB of memory in my PC.

    The free version of CCS has code size limit of 16KB even for assembly code. I normally only write very short programs. Thus originally, I was not concerned about this limit. But if CCS generates more object codes, I may hit that limit.

    Looks like I will stay with IAR KickStart. It has no code size limit for assembly. It too generates unnecessary extra object code. But very rarely and I know how to eliminate them if I want to.

  •  

    Fran��ois Ingelrest said:
    RAM usage is around 1.7 Kb, and comes from all the individual static variables declared in the source files. These variables are accessed only by the functions in these files (hence the static) which are not called from anywhere (empty main()).
    Using CCS 5.1 and MSP430 Code Generation Tools 4.1.0 I have repeated the problem.

     Not sure if this is a "bug" in the linker, or a limitation. Normally, I would expect a linker to add the entire contents of all object files passed to it to the executable. The fact that the MSP430 Linker is clever enough to remove individual unused functions from an object file is an enhancement to other linkers.

    One possible solution would be to move your "library" functions out of the "Executable" (main) CCS project and into a "Static Library" CCS sub-project. The linker will only link object files from the library when referenced from the main project.

  • Thanks for the suggestion! However, it seems like a lot of work to manage many small libraries, while linking only the .obj I need would be way simpler. Is there a way I could tell CCS to automatically generate the Makefile based on a list of files/directories (and not based on ALL files in the projects)? That way, I could simply keep a list of files (one per application I build) and pass the good one to the Makefile generator. Thus, only the .obj I need would be passed to the linker.

    Alternately, I could write myself a Makefile per application, but then I would have to manage all the compilation options by hand instead of using the GUI.

    To continue on the original issue, after going through my files, I finally found why some .obj were included while others were not. One of the files contained a global structure initialized at compile time with a pointer to a function. As a result, that function was "referenced" (but not called) and all the calling subgraph was included by the linker in the image. Initializing the structure at run-time solved that issue.

    BUT I still have the issue of having all the global variables from all my .obj files being included in the image. That's a linker behavior I don't understand: All these global variables are static, thus I don't get the linker logic which includes these variables but not the associated .obj code. How are these static global variables supposed to be accessed without the associated code? Is there something I'm missing?

    More generally I'd like to know how people develop complex applications for MSP430 using CCS. The MSP430 is a powerful UC, and is designed to run quite complex applications. Managing many .obj files must be a common issue when developing for that UC, I can't believe I'm the first one to face that issue.

  • Francois Ingelrest said:
    However, it seems like a lot of work to manage many small libraries, while linking only the .obj I need would be way simpler.

    There is no need to have many small libraries. You can have one static library project that includes many object files, and when the library is linked to your main project only the library object files which are needed to resolve references from the main project get added to the executable. See the driverlib in the MSP430Ware for an example Static Library project.

    See the Archiver Description and Link Description sections of the MSP430 Assembly Language Tools User's Guide for more details of how the linker works.

  • So I've created a big library with all my code just for testing purpose, and this seems to work correctly: The final image includes only what's referenced in my application project. One question out of curiosity: Why is the linker "smarter" on that matter with a static library rather than with a bunch of .obj files? I would have expected it to act the same in both cases.

  • Francois Ingelrest said:
    Why is the linker "smarter" on that matter with a static library rather than with a bunch of .obj files? I would have expected it to act the same in both cases.

    These are two different things.

    If you include an object file, you explicitely tell the linker 'this file is needed'. By specifying libraries, you say 'pick form them what you need and leave the rest'.

    However, normally, a linker can only keep an object file as a whole or leave it. So if you compile your library from one object file and use any tiny bit from it, the whole library is included.
    Main reason is that the linkter does no knwo whether the code inside one monolithic compilation unit can be separated at all. Two object files have been compiled separately and there#s a clear cut between them. Either there are references to the other or not. If so, keep it as a whole, if not, leave it.

    The MSPGCC compiler uses a trick: it puts each funciton ito its own segment. This makes it clear to the linker that there are no implicit connections between segments except for the 'official' references. So the linker can treat each funciton as if it were in its own object ilfe.
    Global variables and also static local variables cannot be separated this way, as the compiler just puts them into the data segment.
    Actually, there is no difference between local static and global variables. Except that the compiler 'forgets' about their existence when done with the function. For the linker, static local variables are just global variables with no references from other functions.

    In my own projects, I often use a central configuration header file which sets some defines and is included by every object file. Then I add #ifdef precompiler directives to the object files. So I can control which code (or which variant, as the code is used for several MSPs and ATMegas too) the compiler shall compile for this specific project.

    It's abit liek an individual linker script, but excludes the code from being compiled at all. And allows for alternatives of the same name (e.g. putchar and getchar are different for USART, USCI or the ATMega, btu all in the same file and all with the same name)

  • Thanks for the detailed explanation, it's clearer to me now. I can now see from the .map file that only what's needed (both ROM & RAM) is included in the compiled image.

**Attention** This is a public forum