Hi,

On Thu, 2 Jun 2016, Steffen Nurpmeso wrote:

Well, have i yet asked this?  I think no...
It's like that for a long time, ever since i use tcc(1) (autumn
last year):

 ?0[steffen@wales tmp]$ cat t.c
 #include <stdlib.h>
 int main(void){
    char *x = realpath(".", NULL);

    return (x != NULL) ? 0 : 1;
 }
 ?0[steffen@wales tmp]$ for i in clang gcc tcc; do \
   $i -o zt t.c && ./zt; echo $i: $?;\
 done
 clang: 0
 gcc: 0
 tcc: 1

tcc(1) only gets realpath(3) with buffer argument right, i wonder
what that can be?

Wow. You've run into the result of an unimplemented feature of tcc, which I never thought would matter in practice ;-) I'll explain, bear with me: in POSIX 2001 the result of using NULL as 'resolved_path' argument to realpath(3) was left implementation defined. This was an unworkable interface (you basically had no way of finding out if the returned buffer has overflown), so in POSIX 2008 this was specified universally as meaning to malloc the returned buffer. Now, glibc at the time (versions < 2.3) was using that old standards freedom to define this as unsupported. You will see in your program that errno is set to EINVAL after realpath returns NULL with a NULL second argument.

Later glibc introduced the newer and more sensible behaviour with accepting NULL as second argument. But glibc also has a strict no change in interfaces policy, and this was a visible change, so symbol versioning had to be used to provide the old behaviour for old applications (those linked with glibc < 2.3) and new behaviour for new ones. So, since then glibc exports two variants of realpath: realpath@GLIBC_2.2.5 and realpath@@GLIBC_2.3. Normally when linking executables against a libc providing these two symbols the link editor, when it supports symbol versions, will choose the newest one, record that fact into the executable, and when running the dynamic linker will search for realpath@GLIBC_2.3 and use it.

"when it supports symbol versions" is of course the crucial part: tcc as ELF linker doesn't. So it leaves in an unversioned reference to 'realpath' in the executable and so the dynamic linker is free to choose any. Now the usual ld.so uses a (normally) sensible heuristic for choosing which one when there are several: for references from dlsym it uses the newest available one, for references from applications that have no symbol version information whatsoever it uses the oldest symbol version, on the grounds that apps without any symversion info must have been created before the ELF implementation provided them, i.e. they must be very very old applications.

This reasoning breaks down with an incomplete link editor like tcc. It creates new applications that are regarded as very old by ld.so. So, what you see is ld.so resolving to the old implementation of realpath, instead of the current one.

Short of implementing proper ELF symbol versioning in tcc there's only an ugly work-around: you could use a different link editor. Perhaps there's somewhen a rainy weekend when the former can be done.


Ciao,
Michael.

_______________________________________________
Tinycc-devel mailing list
Tinycc-devel@nongnu.org
https://lists.nongnu.org/mailman/listinfo/tinycc-devel

Reply via email to