High Performance Computing

 

[Cygwin Tips] [Floppy Disks] [NAG Toolpack 2.5] [Web PNG images] [Controlling symbol scope in C]

 

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).