The obvious answer, though not the most comprehensive, is to check your package manager, e.g
rpm -qi glibc
dpkg -l libc6
(Sadly, glibc doesn't have a pkconfig .pc file, so pkgconfig --modversion glibc is a non-runner.) See also @Gnouc's excellent getconf suggestion.
The simplest case, with gcc+glibc, and the one I mostly use first is to just execute libc.so, as outlined in some of the other answers here. There's no need to pass any arguments, it outputs its version by default. This works back as far as glibc-2.1 (glibc-2.0 seg-faults, though way back then you could check the (now retired) glibcbug script to confirm the version). This method also works with recent (>0.9.15) versions of musl-libc (which just went 1.0 today, March 20th). It does not work with uClibc, it segfaults.
One simple way to tell exactly what your gcc is going to do is compile:
#include <gnu/libc-version.h>
#include <stdio.h>
int main(int argc, char *argv[]) {
printf("%s %s\n",gnu_get_libc_version(),gnu_get_libc_release());
printf("glibc v%i %i.%i\n",__GNU_LIBRARY__,__GLIBC__,__GLIBC_MINOR__);
return 0;
}
(with glibc, <stdio.h> includes <features.h> which defines the relevant GLIBC macros, you need <gnu/libc-version.h> for the function declarations.)
This catches more complex cases (multiple libc's, and/or multiple compilers), assuming you're using the right compiler (and flags) of course. (I suspect it won't distinguish between eglibc and glibc proper though.)
If you are certain you are using glibc (or eglibc) then ld will also confirm the version (sorry, this is not correct).
If __GNU_LIBRARY__ is not defined you will get errors, then it's time for plan B.
gcc -dumpmachine may help, e.g. for uclibc it has a -uclibc suffix, as may gcc -dumpspecs | grep dynamic-linker. This also may imply the ABI.
gcc -print-file-name=libc.so will tell you what file the compiler will use for "-lc", this is almost certainly a linker-script within your gcc installation, which you can read it as plain text. That will show the exact path to libc.so. This will also work if you're passing flags like -m32 or -m64.
In the event you're using uclibc (as used by OpenWRT and more), it defines __UCLIBC_MAJOR__, __UCLIBC_MINOR__ and __UCLIBC_SUBLEVEL__ as well as __UCLIBC__ in <features.h>, so it's easily detected using a minor variation on the above C code snippet. In the interest of compatibility, uClibc may also define the GNU/GLIBC macros as used above, it currently pretends to be glibc-2.2. It does not currently implement the gnu_get_libc_X() functions, but it does implement getconf which may also mislead (I suspect it returns an empty answer for getconf GNU_LIBC_VERSION, my build env is sulking today so I cannot confirm.)
In the unlikely event you're using dietlibc, running diet -v will display the version.
(FWIW, over several years with software using autoconf I've had more problems with unchecked-for gcc and g++ requirements than with checked-for glibc features.)