Partial Linking In C

Updated: Jan 08, 2021

Its better to give an example first then explain. Here is the source code of a library called libplic_math.a, it is a very simple library with addition(plic_add), subtraction(plic_sub) and multiplication(plic_mul) functions.

plic_math.h

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
#ifndef __PLIC_MATH__
#define __PLIC_MATH__

#ifdef __cplusplus
extern "C" {
#endif

  int plic_add(int a, int b);
  int plic_sub(int a, int b);
  int plic_mul(int a, int b);

#ifdef __cplusplus
}
#endif

#endif /* __PLIC_MATH__ */

plic_add.c

1
2
3
4
5
6
#include <stdio.h>
#include "plic_math.h"

int plic_add(int a, int b) {
  return a + b;
}

plic_sub.c

1
2
3
4
5
6
#include <stdio.h>
#include "math.h"

int plic_sub(int a, int b) {
  return a - b;
}

plic_mul.c

1
2
3
4
5
6
#include <stdio.h>
#include "plic_math.h"

int plic_mul(int a, int b) {
  return a * b;
}

Makefile

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
CC ?= gcc

all: libplic_math.a

plic_add.o: plic_add.c plic_math.h
	$(CROSS_COMPILE)$(CC) $(CFLAGS) -fPIC -c -o $@ $<

plic_sub.o: plic_sub.c plic_math.h
	$(CROSS_COMPILE)$(CC) $(CFLAGS) -fPIC -c -o $@ $<

plic_mul.o: plic_mul.c plic_math.h
	$(CROSS_COMPILE)$(CC) $(CFLAGS) -fPIC -c -o $@ $<

libplic_math.a: plic_add.o plic_sub.o plic_mul.o
	ar rcUs $@ $^

clean:
	rm -fr *.o *.a

.PHONY: clean

Here is the source code of another library called libplic_sum.a, this library uses addition(plic_add) function from libplic_math.a library and provides another function to sum the results(plic_sum).

plic_sum.h

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
#ifndef __PLIC_SUM__
#define __PLIC_SUM__

#ifdef __cplusplus
extern "C" {
#endif

  int plic_sum(int i, int j);

#ifdef __cplusplus
}
#endif

#endif /* __PLIC_MATH__ */

plic_sum.c

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
#include <stdio.h>
#include <plic_math.h>
#include "plic_sum.h"

int plic_sum(int i, int j) {
  int n = 0;
  int sum = 0;

  for(n = i; n < j; n++) {
    sum = plic_add(sum, n);
  }

  return sum;
}

Here is the source code of one program plic_sum_elf which uses sum(plic_sum) function from libplic_sum.a.

plic_sum_elf.c

1
2
3
4
5
6
7
#include <stdio.h>
#include "plic_sum.h"

int main(int argc, char *argv[]) {
  printf("sum: %d\n", plic_sum(10, 20));
  return 0;
}

In plic_sum_elf.c, The function plic_sum from libplic_sum.a depends on plic_add function from libplic_math.a, so in order to generate plic_sum_elf executable, we have to link both libplic_sum.a and libplic_math.a like this,

$ cc -static -o plic_sum_elf plic-sum_elf.c -L/path/to/libplic_sum_dir -lplic_sum -L/path/to/libplic_math_dir -lplic_math

If we omit -lplic_math, then compiler will throw error saying undefined reference to plic_add symbol. Imagine that you are writing one library like libplic_sum.a which itself depends on various libraries like libplic_math.a, The user of your library also need to be aware of all the libraries which your library depends.

It is unnecessary for the downstream user to know all the details about your library in order to link with your library

Partial Linking with ‘-r’ flag

To simplify and make linking easier for your downstream user, you can provide your library as partially linked library, means, when you generate your library, you can instruct the compiler through -r flag 1 to resolve all the symbols required for your library and generate one big object file in order to generate a partially linked library.

$ cc -r -o plic_sum_nodeps.o plic_sum.c -L/path/to/libplic_math_dir -lplic_math
$ ar rcUs libplic_sum_nodeps.a plic_sum_nodeps.o

Now, it becomes easy to generate plic_sum_elf executable, no need to link with libplic_math.a, downstream user only need to link with libplic_sum_nodeps.a

$ cc -static -o plic_sum_elf plic_sum_elf.c -L/pat/to/libplic_sum_nodeps_dir -lplic_sum_nodeps

Here is the Makefile which generates libplic_sum.a, libplic_sum_nodeps.a as well as plic_sum_elf executable.

Makefile

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
CC ?= gcc

all: libplic_sum.a libplic_sum_nodeps.a plic_sum_elf

plic_sum.o: plic_sum.c plic_sum.h
	$(CROSS_COMPILE)$(CC) $(CFLAGS) -fPIC -c -o $@ $< -I../plic-math

libplic_sum.a: plic_sum.o
	ar rcUs $@ $^

plic_sum_nodeps.o: plic_sum.o
	$(CROSS_COMPILE)$(CC) $(LDFLAGS) -r -o $@ $< -L../plic-math -lplic_math

libplic_sum_nodeps.a: plic_sum_nodeps.o
	ar rcUs $@ $^

plic_sum_elf: plic_sum_elf.c libplic_sum_nodeps.a
	$(CROSS_COMPILE)$(CC) $(CFLAGS) $(LDFLAGS) -static -o $@ $< -L. -lplic_sum_nodeps

clean:
	rm -fr *.o *.a plic_sum_elf

.PHONY: clean

1

https://gcc.gnu.org/onlinedocs/gcc/Link-Options.html