How _not_ to fix glibc 2.10 function collisions

Following my previous how not to fix post I’d like to explain also how not to fix the other kind of glibc 2.10 failure: function collisions.

With the new version of glibc, support for the latest POSIX C library specification is added; this means that, among other things, a few functions that previously were only available as GNU extensions are now standardised and, thus, visible by default unless requesting a strictly older POSIX version.

The functions that were, before, available as GNU extensions were usually hidden unless the _GNU_SOURCE feature selection macro was defined; in autotools, that meant using the AC_SYSTEM_EXTENSIONS macro to request them explicitly. Now these are visible by default; this wouldn’t be a problem if some packages didn’t decide to either reimplement them, or call their functions just like that.

Most commonly the colliding function is getline(); the reason for which is that the name is really too generic and I would probably curse POSIX committees for accepting it with the same name in the C library; I already cursed GNU for adding it with that name to glibc. With the name of getline() there are over tons of functions, with the most different interfaces, that try to get lines from any kind of media. The solution for these is to rename them to some different name so that the collision is avoided.

More interesting is instead the software that, wanting to use something alike to strndup() decide to create its own version, because some system do lack that function. In this case, renaming the functions, like I’ve seen one user propose today, is crazy. The system already provide the function; use that!

This can be done quite easily with autotools-based packages (and can be applied to other build systems, like cmake, that work on the basis of understanding what the system provides):

# in configure.ac
AC_SYSTEM_EXTENSIONS
AC_CHECK_FUNCS([strndup])

/* in a private header */
#include "config.h"

#ifndef HAVE_STRNDUP
char *strndup(const char *str, size_t len);
#endif

/* in an implementation file */
#ifndef HAVE_STRNDUP
char *strndup(const char *str, size_t len)
{
  [...]
}
#endif

When building on any glibc (2.7+ at least, I’d say), this code will use the system-provided function, without adding further duplicate, useless code; when building on systems where the function is not (yet) available, like FreeBSD 7, then the custom functions will be used.

Of course it takes slightly more time than renaming the function, but we’re here to fix stuff in the right way, aren’t we?

11 thoughts on “How _not_ to fix glibc 2.10 function collisions

  1. But if you use the system-provided function instead of renaming, wouldn’t that result in a completely non-portable binary that doesn’t work on all the Linux machines that have glibc-2.9 or older?

    Like

  2. Please do read my post; I’ve shown the code to make the replacement function apply whenever the system does not support them at all. And it wouldn’t be using them _just_ on 2.10, but also on 2.9, 2.8, 2.7, … since it uses the system extensions.Portability does not mean “I’ll bring in all the possible crap so I don’t have to check whether the system provides them or not”; that is a stupid way to handle it.

    Like

  3. Please do read my question :)If I compile a program using your autotools code on a machine with glibc-2.10 and then move the *compiled binary* to a different machine with an older version of glibc, will the binary work on that other machine?In other words, will it result in a non-portable *binary*? (I know that the source code is portable, but maybe I don’t want to recompile the package.)

    Like

  4. You are _never_ guaranteed that a package built against glibc 2.X will work on 2.Y where X > Y. The _ABI_ of glibc is *backward compatible* but not forward compatible.But in this particular instance, strndup() funcitons _are_ available on most modern glibc systems.Again, if you have to distribute binaries you _have_ to use the oldest glibc versions you want to support!

    Like

  5. I was a bit surprised that you recommend AC_SYSTEM_EXTENSIONS and not AC_GNU_SOURCE. Actually, I do not understand the difference between these two, because also AC_GNU_SOURCE will define things like _ EXTENSIONS__, _POSIX_PTHREAD_SEMANTICS, _TANDEM_SOURCE etc, i.e. the difference is probably only visible on very specific systems and thus hard to check. And as usual, documentation of autotools is very poor: if in doubt, is it really preferrable to use AC_SYSTEM_EXTENSIONS or will this just dramatically increase the chance of name conflicts?

    Like

  6. As you noted, @AC_GNU_SOURCE@ enables a lot more extensions; for instance the @__EXTENSIONS__@ macro is used by Solaris to provide some non-POSIX interfaces. For this very reason, autoconf deprecated the macro with name @AC_GNU_SOURCE@ in favour of @AC_SYSTEM_EXTENSIONS@.When you use the former, recent autotools simply call the latter; and the latter is indeed the one that autoupdate will replace it with if you ran it on an old autotools based package. So indeed the correct choice is @AC_SYSTEM_EXTENSIONS@.On the other hand, unfortunately it does _not_ enable enough interfaces on systems like FreeBSD and Darwiin, so if you check out “feng”:https://blog.flameeyes.eu/t… you’re going to find some extra preprocessor feature macros defined on a per-host basis.Also, it’s probably a good thing to increase the chance of name conflicts: POSIX or not, software should never use the name of the interfaces in the system.

    Like

  7. While often this may be the best solution, I think you are missing a possible issue:You now have two code paths, and if your developers are mostly on one platform, only one of those will be properly tested, which makes it a maintenance issue.Now in the case of strndup I would obviously agree: It is available almost anywhere, and either the systems where it isn’t aren’t really relevant and just marginally supported anyway or you have someone looking after them regularly.But if it was some kind of function that e.g. was only available on Solaris/SPARC and you have no developers nor a test system using it, I’d say just always using your own reimplementation will lead to a more stable and maintainable program.

    Like

  8. Software is already full of conditional code paths that are rarely tested and stuff like that; but most of the problems seems to be when replacement functions are used, rather than not. A system implementation will always be tested more than the single implementation in one particular package, and this leads for it to be better optimised, and safer, than any custom implementation.In the case of very niche functions (although I doubt Solaris has different interfaces between SPARC and x86 systems), then it might be better to just replace all the implementations; but for glibc functions, it really doesn’t make sense. By using this conditional, by the way, you can also make sure that, when the function _is_ added to other operating systems like FreeBSD, it’ll be picked up properly (given this is now a POSIX interface, then it makes sense that most operating systems will be adding it in the next few months.

    Like

  9. Flameeyes, thank you very much for the explanation.You are right that it is good to increase the chance of name conflicts – for the testing phase and _if_ you have a chance to test them on many systems. However, in the final release, enabling unneeded extensions might make the code more likely to break under future system extensions. Anyway, you convinced me to switch to AC_SYSTEM_EXTENSIONS.

    Like

  10. Very late, but I’ll just point out that AC_REPLACE_FUNCS (while adding more files) limits a lot the need for #ifdefs.

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s