Abandon ifuncs and go with the traditional global function pointers.

In addition to the failing testcase, there were a couple of
regressions in piglit's attribs test: one from glBegin_unwrapped vs
glBegin confusion in the __asm__ directives we were generating, and
one where the function pointers apparently were just getting mixed up
at application runtime.
macos/v1.5.9
Eric Anholt 11 years ago
parent e58e98feee
commit 3d2a2b3c80
  1. 18
      README.md
  2. 21
      src/dispatch_common.c
  3. 16
      src/dispatch_common.h
  4. 74
      src/gen_dispatch.py
  5. 2
      test/Makefile.am

@ -108,21 +108,3 @@ We had to solve some of GLEW's problems for piglit and solving them
meant replacing every single piece of GLEW, so we built meant replacing every single piece of GLEW, so we built
piglit-dispatch from scratch. And since we wanted to reuse it in piglit-dispatch from scratch. And since we wanted to reuse it in
other GL-related projects, this is the result. other GL-related projects, this is the result.
Caveats
-------
* libepoxy isn't intended to be dlopen()ed. On ifunc platforms,
RTLD_NOW would cause early resolution of all GL symbols, and the
following error from reentering the linker:
```
Inconsistency detected by ld.so: dl-open.c: 235: dl_open_worker:
Assertion `_dl_debug_initialize (0, args->nsid)->r_state ==
RT_CONSISTENT' failed!
```
* libepoxy isn't intended to be built statically. It usess ifuncs on
linux, since it can avoid its dispatch table entirely in that case.
Building statically disables ifuncs, dropping you to a
higher-overhead path!

@ -476,8 +476,16 @@ epoxy_print_failure_reasons(const char *name,
} }
} }
PUBLIC void #ifdef _WIN32
epoxy_glBegin(GLenum primtype) #define WRAPPER_VISIBILITY PUBLIC
#define WRAPPER(x) x
#else
#define WRAPPER_VISIBILITY static
#define WRAPPER(x) x ## _wrapped
#endif
WRAPPER_VISIBILITY void
WRAPPER(epoxy_glBegin)(GLenum primtype)
{ {
#ifdef _WIN32 #ifdef _WIN32
InterlockedIncrement(&api.begin_count); InterlockedIncrement(&api.begin_count);
@ -490,8 +498,8 @@ epoxy_glBegin(GLenum primtype)
epoxy_glBegin_unwrapped(primtype); epoxy_glBegin_unwrapped(primtype);
} }
PUBLIC void WRAPPER_VISIBILITY void
epoxy_glEnd(void) WRAPPER(epoxy_glEnd)(void)
{ {
epoxy_glEnd_unwrapped(); epoxy_glEnd_unwrapped();
@ -503,3 +511,8 @@ epoxy_glEnd(void)
pthread_mutex_unlock(&api.mutex); pthread_mutex_unlock(&api.mutex);
#endif #endif
} }
#ifndef _WIN32
PUBLIC PFNGLBEGINPROC epoxy_glBegin = epoxy_glBegin_wrapped;
PUBLIC PFNGLENDPROC epoxy_glEnd = epoxy_glEnd_wrapped;
#endif

@ -58,6 +58,18 @@
# endif # endif
#endif #endif
/* On win32, we're going to need to keep a per-thread dispatch table,
* since the function pointers depend on the device and pixel format
* of the current context.
*/
#if defined(_WIN32)
#define USING_DISPATCH_TABLE 1
#define UNWRAPPED_PROTO(x) x
#else
#define USING_DISPATCH_TABLE 0
#define UNWRAPPED_PROTO(x) (*x)
#endif
void *epoxy_egl_dlsym(const char *name); void *epoxy_egl_dlsym(const char *name);
void *epoxy_glx_dlsym(const char *name); void *epoxy_glx_dlsym(const char *name);
void *epoxy_gl_dlsym(const char *name); void *epoxy_gl_dlsym(const char *name);
@ -79,5 +91,5 @@ void epoxy_print_failure_reasons(const char *name,
bool epoxy_extension_in_string(const char *extension_list, const char *ext); bool epoxy_extension_in_string(const char *extension_list, const char *ext);
void epoxy_glBegin_unwrapped(GLenum primtype); void UNWRAPPED_PROTO(epoxy_glBegin_unwrapped)(GLenum primtype);
void epoxy_glEnd_unwrapped(void); void UNWRAPPED_PROTO(epoxy_glEnd_unwrapped)(void);

@ -501,9 +501,22 @@ class Generator(object):
self.outln('') self.outln('')
self.write_function_ptr_typedefs() self.write_function_ptr_typedefs()
self.outln('/* The library ABI is a set of functions on win32 (where')
self.outln(' * we have to use per-thread dispatch tables) and a set')
self.outln(' * of function pointers, otherwise.')
self.outln(' */')
self.outln('#ifdef _WIN32')
self.outln('#define EPOXY_FPTR(x) x')
self.outln('#define EPOXY_FPTR_EXTERN')
self.outln('#else')
self.outln('#define EPOXY_FPTR(x) (*x)')
self.outln('#define EPOXY_FPTR_EXTERN extern')
self.outln('#endif')
for func in self.sorted_functions: for func in self.sorted_functions:
self.outln('{0} epoxy_{1}({2});'.format(func.ret_type, func.name, self.outln('EPOXY_FPTR_EXTERN {0} EPOXY_FPTR(epoxy_{1})({2});'.format(func.ret_type,
func.args_decl)) func.name,
func.args_decl))
self.outln('') self.outln('')
def write_proto_define_header(self, file, style): def write_proto_define_header(self, file, style):
@ -580,23 +593,28 @@ class Generator(object):
self.outln('}') self.outln('}')
self.outln('') self.outln('')
def write_ifunc(self, func): def write_function_pointer(self, func):
# Writes out the ifunc symbol that will have its PLT entry self.outln('static {0}'.format(func.ret_type))
# replaced with the resolved GL symbol when it's called. self.outln('epoxy_{0}_rewrite_ptr({1})'.format(func.wrapped_name,
func.args_decl))
# Tell the linker that even if we're in a static build, we need a PLT. self.outln('{')
self.outln('__asm__(".type epoxy_{0}, @gnu_indirect_function");'.format(func.name)); self.outln(' epoxy_{0} = (void *)epoxy_{1}_resolver();'.format(func.wrapped_name,
func.alias_name))
# Tell the linker that epoxy_glWhatever() needs to be resolved if func.ret_type == 'void':
# by epoxy_ifunc_glWhatever(). self.outln(' epoxy_{0}({1});'.format(func.wrapped_name,
self.outln('void *epoxy_ifunc_{0}(void) __asm__("epoxy_{0}");'.format(func.wrapped_name)) func.args_list))
else:
self.outln(' return epoxy_{0}({1});'.format(func.wrapped_name,
func.args_list))
# Write out the actual epoxy_ifunc_glWhatever().
self.outln('{0} void *epoxy_ifunc_{1}(void)'.format(func.public, func.wrapped_name))
self.outln('{')
self.outln(' return epoxy_{0}_resolver();'.format(func.alias_name))
self.outln('}') self.outln('}')
self.outln('{0}{1} epoxy_{2} = epoxy_{2}_rewrite_ptr;'.format(func.public,
func.ptr_type,
func.wrapped_name))
self.outln('')
def write_provider_enums(self): def write_provider_enums(self):
self.outln('enum {0}_provider {{'.format(self.target)) self.outln('enum {0}_provider {{'.format(self.target))
@ -671,22 +689,6 @@ class Generator(object):
self.outln('#include "epoxy/{0}.h"'.format(self.target)) self.outln('#include "epoxy/{0}.h"'.format(self.target))
self.outln('') self.outln('')
# ifuncs are a newer-gcc and newer-dynamic-linker feature that
# let the library rewrite the application's PLT entry with our
# resolved function pointer.
#
# Note that dlsym() when we're called from the dynamic linker
# while it's loading libraries will break. So, libepoxy can't
# be loaded with RTLD_NOW when ifuncs are in use. We also
# can't use ifuncs for static builds, since static builds
# resolve just like RTLD_NOW.
self.outln('#if defined(__PIC__) && defined(__linux__)')
self.outln('#define USING_IFUNCS 1')
self.outln('#else')
self.outln('#define USING_IFUNCS 0')
self.outln('#endif')
self.outln('')
self.outln('struct dispatch_table {') self.outln('struct dispatch_table {')
for func in self.sorted_functions: for func in self.sorted_functions:
# Aliases don't get their own slot, since they use a shared resolver. # Aliases don't get their own slot, since they use a shared resolver.
@ -699,7 +701,7 @@ class Generator(object):
# bottom. (I want the function_ptr_resolver as the first # bottom. (I want the function_ptr_resolver as the first
# per-GL-call code, since it's the most interesting to see # per-GL-call code, since it's the most interesting to see
# when you search for the implementation of a call) # when you search for the implementation of a call)
self.outln('#if !USING_IFUNCS') self.outln('#if USING_DISPATCH_TABLE')
self.outln('static inline struct dispatch_table *') self.outln('static inline struct dispatch_table *')
self.outln('get_dispatch_table(void);') self.outln('get_dispatch_table(void);')
self.outln('') self.outln('')
@ -713,7 +715,7 @@ class Generator(object):
if not func.alias_func: if not func.alias_func:
self.write_function_ptr_resolver(func) self.write_function_ptr_resolver(func)
self.outln('#if !USING_IFUNCS') self.outln('#if USING_DISPATCH_TABLE')
for func in self.sorted_functions: for func in self.sorted_functions:
if not func.alias_func: if not func.alias_func:
@ -738,12 +740,12 @@ class Generator(object):
self.outln('}') self.outln('}')
self.outln('') self.outln('')
self.outln('#else /* !USING_IFUNCS */') self.outln('#else /* !USING_DISPATCH_TABLE */')
for func in self.sorted_functions: for func in self.sorted_functions:
self.write_ifunc(func) self.write_function_pointer(func)
self.outln('#endif /* !USING_IFUNCS */') self.outln('#endif /* !USING_DISPATCH_TABLE */')
argparser = argparse.ArgumentParser(description='Generate GL dispatch wrappers.') argparser = argparse.ArgumentParser(description='Generate GL dispatch wrappers.')
argparser.add_argument('files', metavar='file.xml', nargs='+', help='GL API XML files to be parsed') argparser.add_argument('files', metavar='file.xml', nargs='+', help='GL API XML files to be parsed')

@ -87,8 +87,6 @@ WGL_TESTS = \
WGL_LIBS = libwgl_common.la WGL_LIBS = libwgl_common.la
endif endif
XFAIL_TESTS = glx_shared_znow
egl_has_extension_nocontext_LDFLAGS = $(X11_LIBS) $(EPOXY) libegl_common.la egl_has_extension_nocontext_LDFLAGS = $(X11_LIBS) $(EPOXY) libegl_common.la
egl_has_extension_nocontext_DEPENDENCIES = $(EPOXY) libegl_common.la egl_has_extension_nocontext_DEPENDENCIES = $(EPOXY) libegl_common.la

Loading…
Cancel
Save