2

My goal is to write a library which exposes only a public API in C language.

Let's say I'm writing a library, and it depends on two other libraries: a.c, and b.c

Contents of a.c:

int a(void) { return 1; }

Contents of b.c:

int b(void) { return 2; }

And let's say my library is m.c and its contents are:

#include "a.h"
#include "b.h"

int m(void) {
  return a() + b();
}

My goal is to hide int a(void) and int b(void) so that they don't pollute the global namespace. I want the only symbol to be exported to be int m(void). That is, if my user defines their own a, they shouldn't get a link error:

#include "m.h"

int a(void) { // SOME OTHER FUNCTION
  return 5;
}

int main(void) {
  return m() + a();
  // THIS WOULD CREATE A LINK ERROR,
  // SINCE THERE ARE TWO `A` FUNCTIONS
  // IN SYMBOL TABLE                 
}

The only thing that comes to mind is to define macros with the same name as a and b like this:

#define a internal_a
#define b internal_b

This would create name-mangled symbols, and once user has an executable, they can strip away internal dependency symbols using strip -s file.elf

Another way would be to design internal dependencies as UNIX style filter programs. However, this would require serialization, and would only work if all the internal dependencies belong to me.

Both methods are not ideal, so I'd like to see if there are any better ways to achieve this in C. I'm also not sure how C++ solves this. Most probably the symbols C++ exposes have names namespace-funcname-params.

edit: This would be trivial if I had all my dependencies in a single file. I'd simply use static keyword where needed. My question is specifically about multiple file libraries.

edit: Even though standard C doesn't provide a portable solution for this, the problem is still very common in programming. So, solutions might not be directly relevant with C, but more with the build tools (compiler, linker, preprocessor..)

  • 3
    Why not enforce some kind of prefix on these functions? I've seen macros like `int EXPORT_FUNCTION a(...)` where that macro injects a prefix of sorts. – tadman Oct 31 '20 at 00:04
  • Because of the possibility of name collisions. – Deniz Basgoren Oct 31 '20 at 00:05
  • 1
    That's what the prefix is all about. If you're going to map to `internal_a` anyway, why not just call it that and be done with it? – tadman Oct 31 '20 at 00:05
  • That won't scale. Wouldn't I end up with names such as `system_subsystem_component_func`? C++ namespaces try to solve this issue with `using namespace x`, and I'm trying to find something similar in C. Your EXPORT_FUNCTION macro seems like an interesting way – Deniz Basgoren Oct 31 '20 at 00:07
  • 1
    Use a prefix that no one else would reasonably use. I suggest `stackoverflow_com_questions_64617159_`. – John Zwinck Oct 31 '20 at 00:07
  • 1
    Look at [this answer](https://stackoverflow.com/questions/58115740/concatenate-prefix-in-the-function-name-with-macro) for discussion along similar lines, or [this one](https://stackoverflow.com/questions/1489932/how-to-concatenate-twice-with-the-c-preprocessor-and-expand-a-macro-as-in-arg) as well. – tadman Oct 31 '20 at 00:10
  • Even if I use prefixes, I would still have code with prefixes all over my library code. I'm trying to achieve `using namespace x` in C, as I said – Deniz Basgoren Oct 31 '20 at 00:11
  • 4
    The only way to not "export" symbols in a portable way is to use internal linkage, using `static`. Since your private functions are in different translation modules that's not possible. You have to research compiler-specific ways of exporting and hiding symbols of your library. – Some programmer dude Oct 31 '20 at 00:12
  • You can either yell at the sky about not having a magical pony or you can deal with the fact that C can't do what you want unless you use prefixes of some kind, or some other compiler-specific extensions that might emulate namespaces. – tadman Oct 31 '20 at 00:12
  • One way to do this might be to avoid exporting functions altogether and instead export *function pointers* that can be used instead. It might be possible to package these up as shared libraries with a single function that exports a function-pointer-based manifest of what's inside, then you can call it through those pointers. – tadman Oct 31 '20 at 00:14
  • I think if I could combine two relocatable files into one, then using some ELF tool, I could turn global symbols into local ones. That would solve the issue. Really wonder how it's done in big codebases like Linux kernel – Deniz Basgoren Oct 31 '20 at 00:15
  • Of course, you could implement a preprocessor that implements a name-mangling scheme and support for some namescape-scope and `using` declarations (not with the C preprocessor, that would take something more elaborated I think). But at that point, you can just use C++. – Hulk Oct 31 '20 at 00:16
  • 4
    `ld` has a switch `-r` with which you can link multiple object modules into one new object module (while resolving symbol references satisfied by the combined object files) and other switches to remove symbols other than those you list from the symbol table. That produces an object file with only the desired symbols visible. Sorry, I do not recall the switches from memory. (And they might be Apple-specific, but I think it is likely they are available in other `ld` versions.) – Eric Postpischil Oct 31 '20 at 00:24
  • @DenizBasgoren Technically you could make `a` static and `#include "a.c"` in`m.c` instead. Though that's rather ugly, doesn't scale well, and can have other drawbacks. – dxiv Oct 31 '20 at 00:32
  • Didn't know about the -r switch. Now what's left is to somehow instruct ld to start from m.c and make all depending symbols local. That's the way to go. Including c file directly is rather sloppy – Deniz Basgoren Oct 31 '20 at 00:37
  • @DenizBasgoren If you are looking for platform or toolset specific solutions then you should mention that in the question (and tags), since the answers could be different than for portable C in general. – dxiv Oct 31 '20 at 00:46
  • 1
    I'm getting that standard C doesn't have language-level support for this. I'm rather interested about how it's done in practice (be it with macros, or tooling), since it is such a common problem in programming. Will also add gcc and ld tags to better reflect this – Deniz Basgoren Oct 31 '20 at 01:00
  • In practice, people _don't_ use single-character names for global objects (functions or variables) and _do_ use systematic prefixes which can be documented. The C library does this — see C11 [§7.31 Future library directions](http://port70.net/~nsz/c/c11/n1570.html#7.31) — and so does POSIX — see [Use and Implementation of Interfaces](https://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html#tag_15_01). The lists of reserved names are quite extensive; rules such as 'type names ending in `_t` are reserved for the implementation' are widely abused (ignored). Prefixes help. – Jonathan Leffler Oct 31 '20 at 03:30
  • With the GNU linker/loader (`ld`), you can use linker scripts to control the visibility of symbols, along with the `-r` option mentioned by @EricPostpischil (which is widely available; it was part of 7th Edition UNIX in the late 70s, but is generally unused). – Jonathan Leffler Oct 31 '20 at 03:32

0 Answers0