If I were to create a c-library that contains functions a(), b(), and
c(). I have a main.c that simply adds two integers together and does
not call a(), b() or c().
Then I compile it like so: gcc mylib.c main.c -o bigger.out
bigger.out is much bigger than had I compiled my code like so: gcc
main.c -o smaller.out. So, my guess is a.out contains a(), b(), c().
Is there an option I can tell to gcc/linker to only include functions/
object files that are 'reachable' from main?
Finally, I played around with -Os, that seems to help. It reduces
bigger.out to 60% it's original size, but it's still 3 times larger
than smaller.out.
The reason why I ask is I inherited some code with a makefile which is
similar to gcc -o a.out *.c This works fine, but now the code needs
to be ported to a smaller platform, and I was hoping that gcc may be
able to figure out what functions/object files should be included in
the exe and not.
any suggestions?
thanks.
> Hello.
>
> If I were to create a c-library that contains functions a(), b(), and
> c(). I have a main.c that simply adds two integers together and does
> not call a(), b() or c().
>
> Then I compile it like so: gcc mylib.c main.c -o bigger.out
>
> bigger.out is much bigger than had I compiled my code like so: gcc
> main.c -o smaller.out. So, my guess is a.out contains a(), b(), c().
>
> Is there an option I can tell to gcc/linker to only include functions/
> object files that are 'reachable' from main?
Basically:
gcc -c -o libmy.o libmy.c
ar cr libmy.a libmy.o
ranlib -s libmy.a
gcc -o should-be-small main.c -L. -lmy
To see what happens, you can use:
gcc -Wl,-whatsloaded,-why_load -o should-be-small main.c -L. -lmy
> The reason why I ask is I inherited some code with a makefile which is
> similar to gcc -o a.out *.c This works fine, but now the code needs
> to be ported to a smaller platform, and I was hoping that gcc may be
> able to figure out what functions/object files should be included in
> the exe and not.
>
> any suggestions?
You will have to compile what is needed yourself. It's the link editor
who selects what object code to include. If you don't compile something
that's not used it's ok. If you don't compile something that is needed,
it won't go. To be sure, compile everything, put it in a library, and
let the linker decide what to include.
--
__Pascal Bourguignon__
Yes and no.
>
> any suggestions?
>
There is no magic single "slim my program"-switch.
But there are some tactics to get the same effect.
-Os will try to generate smaller machine code, all good, but there is more which
is outside the scope of -Os (and -Os basically can do nothing about).
You first have to take a step back and recall a basic C detail:
The translation unit.
The compiler works a translation unit at a time. Or in other words:
A C file at a time.
A C file has to be "self-contained" in some way. You have to declare the
variables you want to use, put in prototypes for functions (C does not require
it, but it's good form, you maybe want to use "-Wimplicit" and
"-Wmissing-prototypes"), all this maybe through a header. And you have the code.
Within the Translation unit, the compiler can do a lot of magic, but as soon as
translation units interact (like, yes, you call a function from the libc, or a
function of your own program, in another file), the compiler is "blind folded".
He can not look over the border of an translation unit. Instead other mechanism
regulate how translation units interact (basically linkage).
Even if you call the compiler with "*.c", many will work "file by file". Some
compiler may switch on a special mode in this case, but not gcc.
And you can even tie the compiler down within a translation unit:
The first problem with lots of "old" code, or when not evangelized about it is:
No use of static.
All functions within the program are of the kind "some_type foo(whatever)". Even
if they are not listed in a header file, not meant to be called by some other
place in the program. This means they have _external linkage_. This is bad in
your case because the compiler can not kill them, because he does not know if
another translation unit may need it.
Put the "static" keyword on the functions local to your translation units. This
way the compiler can optimize and eliminate them much better.
But it gets worse:
External linkage means on ELF systems (most Unix) that I can dynamically link
the functions in YOUR program into MY program. This can be a useful feature at
times, but most of the time it ties the hands of the compiler down on the
remaining non-static functions for future optimizations.
Either annotate your non-static functions with
__attribute__((visibility("hidden"))) or compile and link your code with
"-fvisibility=hidden" on the command line.
Now lets start to shove off some code.
There is a trick you can use to remove translation units, with the help of the
linker: static libraries.
Let's say you put all your translation units into a static lib (an archive, .a):
gcc -c *.c
ar -cru all_obs.a *.o
gcc -o a.out main.o all_obs.a
This way the linker will only put those .o into the resulting a.out, which are
referenced by symbols (name), working the call chain down from main. But there
are two catches:
1) If function a(), b(), c() are in the same .o (translation unit), even if you
only need a(), the linker will copy the whole .o with b() and c()
=> maybe the option -ffunction-sections can help you here (simply put: _every_
function gets its own .o (within an .o), even if they are in the same
translation unit).
Which brings us to:
2) you can break a Program with that. There are sometimes some sophisticated
programming tricks which do not cope well with that. If the program avoids to
clearly reference a function/variable from its code (dirty casting tricks,
function pointer from an variable which is looked up at run time in creative
ways ("Its always at the start of the .data-section"), etc...), the compiler
will not generate a reference, so the linker will not have an unresolved symbol,
so the needed object will not get pulled in.
Simply test it, this will tell you if your program contains such hacks.
But you can even get more elaborate.
Look up the options "-fwhole-program" and "-combine".
If your build is already like:
gcc -o a.out *.c
add -fwhole-program -combine. This way the compiler will look over the border of
a translation unit, basically concatenating all your C code to a single
translation unit.
But to take real advantage of this, you have to first put in some "static" and
"-fvisibility=hidden", so the compiler _can_ eliminate stuff and does not have
to assume functions will be called from elsewhere.
There is also a catch with this: If your program is (very) large, gcc may not be
able to compile it in this mode, because he will need too much resources to do so.
This last catch may be finally resolved (and this unnatural "all at once" build)
with the new gcc 4.5 link time optimizations (LTO). But that is beta ware.
Oh, there is another catch with -fwhole-program or LTO:
This way the compiler will see all your code, at once. So he may get _very_
elaborate at optimizing. He may suddenly see that certain parts of code do
nothing or are dead, taking them out, or substituting complete parts of the
program with constant expressions. Spanning across translation units. This is good!
But this may also break your program, because suddenly some side effect is
killed with this. The bug does not lie within the compiler in this case, but
your code, because you managed to hide this important side effect from the
compiler in some way (like busy waits, in the view of the compiler they do
"nothing": they need a compiler barrier to tell the compiler "Here happens
something beyond the scope of C, it's more than a loop that does nothing".
Remember, "C conforming to the Standard" is all the compiler cares about, not
"It's C, it's portable assembler, if i write this i should get that").
>
> thanks.
Greetings
Jan
--
class WindowsVista extends WindowsXP implements nothing
{}