Discussion:
Evolution of ELF symbol management
Florian Weimer
2016-10-18 09:26:33 UTC
Permalink
In the ancient past, all glibc symbols, including internal ones, were
public. Then came symbol versioning, and hidden symbols.

This did not solve all problems. For static linking, we often have to
mangle symbols so that an implementation of a function in one standard
does not implicitly require the application to be conforming to another
standard (or standard version): the application might define a symbol
which is reserved by the other standard, and we cannot use this function
in the implementation.

For essentially the same reason, we need to mangle references in
non-libc DSOs to libc symbols which are not present in all of the
standards implied by all symbol references to the non-libc DSO.

Symbol versioning does not help here, for two reasons: We do not provide
symbol versions for static builds. For backwards compatibility reasons,
non-versioned symbols interpose all versioned symbols, irrespective of
their version (not just the base definition). The second reason is
important in practice; it is required for interposing malloc.

I think the above sums up the status quo. With this message, I want to
start a discussion why this symbol mangling stops at glibc-internal
cross-DSO references (or static linking). Wouldn't other system
libraries, such as libstdc++, glib, Qt and so on need to do the same
thing? After all, if Qt calls ***@GLIBC_2.31, and the main program
defines foo (which the static linker automatically exports to enable
interposition), we almost certainly would want Qt to continue to call
***@GLIBC_2.31, and not the potentially incompatible implementation of
foo in the main program.

To keep things simple, I suggest that for all new function symbols, we
declare __libc_foo in the header file, redirect foo to __libc_foo,
export both at the same symbol version from the DSO, and make __libc_foo
a strong definition and foo a weak one. (We should not add new variable
symbols.)

For existing symbols, we only do this if we receive reports of conflicts
causing problems in the field. In this case, we add __libc_foo and the
redirect to the header file, and use the current symbol version for the
__libc_foo export (not the one of foo).

Comments?

Thanks,
Florian
Joseph Myers
2016-10-18 16:49:53 UTC
Permalink
I think the above sums up the status quo. With this message, I want to start
a discussion why this symbol mangling stops at glibc-internal cross-DSO
references (or static linking). Wouldn't other system libraries, such as
libstdc++, glib, Qt and so on need to do the same thing? After all, if Qt
linker automatically exports to enable interposition), we almost certainly
incompatible implementation of foo in the main program.
We've previously discussed this in the libstdc++ context and I think
agreed that implementation-namespace versions should be added at least for
functions used in libstdc++ headers, to allow G++ to stop defining
_GNU_SOURCE by default
<https://gcc.gnu.org/bugzilla/show_bug.cgi?id=11196>
<https://gcc.gnu.org/bugzilla/show_bug.cgi?id=51749>
<https://sourceware.org/ml/libc-alpha/2012-03/msg00311.html>. I'd
consider the namespace issues to apply equally to all language runtime
libraries - so including any symbols used in libstdc++ but not in the
headers, for example, and in other language runtimes in GCC where there is
a meaningful question for the relevant languages of certain C symbol names
being reserved or not reserved. Language runtimes also include e.g.
libdfp (on which basis the printf hooks functionality should be exported
under implementation-namespace names).

Doing it more systematically for glibc function symbols rather than only
supporting it for particular privileged language runtimes seems reasonable
to me.
To keep things simple, I suggest that for all new function symbols, we declare
__libc_foo in the header file, redirect foo to __libc_foo, export both at the
same symbol version from the DSO, and make __libc_foo a strong definition and
foo a weak one. (We should not add new variable symbols.)
I take it this is __libc_foo independent of which library contains foo (so
no __libm_foo, __libpthread_foo etc.)?

There are a few existing __libc_foo exports at public symbol versions. Do
all those satisfy the rule that where both foo and __libc_foo exist, the
latest version of foo and the latest version of __libc_foo are aliases or
otherwise have the same semantics? (It would seem very confusing for old
and new __libc_* symbols to follow different rules in that regard.)

What should be done where the symbol is only added in the implementation
namespace - symbols for use in redirection for different standard
versions, macros, inline functions or *_nonshared.a, for example? Should
future such symbols also use the __libc_foo namespace (unless there are
ABI reasons for something else, e.g. the libmvec functions) (so if such a
practice were implemented before glibc 2.25 came out, __iscanonicall would
change to __libc_iscanonicall, etc.), or continue being __foo?

What about compilers that do not support redirection? Right now we have
many individual #defines in the case where __REDIRECT is not supported.
If we required support for asm redirection in compilers using the glibc
headers, it would be possible to define a macro to declare both foo and
__libc_foo, with the same type and the same attributes (and the same throw
() information for C++), and do the redirection, all with one macro call.
Otherwise you get a lot of repetitive boilerplate in headers for every
such function, since a macro cannot generate a #define of another macro.
Or you say that compilers without redirection support don't get any of
these redirections, since they are not semantically required.

(When you're dealing with API issues as well as ABI then the macro
solution runs into complications with wanting to declare __libc_foo
unconditionally for use in libstdc++ headers, but foo only when the right
feature test macros are defined. Those complications can certainly be
resolved, e.g. with macros __<something>_GNU to do the declaration whose
definitions depend on the feature test macros defined, and a first
solution might well only deal with the ABI issues and leave the API ones
for later.)

Being able to make all the declarations with a single macro is attractive,
since right now I'm sure that lots of the declarations in internal
include/ headers are in fact suboptimal because they are missing
attributes present on the public declarations. It would also have the
potential for defining variants of such macros in future that also do
*_hidden_proto (for public and internal function names) when building
glibc. Recall that *_hidden_* are still needed even for internal function
names, whether or not those names are exported - if exported, failure to
use *_hidden_* will be visible through localplt test failures, but if not
exported, less efficient code is still generated in the caller on 32-bit
x86 if the function isn't visibly hidden
<https://sourceware.org/bugzilla/show_bug.cgi?id=18822>.

(In turn, that would allow us to move towards the desired direction of
eliminating most of the include/ header wrappers so tests build in
something much more like a normal installed glibc environment. I think
it's already understood that declarations that aren't just hidden_proto
(foo) or declaring __foo for a function foo in the public header ought to
go in an entirely separate header, not one of those wrappers, and
appropriate macros for function declarations in installed headers could
allow eliminating the remaining appropriate contents from the wrappers -
subject to the issue of the declarations there being for old-style names
for internal functions, not for __libc_*.)

Features that are de facto required for using glibc headers already
include (non-exhaustive list):

* C89 or C++98.

* long long.

* Flexible array members - including the ability for a struct with a
flexible array member to be followed by another member in a containing
struct, which is not a standard C feature. (See _G_config.h's _G_iconv_t;
struct __gconv_info has a flexible array member and is followed by another
member in a struct. I don't know if use of a compiler that gets the
fallback array[1] for a flexible array member would result in any ABI
issues for a user of glibc, or if the ABI in question is purely internal.)

* Some headers require anonymous structs / unions.

* Various macros in various headers may also require other features such
as __typeof and statement expressions.

In fact we have evidence
<https://sourceware.org/ml/libc-alpha/2014-09/msg00017.html> that the
headers have had problems for a long time for compilers not defining
__GNUC__, and those include problems relating to redirection.
For existing symbols, we only do this if we receive reports of conflicts
causing problems in the field. In this case, we add __libc_foo and the
redirect to the header file, and use the current symbol version for the
__libc_foo export (not the one of foo).
"causing problems in the field" should be broadly interpreted there - to
allow adding lots of such functions if someone identifies what's needed to
make the libstdc++ headers or libraries namespace-clean, for example, or
for fixing the namespace issues described in
<https://sourceware.org/bugzilla/show_bug.cgi?id=14106>.

What should be done in the case where __foo already has an export at a
public symbol version (and we have a use for __libc_foo)? Should we
arrange for __foo to be declared (with associated redirections) and say
people should be using that, or add __libc_foo as well? What about where
__foo is already exported, but that export is a compat symbol (if there
are any such cases)? Making it not a compat symbol would run into needing
new exports at new versions on platforms postdating the version where it
was made a compat symbol, and you don't want the API to be __foo on some
platforms and __libc_foo on others.
--
Joseph S. Myers
***@codesourcery.com
Florian Weimer
2016-10-25 14:32:04 UTC
Permalink
Post by Joseph Myers
I think the above sums up the status quo. With this message, I want to start
a discussion why this symbol mangling stops at glibc-internal cross-DSO
references (or static linking). Wouldn't other system libraries, such as
libstdc++, glib, Qt and so on need to do the same thing? After all, if Qt
linker automatically exports to enable interposition), we almost certainly
incompatible implementation of foo in the main program.
We've previously discussed this in the libstdc++ context and I think
agreed that implementation-namespace versions should be added at least for
functions used in libstdc++ headers, to allow G++ to stop defining
_GNU_SOURCE by default
<https://gcc.gnu.org/bugzilla/show_bug.cgi?id=11196>
<https://gcc.gnu.org/bugzilla/show_bug.cgi?id=51749>
<https://sourceware.org/ml/libc-alpha/2012-03/msg00311.html>. I'd
consider the namespace issues to apply equally to all language runtime
libraries - so including any symbols used in libstdc++ but not in the
headers, for example, and in other language runtimes in GCC where there is
a meaningful question for the relevant languages of certain C symbol names
being reserved or not reserved. Language runtimes also include e.g.
libdfp (on which basis the printf hooks functionality should be exported
under implementation-namespace names).
Anything providing a plug-in framework is probably a language run-time
in this sense.
Post by Joseph Myers
Doing it more systematically for glibc function symbols rather than only
supporting it for particular privileged language runtimes seems reasonable
to me.
Fine.
Post by Joseph Myers
To keep things simple, I suggest that for all new function symbols, we declare
__libc_foo in the header file, redirect foo to __libc_foo, export both at the
same symbol version from the DSO, and make __libc_foo a strong definition and
foo a weak one. (We should not add new variable symbols.)
I take it this is __libc_foo independent of which library contains foo (so
no __libm_foo, __libpthread_foo etc.)?
Yes, this was my intent. I'm not fixated on a particular prefix. I
just want to avoid a new discussion for each symbol.
Post by Joseph Myers
There are a few existing __libc_foo exports at public symbol versions. Do
all those satisfy the rule that where both foo and __libc_foo exist, the
latest version of foo and the latest version of __libc_foo are aliases or
otherwise have the same semantics? (It would seem very confusing for old
and new __libc_* symbols to follow different rules in that regard.)
I found a few symbols which differs in the exported version. The
unprefixed symbol has a regular version, and the prefixed one appears as
GLIBC_PRIVATE. These are:

clntudp_bufcreate
fork
longjmp
pread
pwrite
secure_getenv
siglongjmp
system
vfork

__libc_clntudp_bufcreate is GLIBC_PRIVATE and takes an additional
argument compared to clntudp_bufcreate, so that's a true mismatch. It's
used from libnsl.so.

fork, longjmp, siglongjmp, vfork are aliases for the redirection from
libpthread. They cannot use the usual __ alias because they define that
as well.

I don't know why pread, pwrite have __libc_-prefixed aliases instead of
__-prefixed ones. I think the goal is namespace-cleanliness, so either
one would work.

Unfortunately, my current symbol lists do not include symbol values, so
finding further discrepancies (which have the same version, but
different values) is not entirely straightforward. I'll see what I can
do to get more data (probably using some Perl script; unfortunately
dlysm is currently broken in the presence of multiple symbol versions).
Post by Joseph Myers
What should be done where the symbol is only added in the implementation
namespace - symbols for use in redirection for different standard
versions, macros, inline functions or *_nonshared.a, for example? Should
future such symbols also use the __libc_foo namespace (unless there are
ABI reasons for something else, e.g. the libmvec functions) (so if such a
practice were implemented before glibc 2.25 came out, __iscanonicall would
change to __libc_iscanonicall, etc.), or continue being __foo?
I have no strong opinion here. My reason for using __libc_ instead of
__ was that other libraries use __ for internal symbols. With __libc_,
collisions seem even less likely.
Post by Joseph Myers
What about compilers that do not support redirection?
I worry more about things like mlnlffigen or SWIG which parse header
files and attempt to generate bindings from them. Our current header
files are already rather difficult to process by such tools, which often
contain heuristics, in part to avoid implementing full C, in part to get
some sort of API out of the header file even if parts of it lives in
preprocessor macros only.
Post by Joseph Myers
Right now we have
many individual #defines in the case where __REDIRECT is not supported.
If we required support for asm redirection in compilers using the glibc
headers, it would be possible to define a macro to declare both foo and
__libc_foo, with the same type and the same attributes (and the same throw
() information for C++), and do the redirection, all with one macro call.
Otherwise you get a lot of repetitive boilerplate in headers for every
such function, since a macro cannot generate a #define of another macro.
Or you say that compilers without redirection support don't get any of
these redirections, since they are not semantically required.
Unfortunately, I forgot an important detail:

Even in the __GNUC__ case, we need more than just the declaration. If
we just set an asm alias, an interposing definition supplied by the user
will happily interpose the supposedly-protected alias.

This is less relevant for functions in non-standard headers (which
applications would not include accidentally), but if we add something to
<stdio.h> (under _GNU_SOURCE) which is ripe for collisions, we need to
somehow make sure that a user-defined function of the same name does not
end up interposing the alias.

The easiest way to do this is to add a function-style macro which
expends to something that cannot be parsed as part of a function
definition. We discussed using inline functions for this, but this
appraoch was rejected due to standards compliance concerns. But maybe
the desire to put all this in a single macro definition will make us
reconsider. On the hand, inline functions are particularly hard on some
wrapper generators. The internal binding generator of LuaJIT, for
example, supports asm aliases, but ignores inline functions.
Post by Joseph Myers
(When you're dealing with API issues as well as ABI then the macro
solution runs into complications with wanting to declare __libc_foo
unconditionally for use in libstdc++ headers, but foo only when the right
feature test macros are defined. Those complications can certainly be
resolved, e.g. with macros __<something>_GNU to do the declaration whose
definitions depend on the feature test macros defined, and a first
solution might well only deal with the ABI issues and leave the API ones
for later.)
Yes, please. :)
Post by Joseph Myers
Being able to make all the declarations with a single macro is attractive,
since right now I'm sure that lots of the declarations in internal
include/ headers are in fact suboptimal because they are missing
attributes present on the public declarations. It would also have the
potential for defining variants of such macros in future that also do
*_hidden_proto (for public and internal function names) when building
glibc. Recall that *_hidden_* are still needed even for internal function
names, whether or not those names are exported - if exported, failure to
use *_hidden_* will be visible through localplt test failures, but if not
exported, less efficient code is still generated in the caller on 32-bit
x86 if the function isn't visibly hidden
<https://sourceware.org/bugzilla/show_bug.cgi?id=18822>.
With consistent internal and external mangling (which was not the goal
of my proposal, but I can see how it is related), we could compile
sources within libc and outside, for unit testing purposes. It would
also make it easier to avoid linknamespace violations. But that needs
new aliases for basically everything.
Post by Joseph Myers
In fact we have evidence
<https://sourceware.org/ml/libc-alpha/2014-09/msg00017.html> that the
headers have had problems for a long time for compilers not defining
__GNUC__, and those include problems relating to redirection.
Ugh.

Part of the problem is that we don't have a C89 or C++98 implementation
with which we can test easily. I'm not aware of any way to turn GCC or
Clang into a C89-only or a C++98-only compiler (and you said we'd need
some extensions anyway).
Post by Joseph Myers
For existing symbols, we only do this if we receive reports of conflicts
causing problems in the field. In this case, we add __libc_foo and the
redirect to the header file, and use the current symbol version for the
__libc_foo export (not the one of foo).
"causing problems in the field" should be broadly interpreted there - to
allow adding lots of such functions if someone identifies what's needed to
make the libstdc++ headers or libraries namespace-clean, for example, or
for fixing the namespace issues described in
<https://sourceware.org/bugzilla/show_bug.cgi?id=14106>.
Yes, I would assume this is fine.
Post by Joseph Myers
What should be done in the case where __foo already has an export at a
public symbol version (and we have a use for __libc_foo)? Should we
arrange for __foo to be declared (with associated redirections) and say
people should be using that, or add __libc_foo as well? What about where
__foo is already exported, but that export is a compat symbol (if there
are any such cases)? Making it not a compat symbol would run into needing
new exports at new versions on platforms postdating the version where it
was made a compat symbol, and you don't want the API to be __foo on some
platforms and __libc_foo on others.
I think this leads to the question whether we should prefer __ over
__libc_ after all because as part of fixing the glibc-internal
linknamespace issues, we often added a __ symbol with a public version
(but sometimes a GLIBC_PRIVATE version as well). I would prefer if we
could reuse those symbol names. I don't think we can switch them to
__libc_ because they were part of the ABI, although they were not part
of any header file.

I'm less convinced now in which direction to move. Unfortunately, this
has ABI impact, so the consequences are far from trivial.

Maybe we need to take a step back and ask ourselves if we should use
symbol versioning to address this. The two blockers I know of are
purely static links, and the design decision (no doubt for backwards
compatibility) to interpose versioned symbols with unversioned symbols.
The latter is difficult to address, but if we could make the change
somehow, it would enable a nice performance boost in the dynamic linker,
too. But it certainly looks like that for the static link case, we only
have the header files we can tweak to achieve what we want.

Thanks,
Florian
Joseph Myers
2016-10-25 15:37:35 UTC
Permalink
Post by Joseph Myers
There are a few existing __libc_foo exports at public symbol versions. Do
all those satisfy the rule that where both foo and __libc_foo exist, the
latest version of foo and the latest version of __libc_foo are aliases or
otherwise have the same semantics? (It would seem very confusing for old
and new __libc_* symbols to follow different rules in that regard.)
I found a few symbols which differs in the exported version. The unprefixed
symbol has a regular version, and the prefixed one appears as GLIBC_PRIVATE.
clntudp_bufcreate
fork
longjmp
pread
pwrite
secure_getenv
siglongjmp
system
vfork
My concern is mainly about __libc_* symbols at public versions, not
GLIBC_PRIVATE, since we can freely change the ABIs for __libc_* at
GLIBC_PRIVATE if those are confusing.
Post by Joseph Myers
Right now we have
many individual #defines in the case where __REDIRECT is not supported.
If we required support for asm redirection in compilers using the glibc
headers, it would be possible to define a macro to declare both foo and
__libc_foo, with the same type and the same attributes (and the same throw
() information for C++), and do the redirection, all with one macro call.
Otherwise you get a lot of repetitive boilerplate in headers for every
such function, since a macro cannot generate a #define of another macro.
Or you say that compilers without redirection support don't get any of
these redirections, since they are not semantically required.
Even in the __GNUC__ case, we need more than just the declaration. If we just
set an asm alias, an interposing definition supplied by the user will happily
interpose the supposedly-protected alias.
This is less relevant for functions in non-standard headers (which
applications would not include accidentally), but if we add something to
<stdio.h> (under _GNU_SOURCE) which is ripe for collisions, we need to somehow
make sure that a user-defined function of the same name does not end up
interposing the alias.
Same name and type, that is; if the type is wrong and the header
declaration is visible, a compile-time error will occur.
The easiest way to do this is to add a function-style macro which expends to
something that cannot be parsed as part of a function definition. We
discussed using inline functions for this, but this appraoch was rejected due
to standards compliance concerns. But maybe the desire to put all this in a
single macro definition will make us reconsider. On the hand, inline
functions are particularly hard on some wrapper generators. The internal
binding generator of LuaJIT, for example, supports asm aliases, but ignores
inline functions.
There is always the option of having the installed headers be generated
files, so the source tree has .h.in files that contain some sort of
annotations for use by a special glibc-specific preprocessor that does
things the C preprocessor cannot - converting something that looks like a
C macro call (say) into function declarations, __REDIRECT calls - and
function-like macro definitions. Of course then you need to get those
headers generated at an early stage in the glibc build.

Or define a new function attribute whose meaning is that the asm
redirection is disregarded if defining the function, but then user code
would only be protected against accidental interposition when built with a
compiler supporting that new attribute.
I think this leads to the question whether we should prefer __ over __libc_
after all because as part of fixing the glibc-internal linknamespace issues,
we often added a __ symbol with a public version (but sometimes a
We shouldn't have added them with public versions, just internally (and
only at GLIBC_PRIVATE if needed by a separate library from the
definition).
--
Joseph S. Myers
***@codesourcery.com
Florian Weimer
2016-11-21 15:35:36 UTC
Permalink
Post by Joseph Myers
Post by Joseph Myers
There are a few existing __libc_foo exports at public symbol versions. Do
all those satisfy the rule that where both foo and __libc_foo exist, the
latest version of foo and the latest version of __libc_foo are aliases or
otherwise have the same semantics? (It would seem very confusing for old
and new __libc_* symbols to follow different rules in that regard.)
I found a few symbols which differs in the exported version. The unprefixed
symbol has a regular version, and the prefixed one appears as GLIBC_PRIVATE.
clntudp_bufcreate
fork
longjmp
pread
pwrite
secure_getenv
siglongjmp
system
vfork
My concern is mainly about __libc_* symbols at public versions, not
GLIBC_PRIVATE, since we can freely change the ABIs for __libc_* at
GLIBC_PRIVATE if those are confusing.
I put together the attached Python script to check for collisions. It
reports anything that is not UNDEF or GLIBC_PRIVATE and where the
__libc_-prefixed and non-prefixed symbols have different values. It
reports some mismatches in libasan, so I think it works. It does not
flag anything for glibc on i386 and x86_64 with current master.

The script has a hard-coded path to elfutils readelf, it needs Mark's
recent addition of the --symbols=SECTION argument.

So I think we are good on this front.
Post by Joseph Myers
This is less relevant for functions in non-standard headers (which
applications would not include accidentally), but if we add something to
<stdio.h> (under _GNU_SOURCE) which is ripe for collisions, we need to somehow
make sure that a user-defined function of the same name does not end up
interposing the alias.
Same name and type, that is; if the type is wrong and the header
declaration is visible, a compile-time error will occur.
Good point. We could add artificial transparent unions to arguments to
make it harder to write a matching definition, even with current GCC
versions.
Post by Joseph Myers
There is always the option of having the installed headers be generated
files, so the source tree has .h.in files that contain some sort of
annotations for use by a special glibc-specific preprocessor that does
things the C preprocessor cannot - converting something that looks like a
C macro call (say) into function declarations, __REDIRECT calls - and
function-like macro definitions. Of course then you need to get those
headers generated at an early stage in the glibc build.
Interesting idea.

We could generate a different set of such headers of internal glibc user
if required. This could allow us to compile more parts of glibc as
standard C sources, without mangling of public symbols, which would help
with things like unit testing and fuzz testing.
Post by Joseph Myers
I think this leads to the question whether we should prefer __ over __libc_
after all because as part of fixing the glibc-internal linknamespace issues,
we often added a __ symbol with a public version (but sometimes a
We shouldn't have added them with public versions, just internally (and
only at GLIBC_PRIVATE if needed by a separate library from the
definition).
It's a bit too late for that, unfortunately.

Florian
Joseph Myers
2016-10-26 12:17:21 UTC
Permalink
Further consideration of the __libc_* approach:

There are some cases that should never need public aliases: (a) ISO C90
functions (no aliases needed unless called from macros defined in system
headers - such macros should properly call C90 functions by aliases in
case they are shadowed by local variables in the function calling the
macro; the function names are reserved with file scope / external linkage,
but not as local variable names); (b) obsolete functions (even if we still
support linking with a deprecated function because it's in an older
standard we support, we could say namespace-clean use of it isn't
supported); (c) if multiple public functions are or should be aliased to
each other, e.g. for long double = double, we could have just one __libc_*
alias between them.
Maybe we need to take a step back and ask ourselves if we should use symbol
versioning to address this. The two blockers I know of are purely static
links, and the design decision (no doubt for backwards compatibility) to
interpose versioned symbols with unversioned symbols. The latter is difficult
to address, but if we could make the change somehow, it would enable a nice
performance boost in the dynamic linker, too. But it certainly looks like
that for the static link case, we only have the header files we can tweak to
achieve what we want.
And symbol versioning cannot help with the API issue of C++ compilations
being forced to use _GNU_SOURCE because much of the libstdc++
implementation is in the headers and wants to use libc features outside
the libc subset defined in the C++ standard. That requires public API
aliases for lots of functions (maybe for constants, types etc. as well).
--
Joseph S. Myers
***@codesourcery.com
Mike Frysinger
2016-11-20 11:13:04 UTC
Permalink
Post by Florian Weimer
Post by Joseph Myers
In fact we have evidence
<https://sourceware.org/ml/libc-alpha/2014-09/msg00017.html> that the
headers have had problems for a long time for compilers not defining
__GNUC__, and those include problems relating to redirection.
Ugh.
Part of the problem is that we don't have a C89 or C++98 implementation
with which we can test easily. I'm not aware of any way to turn GCC or
Clang into a C89-only or a C++98-only compiler (and you said we'd need
some extensions anyway).
you mean specifically non-GNU extensions ? does having gcc-2.95.3 help ?

this might work for x86 systems:
https://dev.gentoo.org/~vapier/gcc-2.95.3-r10.tbz2
if it helps, i could make it a bit more standalone.
-mike
Florian Weimer
2016-11-21 10:11:56 UTC
Permalink
Post by Mike Frysinger
Post by Florian Weimer
Post by Joseph Myers
In fact we have evidence
<https://sourceware.org/ml/libc-alpha/2014-09/msg00017.html> that the
headers have had problems for a long time for compilers not defining
__GNUC__, and those include problems relating to redirection.
Ugh.
Part of the problem is that we don't have a C89 or C++98 implementation
with which we can test easily. I'm not aware of any way to turn GCC or
Clang into a C89-only or a C++98-only compiler (and you said we'd need
some extensions anyway).
you mean specifically non-GNU extensions ? does having gcc-2.95.3 help ?
No, because most of the the GNU stuff is already in that version.

I have a 2.7.2 build somewhere, and checked recently that at least some
of the headers still work with it. But that doesn't tell us all that
much about C89 compatibility.

Florian
Zack Weinberg
2016-11-16 15:55:00 UTC
Permalink
With this message, I want to start a discussion why this symbol
mangling stops at glibc-internal cross-DSO references (or static
linking). Wouldn't other system libraries, such as libstdc++, glib,
Qt and so on need to do the same thing? After all, if Qt calls
linker automatically exports to enable interposition), we almost
the potentially incompatible implementation of foo in the main
program.
To keep things simple, I suggest that for all new function symbols,
we declare __libc_foo in the header file, redirect foo to
__libc_foo, export both at the same symbol version from the DSO, and
make __libc_foo a strong definition and foo a weak one. (We should
not add new variable symbols.)
For existing symbols, we only do this if we receive reports of
conflicts causing problems in the field. In this case, we add
__libc_foo and the redirect to the header file, and use the current
symbol version for the __libc_foo export (not the one of foo).
I meant to respond to this last month but have not been able to scrape
time away from $DAYJOB until this week. Apologies.

First, I don't have a problem with adding __libc_* aliases as
non-default but public alternative names, so that third-party libraries
can avoid namespace pollution in static linkage and/or choose to refer
to symbols that are more likely to get resolved to the C library's
export than to a colliding symbol in the main executable. I'd reserve
that for cases where there is already a demonstrated need, though, such
as libstdc++. I also don't have a problem with a hypothetical policy
that all new symbols that aren't in the current revisions of C+POSIX
should be given an impl-namespace name as their primary definition, with
the user-namespace name being a weak alias (but still the only name used
in the public headers).

I _do_ have problems with causing these symbols to be used by default.
My main concern is that I think it's tackling the problem in the wrong
place. If we want to back away from the original principle that the
executable always wins, isn't an expanded version of -Bsymbolic mode
what's _really_ wanted? That is, an opt-in link-time declaration that
you want all dynamic symbols resolved at load-time to the same library
that provided them at link-time. Once that exists, we could try to move
slowly in the direction of turning it on by default, along with various
other "yes, fix the bug" linker toggles like -z relro and --as-needed.
Until and unless this possibility has been thoroughly explored and
determined unworkable, I will object to bulk addition of __libc_*
aliases, and to an "all new public prototypes must redirect to a
__libc_* alias" rule.

A secondary reason for not wanting __libc_* aliases to be activated by
default is that it doesn't seem to me that it actually solves the
problem. Applications that _want_ to interpose are going to shift to
the __libc_* names; depending on what the headers actually do,
applications that don't care (but do define a conflicting symbol) might
wind up shifting to those names by accident. __REDIRECT doesn't work
without GCC(-compatible) extensions, after all. (I would not object to
a _documented and enforced_ set of GCC-compatible extensions being a
baseline requirement for use of glibc headers -- but that would need to
happen _first_.)

zw
Florian Weimer
2016-11-18 15:48:08 UTC
Permalink
Post by Zack Weinberg
First, I don't have a problem with adding __libc_* aliases as
non-default but public alternative names, so that third-party libraries
can avoid namespace pollution in static linkage and/or choose to refer
to symbols that are more likely to get resolved to the C library's
export than to a colliding symbol in the main executable. I'd reserve
that for cases where there is already a demonstrated need, though, such
as libstdc++. I also don't have a problem with a hypothetical policy
that all new symbols that aren't in the current revisions of C+POSIX
should be given an impl-namespace name as their primary definition, with
the user-namespace name being a weak alias (but still the only name used
in the public headers).
If we don't declare the library-safe names in headers, how can libraries
call them?

I also do not want to encourage application or library code to reference
the implementation namespace at the source code level. It's ugly, and I
suspect it encourages implementation namespace pollution once
programmers are used to it.
Post by Zack Weinberg
I _do_ have problems with causing these symbols to be used by default.
My main concern is that I think it's tackling the problem in the wrong
place. If we want to back away from the original principle that the
executable always wins, isn't an expanded version of -Bsymbolic mode
what's _really_ wanted?
We could cover a lot of ground if we had a new flag on versioned symbol
definitions which tells the static linker to set a flag on the versioned
symbol reference, and the dynamic linker would then use this flag to
ignore unversioned symbols for binding symbols.

(The static linker currently does not add the version of the interposed
symbol when interposition happens at static link time.)

*However*, this is hardly a complete solution. It does not cover symbol
references from public C++ headers because there is no static linker
invocation that comes between application use of the symbol and the use
from system headers.

It also does not work for static libraries. In those cases, we could
perhaps do some post-processing to add the symbol versions to the .a
files, but it would still need the interposition protection mentioned
above *and* changes to how we build static libraries.
Post by Zack Weinberg
That is, an opt-in link-time declaration that
you want all dynamic symbols resolved at load-time to the same library
that provided them at link-time. Once that exists, we could try to move
slowly in the direction of turning it on by default, along with various
other "yes, fix the bug" linker toggles like -z relro and --as-needed.
The build changes I mentioned in the previous paragraph are more
invasive than just adding a flag to gcc's ld invocation because there is
currently no such invocation at all. We could put it into ar or ranlib,
but that's perhaps too much magic. A new linking step would be needed.

The header files could contain the symbol version and instruct GCC to
put it into the header file. (Even today, it is possible to link
against specific symbol versions without a custom DSO containing them as
the default version, but please don't tell anyone.) But this would
still need the interposition protection. I still think this is the most
promising option, all things considered.

But then we need to step back and ask ourselves: If we have to put the
versioning information in the header, why do we even need symbol
versioning? Why can't we version the interface through its name?

Hence my proposal.
Post by Zack Weinberg
A secondary reason for not wanting __libc_* aliases to be activated by
default is that it doesn't seem to me that it actually solves the
problem. Applications that _want_ to interpose are going to shift to
the __libc_* names; depending on what the headers actually do,
applications that don't care (but do define a conflicting symbol) might
wind up shifting to those names by accident.
What I would like is a GCC attribute which tells the compiler that the
declaration must not be completed with a definition in this translation
unit. That would make the asm aliases pretty robust because then you
can only reach the implementation namespace by explicitly referencing it
in your sources.

Deliberate interposition is much easier if you don't have to worry about
linker tricks and the unversioned-symbol-interposes-all-versioned-symbol
issue (which you cannot detect in the interposing DSO).
Post by Zack Weinberg
__REDIRECT doesn't work
without GCC(-compatible) extensions, after all. (I would not object to
a _documented and enforced_ set of GCC-compatible extensions being a
baseline requirement for use of glibc headers -- but that would need to
happen _first_.)
I wouldn't like that, there are many consumers for header files which
aren't compilers (and would not be forced to implement many GNU extensions).

Thanks,
Florian
Zack Weinberg
2016-11-19 17:25:00 UTC
Permalink
Post by Florian Weimer
Post by Zack Weinberg
First, I don't have a problem with adding __libc_* aliases as
non-default but public alternative names, so that third-party libraries
can avoid namespace pollution in static linkage and/or choose to refer
to symbols that are more likely to get resolved to the C library's
export than to a colliding symbol in the main executable. I'd reserve
that for cases where there is already a demonstrated need, though, such
as libstdc++. I also don't have a problem with a hypothetical policy
that all new symbols that aren't in the current revisions of C+POSIX
should be given an impl-namespace name as their primary definition, with
the user-namespace name being a weak alias (but still the only name used
in the public headers).
If we don't declare the library-safe names in headers, how can libraries
call them?
We could _declare_ the library-safe names in the headers, just not as
the primaries. Like how string.h currently declares both bzero and __bzero.

Incidentally, it occurrs to me that the user-namespace name must exist,
for the sake of people using dlsym(RTLD_DEFAULT, "whatever") to access
symbols that they anticipate existing in future revisions of libc
(relative to the one they used at link time).
Post by Florian Weimer
I also do not want to encourage application or library code to reference
the implementation namespace at the source code level. It's ugly, and I
suspect it encourages implementation namespace pollution once
programmers are used to it.
I don't like it either, but how else could a library's headers opt into
these special names on a per-symbol, per-use basis?

Come to think of it, to actually avoid polluting the user namespace, any
library that wants to use these will need a secondary set of libc
headers that declare _only_ the private names. (This is especially
relevant for C++ with so much code in headers.) If we don't do that,
the user-namespace libc prototype (which still exists under your plan)
might conflict with an unrelated application definition.

I don't like _that_, because now we have to maintain multiple copies of
the same set of prototypes with different names *in different headers*.
But again, I don't see an alternative in C. Maybe we could get
'namespace', 'using', and 'extern "C++"' added to C as a GCC extension?
That would _help_. In fact, that would solve all kinds of problems.
(But we'd have to give up the pretense that our headers work with
anything but GCC.)
Post by Florian Weimer
Post by Zack Weinberg
I _do_ have problems with causing these symbols to be used by default.
My main concern is that I think it's tackling the problem in the wrong
place. If we want to back away from the original principle that the
executable always wins, isn't an expanded version of -Bsymbolic mode
what's _really_ wanted?
We could cover a lot of ground if we had a new flag on versioned symbol
definitions which tells the static linker to set a flag on the versioned
symbol reference, and the dynamic linker would then use this flag to
ignore unversioned symbols for binding symbols.
I was imagining a new annotation on _all_ undefined symbols in a shared
object, giving the soname of the object that they were satisfied by at
link time. At load time, 'getrandom!libc.so.6' resolves to the
'getrandom' definition in libc.so.6, ignoring all other definitions of
the same name. If there are symbol versions involved, only the versions
exported by libc.so.6 are considered. For instance,
Post by Florian Weimer
(The static linker currently does not add the version of the interposed
symbol when interposition happens at static link time.)
I don't understand this statement, and that makes me worry that you are
trying to solve a different problem from the one I thought you were
talking about -- a problem that I might not even know exists. Can you
elaborate, please? How can a shared object be interposed upon at static
link time? Its own static link has already happened!
Post by Florian Weimer
*However*, this is hardly a complete solution. It does not cover symbol
references from public C++ headers because there is no static linker
invocation that comes between application use of the symbol and the use
from system headers.
See above.
Post by Florian Weimer
It also does not work for static libraries. In those cases, we could
perhaps do some post-processing to add the symbol versions to the .a
files, but it would still need the interposition protection mentioned
above *and* changes to how we build static libraries.
Yes, to apply my proposal to static libraries, a new linking step would
be needed, functionally the same as the one that already exists for
shared libraries, but with different effects -- either adding a new
archive member with a bunch of annotations, or rewriting all the .o
files with those annotations.

Frankly I don't care very much about static libraries; I'd be fine with
allowing them to continue to work as they do now (it is already the
case, for instance, that the executable can interpose on internal libc
symbols in a static link that are inaccessible to it in a shared link).
Post by Florian Weimer
The header files could contain the symbol version and instruct GCC to
put it into the header file. (Even today, it is possible to link
against specific symbol versions without a custom DSO containing them as
the default version, but please don't tell anyone.) But this would
still need the interposition protection. I still think this is the most
promising option, all things considered.
But then we need to step back and ask ourselves: If we have to put the
versioning information in the header, why do we even need symbol
versioning? Why can't we version the interface through its name?
Hence my proposal.
You keep talking about symbol versioning but, again, I don't understand
how symbol versioning is relevant, and that makes me worry that you're
trying to solve a different problem that I don't even know about. I
thought the issue here was controlling *which library* provides a
symbol, independent of whether the symbol has versions.

As for what appears in the headers -- again, see above: to solve the
libstdc++/_GNU_SOURCE problem, which I believe I *do* understand, there
needs to be a set of alternative names that libstdc++ headers can
*explicitly* refer to; otherwise we have still polluted the user
namespace. REDIRECTed declarations do not solve that problem. In fact,
with my proposed which-library annotations, we could perfectly well
redirect the mangled names to the normal names:

extern ssize_t __libc_getrandom (void *, size_t, unsigned int)
__asm ("getrandom") __attribute__ ((__bind_to_library ("libc.so.6")));

#ifdef __USE_GNU
extern typeof (__libc_getrandom) getrandom;
#endif

----

All this aside, this discussion is still very brainstormy and that makes
me think that we should *not* yet be supplying mangled names for public
use. Once we start doing that we are stuck with it forever, after all.
Contrariwise, we *can* always retrofit __libc_* aliases or whatever once
we know what we ought to be doing.

Similarly, this should not be a blocker issue for new 2.25 features. (On
that note, cc:ing Siddhesh with their release manager hat on.)

zw
Florian Weimer
2016-11-22 15:09:50 UTC
Permalink
Post by Zack Weinberg
Post by Florian Weimer
Post by Zack Weinberg
First, I don't have a problem with adding __libc_* aliases as
non-default but public alternative names, so that third-party libraries
can avoid namespace pollution in static linkage and/or choose to refer
to symbols that are more likely to get resolved to the C library's
export than to a colliding symbol in the main executable. I'd reserve
that for cases where there is already a demonstrated need, though, such
as libstdc++. I also don't have a problem with a hypothetical policy
that all new symbols that aren't in the current revisions of C+POSIX
should be given an impl-namespace name as their primary definition, with
the user-namespace name being a weak alias (but still the only name used
in the public headers).
If we don't declare the library-safe names in headers, how can libraries
call them?
We could _declare_ the library-safe names in the headers, just not as
the primaries. Like how string.h currently declares both bzero and __bzero.
I have no idea at all why __bzero was introduced. It doesn't look like
a particularly compelling example (similar to __secure_getenv). It may
have been a SunOS thing.
Post by Zack Weinberg
Incidentally, it occurrs to me that the user-namespace name must exist,
for the sake of people using dlsym(RTLD_DEFAULT, "whatever") to access
symbols that they anticipate existing in future revisions of libc
(relative to the one they used at link time).
There also configure scripts which do

extern char whatever();
main () { whatever(); }

for link testing, without including the appropriate header (perhaps
deliberately). That could be considered broken. I'm not sure.
Post by Zack Weinberg
Post by Florian Weimer
I also do not want to encourage application or library code to reference
the implementation namespace at the source code level. It's ugly, and I
suspect it encourages implementation namespace pollution once
programmers are used to it.
I don't like it either, but how else could a library's headers opt into
these special names on a per-symbol, per-use basis?
In general, headers should avoid using libc types, particularly off_t,
time_t, struct timeval, struct stat, and so on. But there might be
exceptions.

For C, it's not clear at all to me whether we need any kind of opt-in
besides compiling with a particular _*_SOURCE variant (which introduces
the definition). We accept that switching to a new glibc version
requires adjusting source code across the system:

<https://sourceware.org/ml/libc-alpha/2016-10/msg00307.html>

We can't provide complete source level compatibility on updates, and for
things compiled with _GNU_SOURCE, it seems to be an explicit non-goal.
At least that's how I read the previous discussion.

For C++, we might use something based on namespaces to get a clear
separation. However, the problem there is that type names and struct
tags end up in C++ mangled identifiers and thus impact application ABI.
I have no good idea what to do there.

But to be honest, I was mainly concentrating on function symbols. I
understand that other identifiers also present problems. But with
those, you can work around issues by reducing the size of translation
units, at least for C.
Post by Zack Weinberg
Come to think of it, to actually avoid polluting the user namespace, any
library that wants to use these will need a secondary set of libc
headers that declare _only_ the private names. (This is especially
relevant for C++ with so much code in headers.) If we don't do that,
the user-namespace libc prototype (which still exists under your plan)
might conflict with an unrelated application definition.
Yes, that's what using a namespace for C++ would achieve.

I'm less convinced we should do this for C. There is precedent,
Microsoft did exactly this for the POSIX-inspired interfaces in their
libc (functions like _open, _close and so on).
Post by Zack Weinberg
I don't like _that_, because now we have to maintain multiple copies of
the same set of prototypes with different names *in different headers*.
See Josephs' idea about auto-generating public headers. It's definitely
a requirement for this.
Post by Zack Weinberg
But again, I don't see an alternative in C.
Reducing the size of translation units can be used to sidestep the
issue, and you can write bridge functions to glue together parts which
are not directly compatible, providing that there are no symbol clashes
(which is why I'm particularly interested in symbol collisions).
Post by Zack Weinberg
Post by Florian Weimer
Post by Zack Weinberg
I _do_ have problems with causing these symbols to be used by default.
My main concern is that I think it's tackling the problem in the wrong
place. If we want to back away from the original principle that the
executable always wins, isn't an expanded version of -Bsymbolic mode
what's _really_ wanted?
We could cover a lot of ground if we had a new flag on versioned symbol
definitions which tells the static linker to set a flag on the versioned
symbol reference, and the dynamic linker would then use this flag to
ignore unversioned symbols for binding symbols.
I was imagining a new annotation on _all_ undefined symbols in a shared
object, giving the soname of the object that they were satisfied by at
link time. At load time, 'getrandom!libc.so.6' resolves to the
'getrandom' definition in libc.so.6, ignoring all other definitions of
the same name. If there are symbol versions involved, only the versions
exported by libc.so.6 are considered. For instance,
We still need to support LD_PRELOAD and interposition of arbitrary
symbols, and not just malloc-related ones, for the benefit of Address
Sanitizer, fakeroot, cwrap, memstomp and other tools.

This is why hard-coding the DSO name does not seem advisable.
Post by Zack Weinberg
Post by Florian Weimer
(The static linker currently does not add the version of the interposed
symbol when interposition happens at static link time.)
I don't understand this statement, and that makes me worry that you are
trying to solve a different problem from the one I thought you were
talking about -- a problem that I might not even know exists. Can you
elaborate, please? How can a shared object be interposed upon at static
link time? Its own static link has already happened!
When an application is linked against a shared object, if it interposes
any symbols in it, the symbols becomes exported, so that interposition
works at run time (otherwise, it could not happen). You can see an
example here:

$ nm -Dg malloc/tst-interpose-nothread | grep ' T '

The application is *not* compiled with -Bdynamic or something like that,
it happens automatically.

But the symbol version from libc.so.6 is not attached to this symbol
(“nm” would not show it, but you can check with eu-readelf, for example).
Post by Zack Weinberg
You keep talking about symbol versioning but, again, I don't understand
how symbol versioning is relevant,
It's a namespace mechanism for ELF, so it's tempting to use it to
address this problem as well.
Post by Zack Weinberg
and that makes me worry that you're
trying to solve a different problem that I don't even know about. I
thought the issue here was controlling *which library* provides a
symbol, independent of whether the symbol has versions.
I'm not convinced this desirable because of the exceptions I listed
above. Both manual name mangling (which I currently prefer) and symbol
versioning with the no-interpose flag (the one I sketched earlier) would
support them. But only name mangling addresses collisions before the
first static link.
Post by Zack Weinberg
As for what appears in the headers -- again, see above: to solve the
libstdc++/_GNU_SOURCE problem, which I believe I *do* understand, there
needs to be a set of alternative names that libstdc++ headers can
*explicitly* refer to; otherwise we have still polluted the user
namespace. REDIRECTed declarations do not solve that problem. In fact,
with my proposed which-library annotations, we could perfectly well
extern ssize_t __libc_getrandom (void *, size_t, unsigned int)
__asm ("getrandom") __attribute__ ((__bind_to_library ("libc.so.6")));
#ifdef __USE_GNU
extern typeof (__libc_getrandom) getrandom;
#endif
Yes, I this would work (subject to the existing shortcomings).
Post by Zack Weinberg
All this aside, this discussion is still very brainstormy and that makes
me think that we should *not* yet be supplying mangled names for public
use. Once we start doing that we are stuck with it forever, after all.
Contrariwise, we *can* always retrofit __libc_* aliases or whatever once
we know what we ought to be doing.
I think getrandom is special because the name is rather generic, just
like some of the new libm names (where we still might have to introduce
mangling based on feedback from distribution rebuilds; I just don't know
yet).

Thanks,
Florian
Andreas Schwab
2016-11-22 15:29:57 UTC
Permalink
Post by Florian Weimer
There also configure scripts which do
extern char whatever();
main () { whatever(); }
for link testing, without including the appropriate header (perhaps
deliberately). That could be considered broken. I'm not sure.
While getting the return type wrong makes it slightly broken, the C
standard does support declaring standard functions on your own, as long
as you don't need special types.

Andreas.
--
Andreas Schwab, SUSE Labs, ***@suse.de
GPG Key fingerprint = 0196 BAD8 1CE9 1970 F4BE 1748 E4D4 88E3 0EEA B9D7
"And now for something completely different."
Florian Weimer
2016-11-22 15:39:41 UTC
Permalink
Post by Andreas Schwab
Post by Florian Weimer
There also configure scripts which do
extern char whatever();
main () { whatever(); }
for link testing, without including the appropriate header (perhaps
deliberately). That could be considered broken. I'm not sure.
While getting the return type wrong makes it slightly broken, the C
standard does support declaring standard functions on your own, as long
as you don't need special types.
Yes, that's true. Thanks for the reminder.

Florian
Zack Weinberg
2016-11-22 15:48:05 UTC
Permalink
Post by Florian Weimer
Post by Andreas Schwab
Post by Florian Weimer
There also configure scripts which do
extern char whatever();
main () { whatever(); }
for link testing, without including the appropriate header (perhaps
deliberately). That could be considered broken. I'm not sure.
While getting the return type wrong makes it slightly broken, the C
standard does support declaring standard functions on your own, as long
as you don't need special types.
Yes, that's true. Thanks for the reminder.
As a practical matter, also, Autoconf has done this for decades and no
one has even sketched a design for a replacement; I think we must
assume we will need to support it for the foreseeable future.

zw
Zack Weinberg
2016-11-22 15:48:47 UTC
Permalink
(I'll respond to the rest of your message later; don't have time today.)
Joseph Myers
2016-11-22 17:42:07 UTC
Permalink
I have no idea at all why __bzero was introduced. It doesn't look like a
It used to be used in a bits/string2.h definition of memset which
involved:

__builtin_constant_p (c) && (c) == '\0'
? ({ void *__s = (s); __bzero (__s, n); __s; })
: memset (s, c, n)
--
Joseph S. Myers
***@codesourcery.com
Zack Weinberg
2016-11-23 14:08:59 UTC
Permalink
Post by Florian Weimer
Post by Zack Weinberg
Post by Florian Weimer
If we don't declare the library-safe names in headers, how can
libraries call them?
We could _declare_ the library-safe names in the headers, just not
as the primaries. Like how string.h currently declares both bzero
and __bzero.
I have no idea at all why __bzero was introduced.
__bzero was the first case that came to mind where existing headers
declare both a name and a __name for the same function, is all. I've
been looking at string.h a lot lately.
Post by Florian Weimer
Post by Zack Weinberg
Post by Florian Weimer
I also do not want to encourage application or library code to
reference the implementation namespace at the source code level.
It's ugly, and I suspect it encourages implementation namespace
pollution once programmers are used to it.
I don't like it either, but how else could a library's headers opt
into these special names on a per-symbol, per-use basis?
In general, headers should avoid using libc types, particularly
off_t, time_t, struct timeval, struct stat, and so on. But there
might be exceptions.
... I'm not sure why we're suddenly discussing typedef names?
Post by Florian Weimer
For C, it's not clear at all to me whether we need any kind of
opt-in besides compiling with a particular _*_SOURCE variant (which
introduces the definition).
I am having trouble articulating why I don't like this. I think it
might be mostly to do with the way new symbols are now different than
old symbols. I'd be okay with an across-the-board change to symbol
resolution (as discussed below) that made interposition not work by
default, but I'm not okay with the idea of its being supported only for
symbols added before some arbitrary release.

Third-party C libraries don't tend to put nearly as much code in inline
functions, so the need for explicitly-useable __names is lessened there,
I think.
Post by Florian Weimer
For C++, we might use something based on namespaces to get a clear
separation. However, the problem there is that type names and
struct tags end up in C++ mangled identifiers and thus impact
application ABI. I have no good idea what to do there.
Arguably that's a Good Thing -- a change to what off_t means is an ABI
break whether or not it shows up in symbol names, and _making_ that one
in particular show up in symbol names might solve some of the problems
that lead to _FILE_OFFSET_BITS=64 still not being default for the older
32-bit architectures.
Post by Florian Weimer
Post by Zack Weinberg
Come to think of it, to actually avoid polluting the user
namespace, any library that wants to use these will need a
secondary set of libc headers that declare _only_ the private
names. (This is especially relevant for C++ with so much code in
headers.) If we don't do that, the user-namespace libc prototype
(which still exists under your plan) might conflict with an
unrelated application definition.
Yes, that's what using a namespace for C++ would achieve.
I'm less convinced we should do this for C. There is precedent,
Microsoft did exactly this for the POSIX-inspired interfaces in
their libc (functions like _open, _close and so on).
That definitely does cause grief for portable code...
Post by Florian Weimer
Post by Zack Weinberg
I was imagining a new annotation on _all_ undefined symbols in a
shared object, giving the soname of the object that they were
satisfied by at link time. At load time, 'getrandom!libc.so.6'
resolves to the 'getrandom' definition in libc.so.6, ignoring all
other definitions of the same name. If there are symbol versions
involved, only the versions exported by libc.so.6 are considered.
We still need to support LD_PRELOAD and interposition of arbitrary
symbols, and not just malloc-related ones, for the benefit of
Address Sanitizer, fakeroot, cwrap, memstomp and other tools.
This is why hard-coding the DSO name does not seem advisable.
This argument applies equally to every new symbol we might add, and in
fact to every _intra_-libc call that currently _can't_ be interposed.
So I'm inclined to discount it.

The solution I'm leaning toward involves each library designating a set
of exported symbols, calls to which _can_ be interposed; the default is
not to allow it. We'd probably have to spend some time figuring out
exactly which of libc's symbols should be interposeable.
Post by Florian Weimer
When an application is linked against a shared object, if it
interposes any symbols in it, the symbols becomes exported, so that
interposition works at run time (otherwise, it could not happen).
$ nm -Dg malloc/tst-interpose-nothread | grep ' T '
The application is *not* compiled with -Bdynamic or something like
that, it happens automatically.
But the symbol version from libc.so.6 is not attached to this symbol
(“nm” would not show it, but you can check with eu-readelf, for example).
Well, OK, why don't we just fix that? Is there a good reason why it
_doesn't_ pick up the symbol version? (I know that for compatibility's
sake an unversioned symbol has to interpose all versions of the same
name, but that wouldn't seem to apply when the versioned symbol was
visible to the static linker.)
Post by Florian Weimer
Post by Zack Weinberg
I thought the issue here was controlling *which library* provides
a symbol, independent of whether the symbol has versions.
I'm not convinced this desirable because of the exceptions I listed
above. Both manual name mangling (which I currently prefer) and
symbol versioning with the no-interpose flag (the one I sketched
earlier) would support them. But only name mangling addresses
collisions before the first static link.
I hope I've explained clearly enough already why I'm not a fan of manual
name mangling, especially on an ad-hoc or new-symbols-only basis.
Post by Florian Weimer
Post by Zack Weinberg
All this aside, this discussion is still very brainstormy and that
makes me think that we should *not* yet be supplying mangled names
for public use. Once we start doing that we are stuck with it
forever, after all. Contrariwise, we *can* always retrofit __libc_*
aliases or whatever once we know what we ought to be doing.
I think getrandom is special because the name is rather generic,
just like some of the new libm names (where we still might have to
introduce mangling based on feedback from distribution rebuilds; I
just don't know yet).
We agreed that the unmangled name has to exist, so how about we move
forward by introducing only the unmangled names for the new symbols
currently proposed (getrandom, explicit_bzero), introduce mangling if
necessary based on feedback, and work toward a long-term solution that
can be applied across the board?

zw
Florian Weimer
2016-11-24 10:01:53 UTC
Permalink
Post by Zack Weinberg
Post by Florian Weimer
In general, headers should avoid using libc types, particularly
off_t, time_t, struct timeval, struct stat, and so on. But there
might be exceptions.
... I'm not sure why we're suddenly discussing typedef names?
I'm trying to come up with reasons why the usual header file conflict
avoidance mechanisms would not work.

I mean, if you use C, you pretty much agreed to using separate
compilation to work around header conflict issues.
Post by Zack Weinberg
Post by Florian Weimer
For C, it's not clear at all to me whether we need any kind of
opt-in besides compiling with a particular _*_SOURCE variant (which
introduces the definition).
I am having trouble articulating why I don't like this. I think it
might be mostly to do with the way new symbols are now different than
old symbols. I'd be okay with an across-the-board change to symbol
resolution (as discussed below) that made interposition not work by
default, but I'm not okay with the idea of its being supported only for
symbols added before some arbitrary release.
I prefer the case-by-case approach because it allows us to review ABI
changes individually.
Post by Zack Weinberg
Third-party C libraries don't tend to put nearly as much code in inline
functions, so the need for explicitly-useable __names is lessened there,
I think.
Right, and separate compilation is available as workaround.
Post by Zack Weinberg
Post by Florian Weimer
For C++, we might use something based on namespaces to get a clear
separation. However, the problem there is that type names and
struct tags end up in C++ mangled identifiers and thus impact
application ABI. I have no good idea what to do there.
Arguably that's a Good Thing -- a change to what off_t means is an ABI
break whether or not it shows up in symbol names, and _making_ that one
in particular show up in symbol names might solve some of the problems
that lead to _FILE_OFFSET_BITS=64 still not being default for the older
32-bit architectures.
Suppose we want to make struct sockaddr_un available to C++ code under a
namespaced name. Then C++ code has to use that name. But this could
change ABI on the C++ side merely due to name mangling (on top of
potential type compatibility issues interface with user code). If we do
not solve this in some way, I don't think many C++ projects will switch
to internal names to avoid the header file collision because it's not
worth the impact on compatibility.
Post by Zack Weinberg
Post by Florian Weimer
Post by Zack Weinberg
I was imagining a new annotation on _all_ undefined symbols in a
shared object, giving the soname of the object that they were
satisfied by at link time. At load time, 'getrandom!libc.so.6'
resolves to the 'getrandom' definition in libc.so.6, ignoring all
other definitions of the same name. If there are symbol versions
involved, only the versions exported by libc.so.6 are considered.
We still need to support LD_PRELOAD and interposition of arbitrary
symbols, and not just malloc-related ones, for the benefit of
Address Sanitizer, fakeroot, cwrap, memstomp and other tools.
This is why hard-coding the DSO name does not seem advisable.
This argument applies equally to every new symbol we might add, and in
fact to every _intra_-libc call that currently _can't_ be interposed.
So I'm inclined to discount it.
I think there's a big difference if you have to write new interceptors
to support newer glibc versions, or if you have to rewrite your whole
library as an audit module because the ability to interpose the symbols
you are interested in is gone completely.
Post by Zack Weinberg
The solution I'm leaning toward involves each library designating a set
of exported symbols, calls to which _can_ be interposed; the default is
not to allow it. We'd probably have to spend some time figuring out
exactly which of libc's symbols should be interposeable.
It seems to me that interposition of arbitrary symbols is currently part
of the programming interface. We didn't plan for things like fakeroot
and cwrap, but someone created those tools eventually, and they
apparently address a real need.
Post by Zack Weinberg
Post by Florian Weimer
When an application is linked against a shared object, if it
interposes any symbols in it, the symbols becomes exported, so that
interposition works at run time (otherwise, it could not happen).
$ nm -Dg malloc/tst-interpose-nothread | grep ' T '
The application is *not* compiled with -Bdynamic or something like
that, it happens automatically.
But the symbol version from libc.so.6 is not attached to this symbol
(“nm” would not show it, but you can check with eu-readelf, for example).
Well, OK, why don't we just fix that? Is there a good reason why it
_doesn't_ pick up the symbol version?
I'm not sure if interposition at load time will still happen. But this
should be easy to verify. I'll give it a try.
Post by Zack Weinberg
We agreed that the unmangled name has to exist, so how about we move
forward by introducing only the unmangled names for the new symbols
currently proposed (getrandom, explicit_bzero), introduce mangling if
necessary based on feedback, and work toward a long-term solution that
can be applied across the board?
What kind of feedback would trigger mangled names? Is having a
real-world application which triggers accidental interposition sufficient?

For getrandom, not using the mangled name by default looks like a
security bug in the making. Less so for explict_bzero.

Thanks,
Florian

Loading...