2

I'm trying to add a sanity check at the link stage. Specifically, I have programs that are statically linking libraries. Some programs are limited to the set of libraries allowed to be linked while others are not. So say for example I have two programs respectively from food.c and fruits.c, and I have two libraries (for which I have source) apple.a and broccoli.a. There is no dependency between apple.a and broccoli.a.

Is there a way I can modify my source and the libraries' source such that the linker will allow me to link both apple.a and broccoli.a into food.c, and will allow me to link apple.a into fruits.c, but will produce a linker error when I try to load apple.a and broccoli.a (or just broccoli.a) into fruits.c? Assume that my source does not #include any headers in the libraries they link, as they are linked in by mistake, are left over from deleted code, function prototypes are manually declared, and so on.

What would be nice is if I could somehow tag these libraries in such a way that my program can reject a particular tag.

The specific compiler I'm using does not implement #pragma poison, which was really the only helpful tool I've found in my search. My hunch tells me there might be something clever I can do with weak symbols, but I'm not exactly sure what or how.

I also don't believe using #define will help me, as my understanding is that would only prevent me from #includeing specific files, but if I'm mistaken in that, then that could also be a fine solution.

Tim
  • 21
  • 1
  • The first things that comes to mind is having global variables with the same name, and getting multiple definition errors. – Thomas Jager May 07 '19 at 20:23
  • 1
    I do not see any logic in it. It does not add any sanity, but it definitely ads a huge disorder. Just link what you are supposed to link. To be mo dynamic in that use autoconf or cmake on the project generation level. – 0___________ May 07 '19 at 20:27
  • Are these libraries unused in the binary that the linker is making? They're (mostly) harmless. Linkers routinely ignore .lib files that don't have any symbols declared in them that the program needs. – 1201ProgramAlarm May 07 '19 at 20:53
  • - Thomas Jager, that is pretty close to the best idea I've come up with, so if it's generally accepted then that will be the route I take – Tim May 07 '19 at 23:52
  • - P__J__, I know it seems rather unreasonable, which I presume is why I haven't had much luck on Google. But for reasons I'm not at liberty to discuss, one way or another it has to happen. There are specific guarantees I need to provide, some of which necessitate restricting the libraries that are linked. Moreover, this is less a tool to prevent it from happening and more a tool support the guarantees I'm claiming the system to provide. – Tim May 07 '19 at 23:55
  • - 1201ProgramAlarm, my previous comment is also probably relevant as a response to yours, which is that while the compiler and linker are optimized to eliminate unused code, it's not a guarantee provided by the specific toolchain I'm using, which means I need to find a way to do it. – Tim May 07 '19 at 23:57

2 Answers2

0

Some programs are limited to the set of libraries allowed to be linked while others are not.

That doesn't make much sense.

Is there a way I can modify my source and the libraries' source such that the linker will allow me to link both apple.a and broccoli.a into food.c, and will allow me to link apple.a into fruits.c, but will produce a linker error when I try to load apple.a and broccoli.a (or just broccoli.a) into fruits.c?

No, because the C language doesn't know anything about libraries. Its specifications are written in a way that allows implementations to provide and use them, but there is no way in the C language to refer to a library itself. It is not a C concept.

And anyway, it doesn't make much sense. Static library names have no inherent significance. They are meaningful only to the extent that they identify their contents. Nothing is gained by disallowing fruits.c linking against broccoli.a, because it would be easy to work around, and it is conceivable that it would even be accidentally worked around through errors in build-system maintenance -- i.e. objects intended for broccoly.a are put in apple.a additionally or instead.

Moreover, most linkers are smart -- they will omit unreferenced functions and objects when they build an executable, so including unneeded (static) libraries in the link is generally harmless.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157
  • Right so the C language doesn't know anything about them, but the linkers might. So maybe "modify source" was misleading. What I mean to ask is if I can modify the source files to specifically target the linker. For example, I believe if `poison` were an option, I could `poison` a specific symbol that is defined only in the libraries that should never be linked. – Tim May 07 '19 at 23:59
  • Maybe a better way to think of this is `broccoli.a` in some way causes `fruits.c` to work improperly. I want a way to enforce that not happening without auditing code or chancing shipping faulty `fruits.c`. – Tim May 08 '19 at 00:13
0

Ok, first things first: This smells extremely like a BIG, BAD hack around a problem that can be solved easier!

Now one possible way would be to enforce a "multiple definition error". For my example there are the following files:

  • broccoli_mark.c which is used to "mark" (see below) the broccoli library.
  • broccoli_fn.c which contains the functionality of the broccoli library.
  • fruits.c which is the program which should not link against the broccoli library. This is "marked" to not allow linking against broccoli.

"marking": This can be implemented by simply defining a global symbol with some known name in these files. For example:

int broccoli_mark; // a global variable

Put only that into broccoli_mark.c and add it somewhere to fruit.c.

Now linking ...

# Compile and ar the library
$ gcc -c broccoli_mark.c
$ gcc -c broccoli_fn.c
$ ar rcs broccoli.a broccoli_mark.o broccoli_fn.o
# Compile and link program
$ gcc -c fruits.c
$ gcc fruits.o broccoli.a

... works .. but only because of how linking with a static library works: Basically the linker only picks out (whole) object files which define a symbol which is undefined in the current linking set (initially the program, then also added object files from the library).

So in order to fail linking we must ensure that broccoli_mark.o (or more specifically its contained symbol broccoli_mark) is included. Approaches:

  • "Merge" multiple object files into a single one:

    # Merge two object files into a single broccoli.o
    $ ld -r -o broccoli.o broccoli_fn.o broccoli_mark.o
    # create the library or not ..
    $ gcc fruits.o broccoli.o
    ld: broccoli.o:(.data+0x0): multiple definition of `broccoli_mark'; fruits.o:(.data+0x0): first defined here
    collect2: error: ld returned 1 exit status
    

    Yeah!

    Note that you can do this for all .o files of the broccoli library ... but then you defeat the useful property of static libraries to only include what's necessary.

    Also note that there are options which could probably break this.

    (Similar alternative: --whole-archive option)

  • Just include the "mark" in a source file which will be surely included (e.g. one which contains the "main" function of the library).

Of course all that "marking" etc can be hidden behind descriptive macro names ... still feels like a pretty bad hack, though!

Daniel Jour
  • 15,896
  • 2
  • 36
  • 63
  • It seems like this would interfere with other sources being able to include both libraries, as the OP specified some should be able to do. – John Bollinger May 08 '19 at 10:51
  • @JohnBollinger No, the "marks" should only be placed in a library (e.g. a mark for broccoli in the broccoli library, a mark for apple in the apple library) and in applications which are *not* compatible with that library. (`fruit.c` is not a library and thus not intended to be included; `food.c` can without issues link against both broccoli and apple libraries) – Daniel Jour May 08 '19 at 11:34
  • @DanielJour okay I think this I can work with and massage to fit my use case, thank you! As far as it feeling hacky, it very well might be but it serves a purpose. Sacrificing properties of static libraries isn't not much of a concern because (and while this will sound like a waste of time and money, I promise it isn't) part of the QA process will essentially be to analyze every line of code and either there will be a way to cover it under MCDC testing (to be later considered whether it is required) or it will be removed entirely from source. – Tim May 08 '19 at 13:34