[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Wed, 24 Oct 2001 14:27:58 -0500 (CDT)
I have some questions on the matter of symbol conventions in glibc.
I haven't found any FAQs on the subject. Feel free to point me to one.
If there isn't one, I'd be happy to write one from collected answers.
The general problem of symbol conventions has cropped up in recent months:
Let me briefly desribe my goals. I am using a tool called Bypass
(http://www.cs.wisc.edu/condor/bypass) that allows one to reimplement a set
of library calls while still maintaining access to the original symbols.
We use this tool to create a location-independent execution environment
for programs running in a variety of distributed systems.
The general technique is to provide a new definition of the symbol
(e.g. write()) which flips a switch and carries out the new implementation.
If write() is invoked from within the new code, then we use dlsym() to
invoke the original libc write() to get the needed functionality. Of
course, this only works with DSOs.
Now, it is not simply the application's direct calls to write() that must
be trapped, but also it's indirect calls. If the application simply
uses stdio, we want to trap the write()s invoked by stdio. Here's where it
Much of the libc functionality is hidden behind stub symbols. For
example, there exists a weak (W) definition of write() which calls a public
(T) definition of __write(). However, this isn't consistent between all
sorts of calls. Looking at the source, we've identified three general
idioms for stubs in glibc:
Case A: Both user code and libc code share the public interface.
For example, the entry point to malloc() is public and is used both by users
and by stdio.
(T) malloc() <-- stdio/libio
Case B: User code calls a weak stub, while libc code uses an internal
(but visible) interface. For example, the write() symbol is directly
accessible by the user, while stdio invokes __write().
(W) write() stdio/libio
Case C: User code calls a weak stub, while libc code uses an internal
(and private) interface. For example, socket() is directly accessible
by the user, but gethostbyname invokes __socket().
(W) socket() gethostbyname
So for our purposes, case A is ideal. We can override the public symbol
in a portable way, and every piece of code sees the new symbol. No
problem. Lots of alternate malloc() libraries work this way.
Case B is less ideal. We can solve the problem by also overriding the
internal symbol. This is of course bad, because the code is then very
tightly tied to the particular implementation of libc and we understand
that there is no guarantee that __write() works in the same way as write().
Case C is the worst. In addition to the problem of case B, we can't
override the symbol in an online manner. As a workaround, we build and
use a glibc that exports these internal symbols.
So, the questions:
- Are these three different models intentional?
- If so, what is the rationale for each?
- Can we convince you that model A is preferable to B and C?
Anyway, I look forward to some discussion on the matter. As I said above,
whatever the answers, I'll be happy to collect them into a summary document.
On a final note, I should pass along some appreciation from many in my
research group to the glibc-maintainers. A free standard library enables
all sorts of research and debugging that is impossible on closed platforms.
- symbol conventions,
Douglas Thain <=