On Tue, 16 Apr 2002, Wojtek Lerch wrote:
> Marc Aurele La France wrote:
> > On Sat, 13 Apr 2002, Wojtek Lerch wrote:
> > > Marc Aurele La France <yyy@xxxxxxxxxxx> said:
> > > > *(void **)(&fptr) = dlsym(handle, "my_function");
> > > > ... which is, I'll grant, a little kludgy, but is perfectly legal in
> > > > ANSI or ISO C terms.
> > > A little?
> > > It's only "legal" in the sense that it doesn't require the compiler to
>issue
> > > a diagnostic; but it is undefined behaviour in ISO C. And it's actually
>less
> > > portable than the original typecast:
> > > * On a platform where function pointers are smaller than data pointers,
> > > you're clobbering memory outside of the fptr variable. A cast from void*
>to
> > > a function pointer would just work: yes, it would throw away the extra
>bits
> > > instead of storing them in some unrelated variable, but if the symbol you
> > > asked for indeed was a function, those extra bits would have all been
> > > insignificant anyway.
> > > * Even if function pointers are the same size as data pointers, their
> > > representation could be different. Imagine a machine that stores function
> > > pointers as "big endian" (segment, then offset) but data pointers as
>"little
> > > endian" (offset, then segment). A normal typecast would allow the
>compiler
> > > to swap the segment and the offset; yours doesn't.
> > Both objections are irrelevant. My suggestion was given within the
> > context of the current specification for dlsym(). Given dlsym() is
> > currently constrained to return a void *, it has no portable means
> It doesn't need portable means. Implementation-defined means are
> sufficient. The implementation of dlsym() is not required to be
> portable.
True, but it still needs to allow dlsym() callers to be portable.
"Portable" here means that an application can be written such that it will
correctly run across all POSIX dlsym() implementations.
> > returning to its caller any information about a symbol other than its
> > existence/non-existence. Thus, any portable dlsym() implementation
> > requires that all pointers have exactly the same representation.
> I'm not sure what you mean by "portable dlsym() implementation"; my
> point was that with some implementations of dlsym(), your code can
> produce bad results while the original one would work. If you claim
The original won't be given a chance to work if the compiler decides to
nix, rather warn about, casts between data pointers and function pointers.
My suggestion for the dlsym() example avoids all that by casting not the
function pointer, but a pointer to it, which is allowed by ISO C.
> that it's impossible to implement dlsym() on the two platforms that I
> described, then I disagree. If you just chose to ignore the possibility
> of ever having to run your code on such platforms because dlsym() cannot
> have a "portable implementation" on them (whatever that means to you),
> then I guess we don't disagree.
I maintain that a dlsym() implementation in an environment with different
pointer representations cannot satisfy POSIX application portability
requirements because such an implementation assumes the application knows
beforehand the nature of the symbol it is naming. When the application
doesn't, this dlsym() implementation can return unexpected data.
POSIX dlsym(), as currently specified, sanctions neither behaviour, and
therefore strictly speaking cannot exist in an environment where pointer
representations differ. Changing POSIX dlsym() to require this
application "pre-knowledge" and/or allow implementation-defined behaviour
when the symbol names a function, is not an option, as doing so would
break many existing dlsym() applications. An nm-like utility would be a
good example of such an application. As would the XFree86 loader server.
It might seem that I am splitting hairs here, but it needs to be
underlined that portibility across all POSIX implementations does not
necessarily imply portability across all ISO C implementations, nor vice
versa. These two "portabilities" intersect, yes, but they are not
identical.
After mulling this issue over some more, I have another suggestion, one
that, at least at first glance, appears to satisfy all concerns I've seen
so far on this matter. Under this scenario, POSIX would sanction an
additional ldopen() flag, say RTLD_SYMINFO, a symbol that would need to be
#ifdef'able. When used in the corresponding dlopen() call, dlsym() would
instead return (still as a void *) a pointer to a structure containing the
following fields:
unsigned int symtype;
void *dataptr;
void (*funcptr)(void);
dataptr and funcptr would be allowed to overlap in memory. symtype is a
bit-field containing at least RTLD_SYMFUNC or RTLD_SYMDATA, along with
whatever other information dlsym() wishes to include. The dlsym()
implementation would also be allowed to define other fields in this
structure, as long as the existence of each such field is signaled by a
corresponding #ifdef'able symbol #define'd in <dlfcn.h>.
To avoid requiring the application to free() this structure, the structure
could be in dlsym()'s static storage, meaning that dlsym() would not be
re-entrant, nor thread-safe.
If dlopen() is not used with RTLD_SYMINFO, dlsym() behaviour would be as
specified in the current POSIX, including the converted value that occurs
when returning a function pointer in an environment where pointer
representations differ.
Marc.
+----------------------------------+-----------------------------------+
| Marc Aurele La France | work: 1-780-492-9310 |
| Computing and Network Services | fax: 1-780-492-1729 |
| 352 General Services Building | email: yyy@xxxxxxxxxxx |
| University of Alberta +-----------------------------------+
| Edmonton, Alberta | |
| T6G 2H1 | Standard disclaimers apply |
| CANADA | |
+----------------------------------+-----------------------------------+
XFree86 Core Team member. ATI driver and X server internals.
|