Question on scope of class member static variable

Jing Yu jingyu@google.com
Fri May 9 03:51:00 GMT 2014


Hi binutils@,

I have a piece of code which behave differently on x86 and aarch64.
The scenario is that both executable and shared library define a TLS
class member static variable.

On aarch64, the executable exports the symbol in dynamic symbol stable
with GLOBAL attribute. When shared library is dlopened, dynamic loader
finds the symbol in executable's symbol table and binds with it. Thus
both dynamic library and executable share the same copy of this
variable.

On x86, executable does not export this symbol. Dynamic loader binds
shared library with its own copy of the variable. Thus the executable
and shared library operates on separate copy of the variable.

My question is whether a class member static variable is accessible by
external shared library. In my code, which behavior (x86 or aarch64)
is correct.

$ g++ var_exe.cc -ldl -o var
$ g++ -shared -fPIC var_l.cc -o libvar1.so
$ g++ -shared -fPIC var_l.cc -o libvar2.so

On x86:
$ ./var
exe: live += 5
exe: live = 5
Calling libentry in ./libvar1.so...
lib: live += 3
lib: live = 3
Calling libentry in ./libvar2.so...
lib: live += 3
lib: live = 3

$ readelf --dyn-syms -W var | grep _ZN3var4liveE
$ readelf --dyn-syms -W libvar1.so | grep _ZN3var4liveE
   13: 0000000000000000     4 TLS     GLOBAL DEFAULT   16 _ZN3var4liveE
$ readelf --dyn-syms -W libvar2.so | grep _ZN3var4liveE
   13: 0000000000000000     4 TLS     GLOBAL DEFAULT   16 _ZN3var4liveE

On aarch64:
$ ./var
exe: live += 5
exe: live = 5
Calling libentry in ./libvar1.so...
lib: live += 3
lib: live = 8
Calling libentry in ./libvar2.so...
lib: live += 3
lib: live = 11

$ readelf --dyn-syms -W var | grep _ZN3var4liveE
   11: 0000000000000000     4 TLS     GLOBAL DEFAULT   18 _ZN3var4liveE
$ readelf --dyn-syms -W libvar1.so | grep _ZN3var4liveE
   16: 0000000000000000     4 TLS     GLOBAL DEFAULT   16 _ZN3var4liveE
$ readelf --dyn-syms -W libvar2.so | grep _ZN3var4liveE
   16: 0000000000000000     4 TLS     GLOBAL DEFAULT   16 _ZN3var4liveE


var.h:
#include <pthread.h>

class var{
 public:
  var(){};
  static void output(const char* prefix) {
    printf("%s: live = %d\n", prefix, live);
  };
  static void inc(const char* prefix, int i) {
    printf("%s: live += %d\n", prefix, i);
    live += i;
  };
 private:
  static __thread int live;
};

var_l.cc:
#include <pthread.h>
#include <stdio.h>
#include "var.h"

__thread int var::live=0;

extern "C" void libentry() {
  var libvar;
  libvar.inc("lib", 3);
  libvar.output("lib");
}

var_exe.cc:
#include <dlfcn.h>
#include <pthread.h>
#include <stdio.h>
#include "var.h"

__thread int var::live=0;

void* load_lib_and_call(const char* libname) {
  void* handle = dlopen(libname, RTLD_LOCAL | RTLD_NOW);
  if (handle == NULL) {
    printf("Can not open shared library %s\n", libname);
    return 0;
   }
   typedef void (*lib_fun_t)();
   lib_fun_t lib_entry_ = (lib_fun_t)(dlsym(handle, "libentry"));
   const char *dlsym_error = dlerror();
   if (dlsym_error) {
     printf("Can not load symbol libentry: %s\n", dlsym_error);
     dlclose(handle);
     return 0;
   }
   printf("Calling libentry in %s...\n", libname);
   lib_entry_();

   return handle;
}

int main() {
  var mainvar;
  mainvar.inc("exe", 5);
  mainvar.output("exe");

  void* handle1 = load_lib_and_call("./libvar1.so");
  void* handle2 = load_lib_and_call("./libvar2.so");

  if (handle1)
    dlclose(handle1);
  if (handle2)
    dlclose(handle2);

  return 0;
}


Thanks!
Jing



More information about the Binutils mailing list