您是否可以编译共享对象以优先选择本地符号,即使它是由使用-rdynamic编译的程序加载的?

The*_*ral 6 c linux gcc shared-libraries linker-flags

我正在C中构建一个由我没有源访问权限的程序动态加载的共享库.目标平台是64位Linux平台,我们正在使用gcc构建.我能够在~100行中构建问题的复制品,但仍有一点需要阅读.希望它是说明性的.

核心问题是我在共享库中定义了两个非静态函数(barbaz).两者都需要是非静态的,因为我们希望调用者能够使用它们.另外,baz电话bar.正在使用我的库的程序也有一个名为的函数bar,这通常不是问题,但调用程序是用-rdynamic它编译的,因为它有一个foo需要在我的共享库中调用的函数.结果是我的共享库最终链接到bar运行时调用程序的版本,产生不直观的结果.

在理想的世界中,我可以在编译共享库时包含一些命令行开关,以防止这种情况发生.

我当前的解决方案是将我的非静态函数重命名为funname_local并将它们声明为静态.然后我定义一个新函数: funname() { return funname_local(); },并将funname我共享库中的任何引用更改为funname_local.这有效,但感觉很麻烦,我更喜欢告诉链接器更喜欢本地编译单元中定义的符号.

internal.c

#include <stdio.h>
#include "internal.h"

void
bar(void)
{
  printf("I should only be callable from the main program\n");
}
Run Code Online (Sandbox Code Playgroud)

internal.h

#if !defined(__INTERNAL__)
#define __INTERNAL__

void
bar(void);

#endif /* defined(__INTERNAL__) */
Run Code Online (Sandbox Code Playgroud)

main.c中

#include <dlfcn.h>
#include <stdio.h>
#include "internal.h"

void
foo(void)
{
  printf("It's important that I am callable from both main and from any .so "
         "that we dlopen, that's why we compile with -rdynamic\n");
}

int
main()
{
  void *handle;
  void (*fun1)(void);
  void (*fun2)(void);
  char *error;

  if(NULL == (handle = dlopen("./shared.so", RTLD_NOW))) { /* Open library */
    fprintf(stderr, "dlopen: %s\n", dlerror());
    return 1;
  }
  dlerror(); /* Clear any existing error */

  *(void **)(&fun1) = dlsym(handle, "baz"); /* Get function pointer */
  if(NULL != (error = dlerror()))  {
    fprintf(stderr, "dlsym: %s\n", error);
    dlclose(handle);
    return 1;
  }
  *(void **)(&fun2) = dlsym(handle, "bar"); /* Get function pointer */
  if(NULL != (error = dlerror()))  {
    fprintf(stderr, "dlsym: %s\n", error);
    dlclose(handle);
    return 1;
  }

  printf("main:\n");
  foo();
  bar();
  fun1();
  fun2();

  dlclose(handle);
  return 0;
}
Run Code Online (Sandbox Code Playgroud)

main.h

#if !defined(__MAIN__)
#define __MAIN__

extern void
foo(void);

#endif /* defined(__MAIN__) */
Run Code Online (Sandbox Code Playgroud)

shared.c

#include <stdio.h>
#include "main.h"

void
bar(void)
{
  printf("bar:\n");
  printf("It's important that I'm callable from a program that loads shared.so"
         " as well as from other functions in shared.so\n");
}

void
baz(void)
{
  printf("baz:\n");
  foo();
  bar();
  return;
}
Run Code Online (Sandbox Code Playgroud)

编译:

$ gcc -m64 -std=c89 -Wall -Wextra -Werror -pedantic -o main main.c internal.c -l dl -rdynamic
$ gcc -m64 -std=c89 -Wall -Wextra -Werror -pedantic -shared -fPIC -o shared.so shared.c
Run Code Online (Sandbox Code Playgroud)

跑:

$ ./main
main:
It's important that I am callable from both main and from any .so that we dlopen, that's why we compile with -rdynamic
I should only be callable from the main program
baz:
It's important that I am callable from both main and from any .so that we dlopen, that's why we compile with -rdynamic
I should only be callable from the main program
bar:
It's important that I'm callable from a program that loads shared.so as well as from other functions in shared.so
Run Code Online (Sandbox Code Playgroud)

Rom*_*mov 8

您是否尝试过-Bsymbolic链接器选项(或-Bsymbolic-functions)?引自ld男人:

-Bsymbolic

创建共享库时,将对全局符号的引用绑定到共享库中的定义(如果有).通常,链接到共享库的程序可以覆盖共享库中的定义.在创建与位置无关的可执行文件时,此选项还可以与--export-dynamic选项一起使用,以将对全局符号的引用绑定到可执行文件中的定义.此选项仅对支持共享库和位置无关可执行文件的ELF平台有意义.

它似乎解决了这个问题:

$ gcc -m64 -std=c89 -Wall -Wextra -Werror -pedantic -shared -fPIC -o shared.so shared.c
$ gcc -m64 -std=c89 -Wall -Wextra -Werror -pedantic -o main main.c internal.c -l dl -rdynamic
$ ./main 
main:
It's important that I am callable from both main and from any .so that we dlopen, that's why we compile with -rdynamic
I should only be callable from the main program
baz:
It's important that I am callable from both main and from any .so that we dlopen, that's why we compile with -rdynamic
I should only be callable from the main program
bar:
It's important that I'm callable from a program that loads shared.so as well as from other functions in shared.so
$ gcc -m64 -std=c89 -Wall -Wextra -Werror -pedantic -shared -fPIC -Wl,-Bsymbolic -o shared.so shared.c
$ ./main 
main:
It's important that I am callable from both main and from any .so that we dlopen, that's why we compile with -rdynamic
I should only be callable from the main program
baz:
It's important that I am callable from both main and from any .so that we dlopen, that's why we compile with -rdynamic
bar:
It's important that I'm callable from a program that loads shared.so as well as from other functions in shared.so
bar:
It's important that I'm callable from a program that loads shared.so as well as from other functions in shared.so
Run Code Online (Sandbox Code Playgroud)