The program doesn't "know" anything. The executable has a header used by the loader that tells it to use libc. If the libc version does not contain the symbols that the program expects or the symbols do not have compatible definitions (meaning identical function signature and ABI, including struct layout) the program will probably crash. If the program links against any shared libraries they'll use the same version of libc that's loaded with the executable.
There are ways around this that are varying degrees of acceptable. Versioning libc itself is outside the scope of the language, since it really depends on how the system linkers and loaders are implemented.
There are ways around this that are varying degrees of acceptable. Versioning libc itself is outside the scope of the language, since it really depends on how the system linkers and loaders are implemented.