Add TLS support to libGL and, by virtue of using glthread.h and GL_CALL, all
authorIan Romanick <idr@us.ibm.com>
Wed, 13 Apr 2005 20:59:15 +0000 (20:59 +0000)
committerIan Romanick <idr@us.ibm.com>
Wed, 13 Apr 2005 20:59:15 +0000 (20:59 +0000)
DRI drivers.  A TLS enabled libGL can load a TLS or a non-TLS DRI driver,
but a TLS DRI driver requires a TLS enabled libGL.

This fixes bug #1822.

configs/linux-dri
src/glx/x11/dri_glx.c
src/mesa/glapi/gl_x86_asm.py
src/mesa/glapi/glapi.c
src/mesa/glapi/glapi.h
src/mesa/glapi/glthread.h
src/mesa/x86/glapi_x86.S

index 5d44ccd..0953fb2 100644 (file)
@@ -16,6 +16,8 @@ MKDEP = /usr/X11R6/bin/makedepend
 WARN_FLAGS = -Wall
 OPT_FLAGS  = -O -g
 PIC_FLAGS  = -fPIC
+
+# Add '-DGLX_USE_TLS' to ARCH_FLAGS to enable TLS support.
 ARCH_FLAGS ?=
 
 DEFINES = -D_POSIX_SOURCE -D_POSIX_C_SOURCE=199309L -D_SVID_SOURCE \
index b703c4f..7ac80eb 100644 (file)
@@ -222,6 +222,15 @@ static __DRIdriver *OpenDriver(const char *driverName)
       void *handle = NULL;
 
       
+      /* If TLS support is enabled, try to open the TLS version of the driver
+       * binary first.  If that fails, try the non-TLS version.
+       */
+#ifdef GLX_USE_TLS
+      snprintf(realDriverName, 200, "%s/tls/%s_dri.so", libDir, driverName);
+      InfoMessageF("OpenDriver: trying %s\n", realDriverName);
+      handle = dlopen(realDriverName, RTLD_NOW | RTLD_GLOBAL);
+#endif
+
       if ( handle == NULL ) {
         snprintf(realDriverName, 200, "%s/%s_dri.so", libDir, driverName);
         InfoMessageF("OpenDriver: trying %s\n", realDriverName);
index 9c29993..442069b 100644 (file)
@@ -63,7 +63,10 @@ class PrintGenericStubs(gl_XML.FilterGLAPISpecBase):
                print "* the symbol visibility mode to 'default'."
                print '*/'
                print '#if defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 303'
-               print '#pragma GCC visibility push(default)'
+               print '#  pragma GCC visibility push(default)'
+               print '#  define HIDDEN(x) .hidden x'
+               print '#else'
+               print '#  define HIDDEN(x)'
                print '#endif'
                print ''
                print '#ifndef __WIN32__'
@@ -94,7 +97,17 @@ class PrintGenericStubs(gl_XML.FilterGLAPISpecBase):
                print '#  define THREADS'
                print '#endif'
                print ''
-               print '#if defined(PTHREADS)'
+               print '#ifdef GLX_USE_TLS'
+               print ''
+               print '#  define GL_STUB(fn,off,fn_alt)\t\t\t\\'
+               print 'ALIGNTEXT16;\t\t\t\t\t\t\\'
+               print 'GLOBL_FN(GL_PREFIX(fn, fn_alt));\t\t\t\\'
+               print 'GL_PREFIX(fn, fn_alt):\t\t\t\t\t\\'
+               print '\tCALL(_x86_get_dispatch) ;\t\t\t\\'
+               print '\tNOP ;\t\t\t\t\t\t\\'
+               print '\tJMP(GL_OFFSET(off))'
+               print ''
+               print '#elif defined(PTHREADS)'
                print '#  define GL_STUB(fn,off,fn_alt)\t\t\t\\'
                print 'ALIGNTEXT16;\t\t\t\t\t\t\\'
                print 'GLOBL_FN(GL_PREFIX(fn, fn_alt));\t\t\t\\'
@@ -136,7 +149,16 @@ class PrintGenericStubs(gl_XML.FilterGLAPISpecBase):
                print ''
                print 'SEG_TEXT'
                print ''
-               print '#ifdef PTHREADS'
+               print '#ifdef GLX_USE_TLS'
+               print ''
+               print '\tGLOBL\tGLNAME(_x86_get_dispatch)'
+               print '\tHIDDEN(GLNAME(_x86_get_dispatch))'
+               print 'ALIGNTEXT16'
+               print 'GLNAME(_x86_get_dispatch):'
+               print '\tmovl\t%gs:_glapi_tls_Dispatch@NTPOFF, %eax'
+               print '\tret'
+               print ''
+               print '#elif defined(PTHREADS)'
                print 'EXTERN GLNAME(_glapi_Dispatch)'
                print 'EXTERN GLNAME(_gl_DispatchTSD)'
                print 'EXTERN GLNAME(pthread_getspecific)'
@@ -152,13 +174,39 @@ class PrintGenericStubs(gl_XML.FilterGLAPISpecBase):
                print 'EXTERN GLNAME(_glapi_get_dispatch)'
                print '#endif'
                print ''
-               print '\t\tALIGNTEXT16 ; GLOBL GLNAME(gl_dispatch_functions_start)'
+
+               print '#if defined( GLX_USE_TLS )'
+               print '\t\t.section\twtext, "awx", @progbits'
+               print '#endif /* defined( GLX_USE_TLS ) */'
+
+               print ''
+               print '\t\tALIGNTEXT16'
+               print '\t\tGLOBL GLNAME(gl_dispatch_functions_start)'
+               print '\t\tHIDDEN(GLNAME(gl_dispatch_functions_start))'
                print 'GLNAME(gl_dispatch_functions_start):'
                print ''
                return
 
        def printRealFooter(self):
                print ''
+               print '\t\tGLOBL\tGLNAME(gl_dispatch_functions_end)'
+               print '\t\tHIDDEN(GLNAME(gl_dispatch_functions_end))'
+               print '\t\tALIGNTEXT16'
+               print 'GLNAME(gl_dispatch_functions_end):'
+               print ''
+               print '#if defined(GLX_USE_TLS) && defined(__linux__)'
+               print ' .section ".note.ABI-tag", "a"'
+               print ' .p2align 2'
+               print ' .long   1f - 0f   /* name length */'
+               print ' .long   3f - 2f   /* data length */'
+               print ' .long   1         /* note length */'
+               print '0:       .asciz "GNU"      /* vendor name */'
+               print '1:       .p2align 2'
+               print '2:       .long   0         /* note data: the ABI tag */'
+               print ' .long   2,4,20    /* Minimum kernel version w/TLS */'
+               print '3:       .p2align 2        /* pad out section */'
+               print '#endif /* GLX_USE_TLS */'
+               print ''
                print '#endif  /* __WIN32__ */'
                return
 
@@ -167,11 +215,11 @@ class PrintGenericStubs(gl_XML.FilterGLAPISpecBase):
 
                alt = "%s@%u" % (f.name, stack)
                if f.fn_alias == None:
-                   print '\tGL_STUB(%s, _gloffset_%s, %s)' % (f.name, f.real_name, alt)
+                       print '\tGL_STUB(%s, _gloffset_%s, %s)' % (f.name, f.real_name, alt)
                else:
-                   alias_alt = "%s@%u" % (f.real_name, stack)
-                   print '\tGL_STUB_ALIAS(%s, _gloffset_%s, %s, %s, %s)' % \
-                       (f.name, f.real_name, alt, f.real_name, alias_alt)
+                       alias_alt = "%s@%u" % (f.real_name, stack)
+                       print '\tGL_STUB_ALIAS(%s, _gloffset_%s, %s, %s, %s)' % \
+                           (f.name, f.real_name, alt, f.real_name, alias_alt)
                return
 
 def show_usage():
index bbc0a96..8ed8330 100644 (file)
 static GLboolean WarnFlag = GL_FALSE;
 static _glapi_warning_func warning_func;
 
+static void init_glapi_relocs(void);
+
+static _glapi_proc generate_entrypoint(GLuint functionOffset);
+static void fill_in_entrypoint_offset(_glapi_proc entrypoint, GLuint offset);
 
 /*
  * Enable/disable printing of warning messages.
@@ -133,6 +137,28 @@ static GLint NoOpUnused(void)
 
 #if defined(THREADS)
 
+#if defined(GLX_USE_TLS)
+
+__thread struct _glapi_table * _glapi_tls_Dispatch
+    __attribute__((tls_model("initial-exec")))
+    = (struct _glapi_table *) __glapi_noop_table;
+
+static __thread struct _glapi_table * _glapi_tls_RealDispatch
+    __attribute__((tls_model("initial-exec")))
+    = (struct _glapi_table *) __glapi_noop_table;
+
+__thread void * _glapi_tls_Context
+    __attribute__((tls_model("initial-exec")));
+
+/**
+ * Legacy per-thread dispatch pointer.  This is only needed to support
+ * non-TLS DRI drivers.
+ */
+
+_glthread_TSD _gl_DispatchTSD;
+
+#else
+
 /**
  * \name Multi-threaded control support variables
  *
@@ -166,6 +192,7 @@ static _glthread_TSD RealDispatchTSD;    /**< only when using override */
 static _glthread_TSD ContextTSD;         /**< Per-thread context pointer */
 /*@}*/
 
+#endif /* defined(GLX_USE_TLS) */
 
 #define DISPATCH_TABLE_NAME __glapi_threadsafe_table
 #define UNUSED_TABLE_NAME __unused_threadsafe_functions
@@ -184,6 +211,29 @@ static GLint glUnused(void)
 /***** END THREAD-SAFE DISPATCH *****/
 
 
+#if defined(GLX_USE_TLS)
+
+/**
+ * \name Old dispatch pointers
+ *
+ * Very old DRI based drivers assume that \c _glapi_Dispatch will never be
+ * \c NULL.  Becuase of that, special "thread-safe" dispatch functions are
+ * needed here.  Slightly more recent drivers detect the multi-threaded case
+ * by \c _glapi_DispatchTSD being \c NULL.
+ *
+ * \deprecated
+ * 
+ * \warning
+ * \c _glapi_RealDispatch does not exist in TLS builds.  I don't think it was
+ * ever used outside libGL.so, so this should be safe.
+ */
+/*@{*/
+PUBLIC const struct _glapi_table *_glapi_Dispatch = (struct _glapi_table *) __glapi_threadsafe_table;
+PUBLIC const struct _glapi_table *_glapi_DispatchTSD = NULL;
+PUBLIC const void *_glapi_Context = NULL;
+/*@}*/
+
+#else
 
 PUBLIC struct _glapi_table *_glapi_Dispatch = (struct _glapi_table *) __glapi_noop_table;
 #if defined( THREADS )
@@ -191,10 +241,11 @@ PUBLIC struct _glapi_table *_glapi_DispatchTSD = (struct _glapi_table *) __glapi
 #endif
 PUBLIC struct _glapi_table *_glapi_RealDispatch = (struct _glapi_table *) __glapi_noop_table;
 
-
 /* Used when thread safety disabled */
 PUBLIC void *_glapi_Context = NULL;
 
+#endif /* defined(GLX_USE_TLS) */
+
 
 static GLboolean DispatchOverride = GL_FALSE;
 
@@ -224,7 +275,7 @@ str_dup(const char *str)
 PUBLIC void
 _glapi_check_multithread(void)
 {
-#if defined(THREADS)
+#if defined(THREADS) && !defined(GLX_USE_TLS)
    if (!ThreadSafe) {
       static unsigned long knownID;
       static GLboolean firstCall = GL_TRUE;
@@ -255,7 +306,9 @@ PUBLIC void
 _glapi_set_context(void *context)
 {
    (void) __unused_noop_functions; /* silence a warning */
-#if defined(THREADS)
+#if defined(GLX_USE_TLS)
+   _glapi_tls_Context = context;
+#elif defined(THREADS)
    (void) __unused_threadsafe_functions; /* silence a warning */
    _glthread_SetTSD(&ContextTSD, context);
    _glapi_Context = (ThreadSafe) ? NULL : context;
@@ -274,7 +327,9 @@ _glapi_set_context(void *context)
 PUBLIC void *
 _glapi_get_context(void)
 {
-#if defined(THREADS)
+#if defined(GLX_USE_TLS)
+   return _glapi_tls_Context;
+#elif defined(THREADS)
    if (ThreadSafe) {
       return _glthread_GetTSD(&ContextTSD);
    }
@@ -294,6 +349,13 @@ _glapi_get_context(void)
 PUBLIC void
 _glapi_set_dispatch(struct _glapi_table *dispatch)
 {
+#if defined(PTHREADS) || defined(GLX_USE_TLS)
+   static pthread_once_t once_control = PTHREAD_ONCE_INIT;
+
+
+   pthread_once( & once_control, init_glapi_relocs );
+#endif
+
    if (!dispatch) {
       /* use the no-op functions */
       dispatch = (struct _glapi_table *) __glapi_noop_table;
@@ -304,7 +366,15 @@ _glapi_set_dispatch(struct _glapi_table *dispatch)
    }
 #endif
 
-#if defined(THREADS)
+#if defined(GLX_USE_TLS)
+   if (DispatchOverride) {
+      _glapi_tls_RealDispatch = dispatch;
+   }
+   else {
+      _glthread_SetTSD(&_gl_DispatchTSD, (void *) dispatch);
+      _glapi_tls_Dispatch = dispatch;
+   }   
+#elif defined(THREADS)
    if (DispatchOverride) {
       _glthread_SetTSD(&RealDispatchTSD, (void *) dispatch);
       if (ThreadSafe)
@@ -342,7 +412,13 @@ _glapi_set_dispatch(struct _glapi_table *dispatch)
 PUBLIC struct _glapi_table *
 _glapi_get_dispatch(void)
 {
-#if defined(THREADS)
+#if defined(GLX_USE_TLS)
+   struct _glapi_table * api = (DispatchOverride)
+     ? _glapi_tls_RealDispatch : _glapi_tls_Dispatch;
+
+   assert( api != NULL );
+   return api;
+#elif defined(THREADS)
    if (ThreadSafe) {
       if (DispatchOverride) {
          return (struct _glapi_table *) _glthread_GetTSD(&RealDispatchTSD);
@@ -399,7 +475,10 @@ _glapi_begin_dispatch_override(struct _glapi_table *override)
 
    _glapi_set_dispatch(real);
 
-#if defined(THREADS)
+#if defined(GLX_USE_TLS)
+   _glthread_SetTSD(&_gl_DispatchTSD, (void *) override);
+   _glapi_tls_Dispatch = override;
+#elif defined(THREADS)
    _glthread_SetTSD(&_gl_DispatchTSD, (void *) override);
    if ( ThreadSafe ) {
       _glapi_Dispatch = (struct _glapi_table *) __glapi_threadsafe_table;
@@ -424,10 +503,14 @@ _glapi_end_dispatch_override(int layer)
    DispatchOverride = GL_FALSE;
    _glapi_set_dispatch(real);
    /* the rest of this isn't needed, just play it safe */
-#if defined(THREADS)
+#if defined(GLX_USE_TLS)
+   _glapi_tls_RealDispatch = NULL;
+#else
+# if defined(THREADS)
    _glthread_SetTSD(&RealDispatchTSD, NULL);
-#endif
+# endif
    _glapi_RealDispatch = NULL;
+#endif
 }
 
 
@@ -439,7 +522,9 @@ _glapi_get_override_dispatch(int layer)
    }
    else {
       if (DispatchOverride) {
-#if defined(THREADS)
+#if defined(GLX_USE_TLS)
+        return (struct _glapi_table *) _glapi_tls_Dispatch;
+#elif defined(THREADS)
          return (struct _glapi_table *) _glthread_GetTSD(&_gl_DispatchTSD);
 #else
          return _glapi_Dispatch;
@@ -498,9 +583,15 @@ get_static_proc_offset(const char *funcName)
 
 
 #ifdef USE_X86_ASM
+
+#if defined( GLX_USE_TLS )
+extern       GLubyte gl_dispatch_functions_start[];
+extern       GLubyte gl_dispatch_functions_end[];
+#else
 extern const GLubyte gl_dispatch_functions_start[];
+#endif
 
-# if defined(THREADS)
+# if defined(THREADS) && !defined(GLX_USE_TLS)
 #  define X86_DISPATCH_FUNCTION_SIZE  32
 # else
 #  define X86_DISPATCH_FUNCTION_SIZE  16
@@ -603,45 +694,20 @@ static _glapi_proc
 generate_entrypoint(GLuint functionOffset)
 {
 #if defined(USE_X86_ASM)
-   /*
-    * This x86 code contributed by Josh Vanderhoof.
-    *
-    *  0:   a1 10 32 54 76          movl   __glapi_Dispatch,%eax
-    *       00 01 02 03 04
-    *  5:   85 c0                   testl  %eax,%eax
-    *       05 06
-    *  7:   74 06                   je     f <entrypoint+0xf>
-    *       07 08
-    *  9:   ff a0 10 32 54 76       jmp    *0x76543210(%eax)
-    *       09 0a 0b 0c 0d 0e
-    *  f:   e8 fc ff ff ff          call   __glapi_get_dispatch
-    *       0f 10 11 12 13
-    * 14:   ff a0 10 32 54 76       jmp    *0x76543210(%eax)
-    *       14 15 16 17 18 19
+   /* 32 is chosen as something of a magic offset.  For x86, the dispatch
+    * at offset 32 is the first one where the offset in the
+    * "jmp OFFSET*4(%eax)" can't be encoded in a single byte.
     */
-   static const unsigned char insn_template[] = {
-      0xa1, 0x00, 0x00, 0x00, 0x00,
-      0x85, 0xc0,
-      0x74, 0x06,
-      0xff, 0xa0, 0x00, 0x00, 0x00, 0x00,
-      0xe8, 0x00, 0x00, 0x00, 0x00,
-      0xff, 0xa0, 0x00, 0x00, 0x00, 0x00
-   };
-   unsigned char *code = (unsigned char *) malloc(sizeof(insn_template));
-   unsigned int next_insn;
-   if (code) {
-      memcpy(code, insn_template, sizeof(insn_template));
+   const GLubyte * const template_func = gl_dispatch_functions_start 
+     + (X86_DISPATCH_FUNCTION_SIZE * 32);
+   GLubyte * const code = (GLubyte *) malloc( X86_DISPATCH_FUNCTION_SIZE );
 
-#if defined( THREADS )
-      *(unsigned int *)(code + 0x01) = (unsigned int)&_glapi_DispatchTSD;
-#else
-      *(unsigned int *)(code + 0x01) = (unsigned int)&_glapi_Dispatch;
-#endif
-      *(unsigned int *)(code + 0x0b) = (unsigned int)functionOffset * 4;
-      next_insn = (unsigned int)(code + 0x14);
-      *(unsigned int *)(code + 0x10) = (unsigned int)_glapi_get_dispatch - next_insn;
-      *(unsigned int *)(code + 0x16) = (unsigned int)functionOffset * 4;
+
+   if ( code != NULL ) {
+      (void) memcpy( code, template_func, X86_DISPATCH_FUNCTION_SIZE );
+      fill_in_entrypoint_offset( (_glapi_proc) code, functionOffset );
    }
+
    return (_glapi_proc) code;
 #elif defined(USE_SPARC_ASM)
 
@@ -707,10 +773,19 @@ static void
 fill_in_entrypoint_offset(_glapi_proc entrypoint, GLuint offset)
 {
 #if defined(USE_X86_ASM)
-
-   unsigned char *code = (unsigned char *) entrypoint;
-   *(unsigned int *)(code + 0x0b) = offset * 4;
-   *(unsigned int *)(code + 0x16) = offset * 4;
+   GLubyte * const code = (GLubyte *) entrypoint;
+
+   
+#if X86_DISPATCH_FUNCTION_SIZE == 32
+   *((unsigned int *)(code + 11)) = 4 * offset;
+   *((unsigned int *)(code + 22)) = 4 * offset;
+#elif X86_DISPATCH_FUNCTION_SIZE == 16 && defined( GLX_USE_TLS )
+   *((unsigned int *)(code +  8)) = 4 * offset;
+#elif X86_DISPATCH_FUNCTION_SIZE == 16
+   *((unsigned int *)(code +  7)) = 4 * offset;
+#else
+# error Invalid X86_DISPATCH_FUNCTION_SIZE!
+#endif
 
 #elif defined(USE_SPARC_ASM)
 
@@ -1028,3 +1103,25 @@ _glapi_check_table(const struct _glapi_table *table)
    (void) table;
 #endif
 }
+
+
+/**
+ * Perform platform-specific GL API entry-point fixups.
+ * 
+ * 
+ */
+static void
+init_glapi_relocs( void )
+{
+#if defined( USE_X86_ASM ) && defined( GLX_USE_TLS )
+    extern void * _x86_get_dispatch(void);
+    const GLubyte * const get_disp = (const GLubyte *) _x86_get_dispatch;
+    GLubyte * curr_func = (GLubyte *) gl_dispatch_functions_start;
+
+
+    while ( curr_func != (GLubyte *) gl_dispatch_functions_end ) {
+       (void) memcpy( curr_func, get_disp, 6 );
+       curr_func += X86_DISPATCH_FUNCTION_SIZE;
+    }
+#endif /* defined( USE_X86_ASM ) && defined( GLX_USE_TLS ) */
+}
index 7cdccc1..e3ef066 100644 (file)
@@ -54,10 +54,17 @@ typedef void (*_glapi_warning_func)(void *ctx, const char *str, ...);
 typedef void (*_glapi_proc)(void); /* generic function pointer */
 
 
-extern void *_glapi_Context;
+#if defined (GLX_USE_TLS)
+
+const extern void *_glapi_Context;
+const extern struct _glapi_table *_glapi_Dispatch;
 
+#else
+
+extern void *_glapi_Context;
 extern struct _glapi_table *_glapi_Dispatch;
 
+#endif /* defined (GLX_USE_TLS) */
 
 extern void
 _glapi_noop_enable_warnings(GLboolean enable);
index 1d2a4a7..615d169 100644 (file)
@@ -306,7 +306,14 @@ _glthread_GetTSD(_glthread_TSD *);
 extern void
 _glthread_SetTSD(_glthread_TSD *, void *);
 
-#ifndef GL_CALL
+#if defined(GLX_USE_TLS)
+
+extern __thread struct _glapi_table * _glapi_tls_Dispatch
+    __attribute__((tls_model("initial-exec")));
+
+# define GL_CALL(name) (*(_glapi_tls_Dispatch-> name))
+
+#elif !defined(GL_CALL)
 # if defined(THREADS)
 extern struct _glapi_table * _glapi_DispatchTSD;
 #  define GL_CALL(name) \
index aee8565..c0a971b 100644 (file)
 * the symbol visibility mode to 'default'.
 */
 #if defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 303
-#pragma GCC visibility push(default)
+#  pragma GCC visibility push(default)
+#  define HIDDEN(x) .hidden x
+#else
+#  define HIDDEN(x)
 #endif
 
 #ifndef __WIN32__
 #  define THREADS
 #endif
 
-#if defined(PTHREADS)
+#ifdef GLX_USE_TLS
+
+#  define GL_STUB(fn,off,fn_alt)                       \
+ALIGNTEXT16;                                           \
+GLOBL_FN(GL_PREFIX(fn, fn_alt));                       \
+GL_PREFIX(fn, fn_alt):                                 \
+       CALL(_x86_get_dispatch) ;                       \
+       NOP ;                                           \
+       JMP(GL_OFFSET(off))
+
+#elif defined(PTHREADS)
 #  define GL_STUB(fn,off,fn_alt)                       \
 ALIGNTEXT16;                                           \
 GLOBL_FN(GL_PREFIX(fn, fn_alt));                       \
@@ -106,7 +119,16 @@ GL_PREFIX(fn, fn_alt):                                     \
 
 SEG_TEXT
 
-#ifdef PTHREADS
+#ifdef GLX_USE_TLS
+
+       GLOBL   GLNAME(_x86_get_dispatch)
+       HIDDEN(GLNAME(_x86_get_dispatch))
+ALIGNTEXT16
+GLNAME(_x86_get_dispatch):
+       movl    %gs:_glapi_tls_Dispatch@NTPOFF, %eax
+       ret
+
+#elif defined(PTHREADS)
 EXTERN GLNAME(_glapi_Dispatch)
 EXTERN GLNAME(_gl_DispatchTSD)
 EXTERN GLNAME(pthread_getspecific)
@@ -122,7 +144,13 @@ GLNAME(_x86_get_dispatch):
 EXTERN GLNAME(_glapi_get_dispatch)
 #endif
 
-               ALIGNTEXT16 ; GLOBL GLNAME(gl_dispatch_functions_start)
+#if defined( GLX_USE_TLS )
+               .section        wtext, "awx", @progbits
+#endif /* defined( GLX_USE_TLS ) */
+
+               ALIGNTEXT16
+               GLOBL GLNAME(gl_dispatch_functions_start)
+               HIDDEN(GLNAME(gl_dispatch_functions_start))
 GLNAME(gl_dispatch_functions_start):
 
        GL_STUB(NewList, _gloffset_NewList, NewList@8)
@@ -1120,4 +1148,22 @@ GLNAME(gl_dispatch_functions_start):
        GL_STUB_ALIAS(PointParameterfSGIS, _gloffset_PointParameterfEXT, PointParameterfSGIS@8, PointParameterfEXT, PointParameterfEXT@8)
        GL_STUB_ALIAS(PointParameterfvSGIS, _gloffset_PointParameterfvEXT, PointParameterfvSGIS@8, PointParameterfvEXT, PointParameterfvEXT@8)
 
+               GLOBL   GLNAME(gl_dispatch_functions_end)
+               HIDDEN(GLNAME(gl_dispatch_functions_end))
+               ALIGNTEXT16
+GLNAME(gl_dispatch_functions_end):
+
+#if defined(GLX_USE_TLS) && defined(__linux__)
+       .section ".note.ABI-tag", "a"
+       .p2align 2
+       .long   1f - 0f   /* name length */
+       .long   3f - 2f   /* data length */
+       .long   1         /* note length */
+0:     .asciz "GNU"      /* vendor name */
+1:     .p2align 2
+2:     .long   0         /* note data: the ABI tag */
+       .long   2,4,20    /* Minimum kernel version w/TLS */
+3:     .p2align 2        /* pad out section */
+#endif /* GLX_USE_TLS */
+
 #endif  /* __WIN32__ */