Question about static libraries exporting functions

M

moschops

I'm not even sure this is the right group to ask about static libraries.
If not, please accept my apologies.

Anyway, static libraries. I often build them, for the usual reasons, and
each time I do I end up with a header file to include in any program
that will be using the functions provided by the static lib, and the
static lib itself.

When I build the static lib, I must specify the functions I wish to be
available (exported) - accordingly, even if the static library is
chock-full of sub-functions and the like, if I export only one function,
the program using the static lib may call only on that one function.
Attempts to use any other function will be met with the usual undefined
symbol error messages during linking, which is great for ensuring people
use the library only as intended (analogous to a class' private and
public functions), and of course the function that can be called has
full access to all the other functions within the library as expected.

How does a static library indicate which functions may be called from
it? It must do it somehow, as it's a single file. Is there a listing
somewhere in there that can be read by my linker?

'Chops
 
A

Andrey Tarasevich

moschops said:
...
How does a static library indicate which functions may be called from
it? It must do it somehow, as it's a single file. Is there a listing
somewhere in there that can be read by my linker?
...

This is off-topic here, since there's no such thing as "libraries" (in the
implied sense of the word) in C++ language.

However, normally there's no way to do what you want. Every function with
external linkage will be linkable from outside. All you have is that functions
with internal linkage (declared as 'static') will not be linkable from outside.
In case when your library is compiled from a single translation unit (single
source file), you can control the external "visibility" of its functions by
using the 'static' specifier. But, obviously, this approach won't work in
general case, since in general case one has more than one translation unit in
the library and needs to call functions across the units.
 
P

Pascal Bourguignon

moschops said:
How does a static library indicate which functions may be called from
it?

By indicating it.
It must do it somehow, as it's a single file. Is there a listing
somewhere in there that can be read by my linker?

Yes.

On unix, static libraries (.a files) are basically ar(1) archives of
..o files. To make for a more efficient link edit, the symbols
exported by the .o files are usually gathered in a symbol table stored
in the library. You can dump this symbol table with the nm(1) command.

On linux, object files are elf(5) files. You can dump the symbol
table from these object files with objdump(1).


Reading the man page elf(5) should give you all the details you may
want.


[pjb@thalassa tmp]$ nm /usr/lib/libz.a | head -20

adler32.o:
00000000 T adler32
000002f0 T adler32_combine

compress.o:
00000000 r .LC0
U _GLOBAL_OFFSET_TABLE_
00000000 T __i686.get_pc_thunk.bx
000000f0 T compress
00000020 T compress2
00000000 T compressBound
U deflate
U deflateEnd
U deflateInit_

crc32.o:
U _GLOBAL_OFFSET_TABLE_
00000000 T __i686.get_pc_thunk.bx
00000000 T __i686.get_pc_thunk.cx
[pjb@thalassa tmp]$ ar xv /usr/lib/libz.a
x - adler32.o
x - compress.o
x - crc32.o
x - gzio.o
x - uncompr.o
x - deflate.o
x - trees.o
x - zutil.o
x - inflate.o
x - infback.o
x - inftrees.o
x - inffast.o
[pjb@thalassa tmp]$ objdump -t adler32.o

adler32.o: file format elf32-i386

SYMBOL TABLE:
00000000 l d .text 00000000 .text
00000000 l d .data 00000000 .data
00000000 l d .bss 00000000 .bss
00000000 l d .comment 00000000 .comment
00000000 l d .note.GNU-stack 00000000 .note.GNU-stack
00000000 g F .text 000002e9 adler32
000002f0 g F .text 000000bf adler32_combine


[pjb@thalassa tmp]$
 
J

James Kanze

I'm not even sure this is the right group to ask about static
libraries. If not, please accept my apologies.
Anyway, static libraries. I often build them, for the usual reasons, and
each time I do I end up with a header file to include in any program
that will be using the functions provided by the static lib, and the
static lib itself.
When I build the static lib, I must specify the functions I
wish to be available (exported) - accordingly, even if the
static library is chock-full of sub-functions and the like, if
I export only one function, the program using the static lib
may call only on that one function. Attempts to use any other
function will be met with the usual undefined symbol error
messages during linking, which is great for ensuring people
use the library only as intended (analogous to a class'
private and public functions), and of course the function that
can be called has full access to all the other functions
within the library as expected.

By default, aren't all symbols are exported? (They are with
every system I've ever used.) I suspect that there are even a
lot of library managers which don't allow limiting which symbols
are visible. (Most "libraries" aren't really much more than a
dumb collection of object files, with an added index.)
How does a static library indicate which functions may be
called from it?

Mostly, it doesn't. If the symbol is publicly accessible in the
object file, it is accessible in the library.
It must do it somehow, as it's a single file. Is there a
listing somewhere in there that can be read by my linker?

Possibly (but I've never seen a library manager which supports
it), the library manager only puts a small subset of the symbols
in the index, and the loader doesn't look beyond the index. I
rather think that this would cause problems as well, however,
since you still want the non-exported symbols available from
other modules in the library itself.
 
P

Pete Becker

This is off-topic here, since there's no such thing as "libraries" (in
the implied sense of the word) in C++ language.

On the contrary: the C++ Standard expressly provides "additional
library facilities" beyond those in C. [intro.scope]/2. It refers
explicitly to the Standard C Library, [intro.refs]/2, among others. It
has its own library (far too many references to enumerate here). And
translation units can be translated and preserved "in libraries"
[lex]/2, which can then be "linked to satisfy external references to
functions and objects not defined in the current translation [unit]."
[lex.phases]/1.9.

What the standard doesn't do is specify the structure of libraries or
how to invoke tools to create and use them. That doesn't make general
questions about how libraries work off topic.
 
J

Jeff Schwab

moschops said:
When I build the static lib, I must specify the functions I wish to be
available (exported) - accordingly, even if the static library is
chock-full of sub-functions and the like, if I export only one function,
the program using the static lib may call only on that one function.
Attempts to use any other function will be met with the usual undefined
symbol error messages during linking, which is great for ensuring people
use the library only as intended (analogous to a class' private and
public functions), and of course the function that can be called has
full access to all the other functions within the library as expected.

How does a static library indicate which functions may be called from
it? It must do it somehow, as it's a single file. Is there a listing
somewhere in there that can be read by my linker?

Put the public interface in the header file, in named namespaces or at
global scope. Declare and define private functions, classes, etc. in
unnamed namespaces within the library's implementation files. For
example, suppose a (naive) library has the following header:

#ifndef MYMATH_H
#define MYMATH_H

namespace mymath {

/* If n > 0, returns base to the nth power; else, returns 1.
*
* Warning: Does not check for overflow or underflow.
*/
int intpow(int base, int n);
}

#endif

The implementation file might look like this:

#include "mymath.h"

namespace {

int intpow_impl(int base, int n) {

int result = 1;

while (--n > 0) {
result *= base;
}

return result;
}

}

namespace mymath {

int intpow(int base, int n) {
return intpow_impl(base, n);
}
}

The client code then has no standard way of referring to the
implementation code:

#include <iostream>

#include "mymath.h"

int main() {
using mymath::intpow;

/* This works. */
std::cout << intpow(3, 5) << '\n';

/* This causes a compile-time error. */
// int intpow_impl(int, int);
// std::cout << intpow_impl(3, 5) << '\n';
}

Unnamed namespaces are akin to static linkage, but more powerful.
(Static linkage is deprecated in C++, anyway.) For example, suppose
intpow is replaced with a template parameter giving the value type,
rather than hard-coded ints. The implementation then may have to be in
the same translation unit as client code, before the first instantiation
of the template. Static linkage would not help; however, an unnamed
namespace can be embedded within the library's namespace, with private
symbols explicitly hidden by declarations that have no corresponding
definitions. For example, the corresponding template library header
looks like this:

#ifndef MYMATH_H
#define MYMATH_H

namespace mymath {

/* If n > 0, returns base to the nth power; else, returns 1.
*
* Warning: Does not check for overflow or underflow.
*/
template<typename T>
T intpow(T const& base, T const& n);
}

#include "mymath.t"
#endif

The corresponding implementation (.t) file looks like this:

namespace mymath {

/* Seen by client code. No implementation is provided. */
template<typename T>
T intpow_impl(T const& base, T n);

namespace {

/* Hides ::mymath::intpow_impl. Invisible outside the
* current (unnamed) namespace. */
template<typename T>
T intpow_impl(T const& base, T n) {

T result = 1;

while (--n > 0) {
result *= base;
}

return result;
}

template<typename T>
T intpow(T const& base, T const& n) {
return intpow_impl(base, n);
}
}
}

The client code still cannot invoke the private function:

#include <iostream>

#include "mymath.h"

int main() {

/* This works. */
std::cout << mymath::intpow(3, 5) << '\n';

/* This causes a compile-time error. */
// int intpow_impl(int, int);
// intpow_impl(3, 5);

/* This causes a link error. */
// mymath::intpow_impl(3, 5);
}

In this example, the error is delayed until link-time; however, you
could provide the outer version of the implementation function with some
concept check that intentionally does not compile, but gives the client
a meaningful error message.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,071
Latest member
MetabolicSolutionsKeto

Latest Threads

Top