0

I'm developing a static library (*.a file) with GCC and an ELF target.

I need the following (AND12 setup):

  • the data part must be linked into the executable iff both features feature1 and feature2 are linked in (because they are used)
  • feature3 should be able to detect whether data was linked in

Is there a way to do this using weak symbols or some other tricks understood by GCC and GNU ld(1)?


FYI I was able to implement this (OR12 setup):

  • the data part must be linked into the executable iff either feature feature1 or feature2 is linked in
  • feature3 is able to detect whether data is linked in

Here is how I did it:

/* data.c */
const char data[] = "RealData";
/* f1.c */
#include <stdio.h>
extern const char data[];
void feature1() {
  printf("feature1 %s\n", data);
}
/* f2.c */
#include <stdio.h>
extern const char data[];
void feature1() {
  printf("feature2 %s\n", data);
}
/* f3.c */
#include <stdio.h>
__attribute__((weak)) extern const char data[];
void feature3() {
  printf("feature3 %s\n", data);
}
/* main.c */
void feature3(void);
int main(int argc, char **argv) {
  (void)argc; (void)argv;
  feature3();
  return 0;
}
/* main1.c */
void feature1(void);
void feature3(void);
int main(int argc, char **argv) {
  (void)argc; (void)argv;
  feature1();
  feature3();
  return 0;
}
/* main2.c */
void feature2(void);
void feature3(void);
int main(int argc, char **argv) {
  (void)argc; (void)argv;
  feature2();
  feature3();
  return 0;
}
/* main12.c */
void feature1(void);
void feature2(void);
void feature3(void);
int main(int argc, char **argv) {
  (void)argc; (void)argv;
  feature1();
  feature2();
  feature3();
  return 0;
}

It works like this:

$ gcc -c -O2 -W -Wall -Werror f1.c f2.c f3.c data.c && rm -f f123.a && ar crs f123.a f1.o f2.o f3.o data.o
$ qq xstatic gcc -s -O2 -W -Wall -Werror main.c f123.a && ./a.out
feature3 (null)
$ qq xstatic gcc -s -O2 -W -Wall -Werror main1.c f123.a && ./a.out
feature1 RealData
feature3 RealData
$ qq xstatic gcc -s -O2 -W -Wall -Werror main2.c f123.a && ./a.out
feature2 RealData
feature3 RealData
$ qq xstatic gcc -s -O2 -W -Wall -Werror main12.c f123.a && ./a.out
feature1 RealData
feature2 RealData
feature3 RealData

What I want to get is the following (different from the above for main1.c and main2.c):

$ gcc -c -O2 -W -Wall -Werror f1.c f2.c f3.c data.c && rm -f f123.a && ar crs f123.a f1.o f2.o f3.o data.o
$ qq xstatic gcc -s -O2 -W -Wall -Werror main.c f123.a && ./a.out
feature3 (null)
$ qq xstatic gcc -s -O2 -W -Wall -Werror main1.c f123.a && ./a.out
feature1 (null)
feature3 (null)
$ qq xstatic gcc -s -O2 -W -Wall -Werror main2.c f123.a && ./a.out
feature2 (null)
feature3 (null)
$ qq xstatic gcc -s -O2 -W -Wall -Werror main12.c f123.a && ./a.out
feature1 RealData
feature2 RealData
feature3 RealData

How should the library files f1.c, f2.c, f3.c and data.c be modified so that it works like as above?

Please note that I'm only interested in a setup where the linker is able to figure it out correctly when to include the data.

I'm not interested in manual workarounds in which the main function passes additional information to the feature... functions, for example when only main has extern const char data[], and it passes it to feature... functions as arguments.

I'm not interested in a workaround where the data is always present in the executable (OR12 setup), and feature1 and feature2 detect the presence of teach other, and they ignore the data unless both of them are present. (That's because the data is huge, and it's wasteful to store and ship something which is not needed.)

I know about and for the purposes of this question I'm not interested in workarounds where the data is stored outside the executable, e.g. in a separate file, which is shipped to the end user only when neded.

It's also OK for me if the solution has code instead of data, because the code can also contain and return data.


How is this useful? data is huge, and it is an implementation detail of the library, and it is only needed by library features feature1 and feature2 if both feature1 and feature2 are used. For maximum convenience, the user expects that the linker is able to figure out automatically what to include in the final executable. The user doesn't want to have data included when not needed (because it would make the final executable too large unnecessarily), and they also don't want to manually track whether it is needed (because the user builds several versions of the executable, and the dependencies are deep down in user code).

pts
  • 80,836
  • 20
  • 110
  • 183

0 Answers0