Background
It is sometimes necessary to hide global symbols while
linking a C program. If, for example, you need to link two functions of
the same name into a single image or if you want to build an object file
which only exposes its intended entry points. The Gnu tools make this
easy.
The essential trick is to use
objcopy and its -G flag to turn
unwanted global symbols into local ones. Combined with repeated use of
ld to build partially linked object files, we
can achieve most desired results.
An example
Say we have the following C files:
m.c |
b.c |
c.c |
#include <stdio.h>
int main() {
printf("b returns %d\n",b());
printf("and c returns %d\n",c());
return 0;
} |
int d(void);
int b() {
return d();
}
|
int d(void);
int c() {
return d();
}
|
|
d2.c |
d1.c |
|
int d() {
return 2;
} |
int d() {
return 1;
} |
We can try to compile and link them:
sh-2.05a$ gcc -o m m.c b.c
c.c d1.c d2.c
/tmp/ccGmsYXA.o: In function `d':
/tmp/ccGmsYXA.o(.text+0x0): multiple definition of `d'
/tmp/ccWmkFx6.o(.text+0x0): first defined here
collect2: ld returned 1 exit status
but this will fail as there are two copies of the d()
function. A simple solution would be to combine each d?.c file with the
appropriate b.c or c.c, using a static declaration to ensure that the
declarations of d are local to the file:
b2x.c |
c1x.c |
static
int d() {
return 2;
}
int b() {
return d();
}
|
static
int d() {
return 1;
}
int c() {
return d();
}
|
sh-2.05a$ gcc -o m m.c
b2x.c c1x.c
sh-2.05a$ ./m
b returns 2
and c returns 1
This works fine, but may not be appropriate for your
situation. If we do not wish to change the file structure of the source or if we
wish to hide some globals from subsequent link steps, we can proceed as follows:
- Compile the C files to object code:
sh-2.05a$ gcc
-c b.c c.c d1.c d2.c
sh-2.05a$ ls
b.c b.o c.c c.o d1.c d1.o d2.c d2.o m.c
- Incrementally link the appropriate pairs of files
into new object files:
sh-2.05a$ ld -r
-o c1.o c.o d1.o
sh-2.05a$ ld -r -o b2.o b.o d2.o
- Use the objcopy
utility to localise all symbols not listed after
-G flags (of which there can
be several):
sh-2.05a$ objcopy -G c c1.o c1f.o
sh-2.05a$ objcopy -G b b2.o b2f.o
- Finally, link the resulting objects with the main
program, C startup and C library:
sh-2.05a$ gcc -o m m.c c1f.o b2f.o
sh-2.05a$ ./m
b returns 2
and c returns 1
If you need to see what is happening, you can use the nm
utility to inspect the symbol tables of the object files:
nm c1.o |
nm c1f.o |
00000000
T c
00000014 T d
00000000 t gcc2_compiled.
00000014 t gcc2_compiled. |
00000000
T c
00000014 t d
00000000 t gcc2_compiled.
00000014 t gcc2_compiled. |
As you can see, the magic is that symbol
d has turned from a global (T)
to a local (t).
|