Why are different symbols in libc declared with different bindings,

1510 0x0003d200 0xf7d55200   WEAK   FUNC   55 system
 454 0x00067b40 0xf7d7fb40   WEAK   FUNC  474 puts
 147 0x000303d0 0xf7d483d0 GLOBAL   FUNC   33 exit

From readelf -s /lib/i386-linux-gnu/libc-2.27.so,

1510: 0003d200    55 FUNC    WEAK   DEFAULT   13 system@@GLIBC_2.0
 454: 00067b40   474 FUNC    WEAK   DEFAULT   13 puts@@GLIBC_2.0
 147: 000303d0    33 FUNC    GLOBAL DEFAULT   13 exit@@GLIBC_2.0

From this question, it seems like the authors' libc has a LOCAL exit?

What the rhyme or reason behind the different libc binding levels?

2 Answers 2


Weak symbols in glibc permit overriding even under static linking.

The global exit is a strong symbol. Additional definitions will not resolve, and will raise linking errors.

Thus you might be able to supply your own puts, and barring a second definition of puts that is strong, the puts with the largest memory occupation (size) will be chosen.

Why the largest size? Well, GCC needs some discriminator, and most of the glibc is structured such that the public symbols are nearly universally aliased to private symbols, which means the size is rather small (a jump). This means that if you provide your own implementation of puts, unless you structure it to alias exactly the same way, odds are you implementation is larger. For puts the alias directs to __IO_puts.

This is the mechanism by which the standard library both provides implementations of standard library calls and permits you to override them.

  • I agree, but I think the question remains. Why? Why is that I can override puts but not exit? Why is only puts, and not exit, a WEAK alias of an internal symbol? Oct 31, 2018 at 20:08
  • exit has non override-able behavior because it is a routine that handles the shutdown of processes with the operating system. Any "external" implementation is likely to break the contracts between the operating system and the process model it expects. Remember, the program doesn't really return the exit code to the parent process, it signals it to the kernel, which then provides it to the parent process. There are other shutdown hooks available if you want on-exit behavior, namely atexit(...)
    – Edwin Buck
    Nov 1, 2018 at 15:03
  • Do you have a reference for the 'largest memory occupation' logic? Also, I don't understand your static linking argument. I mean, by default, when linking statically, a symbol might be defined in multiple static libs, and then the order (of the libraries specified on the command line) is significant and the first symbol definition wins. Feb 23, 2020 at 19:47
  • @maxschlepzig It's been a long time, but my fuzzy memory recalls that there's an overriding problem with functions that back to system calls. Basically, if you override puts() then there's no way to call the pre-overridden puts() to do the system call to get your characters to print. So you need to have a non-overridden original puts() for your new overriding puts() code to work. Since the system call is just a few bytes, the comparison to determine which one is the wrapper (I might have read it from glibc source code) was size.
    – Edwin Buck
    Feb 24, 2020 at 3:46

The answer to the question of what is happening from a technical viewpoint is that it's an implementation detail for the reasons Edwin mentioned about (symbol aliasing). But moreover, why it's there is that it's happenstance, and that it would be suitable for a patch.

15:14 < azanella> EvanCarroll, it is an implementation detail, exit is not used internally so there is no need to define an internal symbol with a alias to it [...] system and puts are both implemented by different symbols with alias to them [...] but it is a good question though, I believe for exit case it glibc is not really static linking focused and no one has raised any issue on trying to override it in static linking

From #glibc on irc.freenode.net on Oct 31, 2018

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .