This is the mail archive of the
binutils@sourceware.org
mailing list for the binutils project.
Question on scope of class member static variable
- From: Jing Yu <jingyu at google dot com>
- To: binutils at sourceware dot org
- Date: Thu, 8 May 2014 20:51:16 -0700
- Subject: Question on scope of class member static variable
- Authentication-results: sourceware.org; auth=none
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