Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to have aliased variables in shared libraries?

I want to put two aliased variables in a library so that the application code can use either name. But I find it can be done in a static library but not in a shared library. Here is my experiment. I did it on an X86 Linux machine with gcc compiler.

test.c -- the application code

#include <stdio.h>   
extern int myfunc(int, int);
extern int myglob;
extern int myglob_;

int main(int argc, const char* argv[])
{
    printf("&myglob = %p\n", &myglob);
    printf("&myglob_ = %p\n", &myglob_); 
    myglob = 1234;
    printf("myglob = %d\n", myglob);
    printf("myglob_ = %d\n", myglob_);

    return myfunc(argc, argc);
}

my.c -- the library code

int myglob = 42;
extern int myglob_ __attribute__ ((alias("myglob")));   
int myfunc(int a, int b)
{
    myglob += a;
    return b + myglob;
}

Build and run with static library. We can see myglob and myglob_ are indeed aliased.

gcc -c my.c
ar rsc libmy.a my.o
gcc -o test test.c -L. -lmy
./test
&myglob = 0x601040
&myglob_ = 0x601040
myglob = 1234
myglob_ = 1234

Build and run with shared library. We can see myglob and myglob_ point to different addresses and basically are two distinct variables.

gcc -fPIC -c my.c
gcc -shared -o libmy.so my.o
gcc -o test test.c -L. -lmy
./test
&myglob = 0x601048
&myglob_ = 0x601050
myglob = 1234
myglob_ = 42

So, why do aliased symbols not work in shared library? How to fix it? Thanks.

=============Follow up===============

I tried to build a position-independent executable using "gcc -o test test.c -L. -lmy -fPIC". With this, myglob and myglob_ are indeed aliased with the shared library. However, this approach does not work in the background problem which spawned the question. I list the problem and the reason why I need it. (Note that I have to use F77 common blocks in my project)

A Fortran header file

! myf.h
common /myblock/ myglob
save myblock

Init routine in Fortran

! initf.f90
subroutine initf()
    integer myglob
    common /myblock/  myglob
    call initc(myglob)
end subroutine

Init routine in C

// initc.c
#include <stdio.h>
extern void initf_(); 
void init() { initf_(); }
extern void init_() __attribute__((alias("init")));
void initc_(int *pmyglob) { printf("&myglob in library = %p\n", pmyglob); }

Fortran library wrapper in C

// my.c
#include <stdio.h>
extern void myfunc(int *pmyglob)
{
    printf("&myglob in app     = %p\n", pmyglob);
    // May call a Fortran subroutine. That's why myfunc() is a wrapper
}
extern void myfunc_(int *) __attribute__ ((alias("myfunc")));
typedef struct myblock_t_ {
    int idx;
} myblock_t;
myblock_t myblock __attribute__((aligned(16))) = {0};
extern myblock_t myblock_ __attribute__((alias("myblock")));

Application in Fortran

! test.f90
program main
    include 'myf.h'
    call init();
    call myfunc(myglob);
end program

Build a shared library libmy.so and use it

gfortran -fPIC -c initf.f90
gcc -fPIC -c initc.c
gcc -fPIC -c my.c
gcc -fPIC -shared -o libmy.so initf.o initc.o my.o
gfortran -fPIC -o test test.f90 -L. -lmy
./test
&myglob in library = 0x601070
&myglob in app     = 0x601070

Suppose we want the library to be portable, and the application is compiled by another Fortran compiler with a different name mangling convention (I use gfortran --fno-underscoring to mimic that)

// libmy.so is built as same as above
...
gfortran -fPIC -fno-underscoring  -o test test.f90 -L. -lmy
./test
&myglob in library = 0x7fb3c19d9060
&myglob in app     = 0x601070

Any suggestions? Thanks.

like image 279
Junchao Zhang Avatar asked Oct 11 '14 14:10

Junchao Zhang


1 Answers

This is happening because of copy relocations. Read about them here.

 readelf -Wr test | grep myglob
0000000000601028  0000000600000005 R_X86_64_COPY  0000000000601028 myglob + 0
0000000000601030  0000000900000005 R_X86_64_COPY  0000000000601030 myglob_ + 0
like image 139
Employed Russian Avatar answered Nov 14 '22 13:11

Employed Russian