How to share a global variable between a main process and a dynamic library via a static library (macOS)?

心已入冬 提交于 2020-02-02 10:13:30

问题


My question seems to be similar to this question however in my case the shared variable is in a static library and also the advice from this question didn't help me: Sharing global data between a shared library and main. Along the way I saw the issues from this question but no final solution either: OS X linker unable to find symbols from a C file which only contains variables.

I am trying to make my main process and a dynamic library that it dlopen's to share the global variable SHARED which is located in a shared library that they both link to.

I have created a minimal project on GitHub but I am also providing the contents of that project below.

The issue: The output I am expecting is to see the same variable and the same address in both cases. Instead I am seeing two copies of the SHARED variable.

My question: which combination of compilation and linker flags could remove the second instance of the SHARED variable so that only one instance would be properly shared between the main process and the dynamic library.

Additional background

After additional research, I think this question can be reduced to the following one: how to get the Linux behavior of the -rdynamic flag?

This is not my intention to run the code like this. I am porting existing software that runs on Linux. That software shares global variables between its main process and the dynamic libraries. I have verified that it is using -rdynamic to achieve this kind of behavior on Linux: on Linux, simply adding -rdynamic to the linker flags of my example's executable does! make the global variable to become shared.

What exactly does -rdynamic do and when exactly is it needed? describes the behavior that I am looking for:

If you use "dlopen" to load a dynamic object which needs to refer back to the symbols defined by the program, rather than some other dynamic object, then you will probably need to use this option when linking the program itself. ...

Now the problem is that I cannot achieve this behavior with my example on macOS. Adding -rdynamic seems to not have the effect that it has on Linux.

output

Hello, World!
SHARED: 0x104970030 123
SHARED: 0x104988018 (null)

Process finished with exit code 0

main.c

#include "dynamic_lib.h"

#include <assert.h>
#include <dlfcn.h>
#include <stdio.h>

extern char *SHARED;

int main() {
  printf("Hello, World!\n");

  SHARED = "123";
  printf("SHARED: %p %s\n", &SHARED, SHARED);

  void *handle = dlopen("libdynamic_lib.dylib", RTLD_NOW | RTLD_GLOBAL);
  assert(handle != NULL);

  void *sym = dlsym(handle, "dynamic_lib_func");
  assert(sym != NULL);

  ((void (*)(void))sym)();

  return 0;
}

dynamic_lib.c

#include "dynamic_lib.h"

#include "static_lib.h"

#include <stdio.h>

void dynamic_lib_func() {
  printf("SHARED: %p %s\n", &SHARED, SHARED);
}

static_lib.c

#include "static_lib.h"

char *SHARED; // adding = 0 doesn't change much

CMakeLists.txt

cmake_minimum_required(VERSION 3.13)
project(untitled1 C)

set(CMAKE_C_STANDARD 99)

add_library(static_lib STATIC static_lib.c)
add_library(dynamic_lib SHARED dynamic_lib.c)

target_link_libraries(dynamic_lib static_lib)

add_executable(untitled1 main.c)
target_link_libraries(untitled1 static_lib)

add_dependencies(untitled1 dynamic_lib)

回答1:


Doing more experiments and tweaking the linker flags led me to some other SO questions, including this and this.

Instead of -rdynamic that works on Linux, this is what works on macOS:

The -undefined dynamic_lookup has to be added to the linker flags of the dynamic library.

In my example, the change is as follows:

# It is important that we DO NOT link against static_lib and at the same time
# the -undefined dynamic_lookup is provided.
# target_link_libraries(dynamic_lib static_lib)
target_link_options(dynamic_lib PRIVATE -undefined dynamic_lookup)

The output that I see now is:

Hello, World!
SHARED: 0x109ead030 123
SHARED: 0x109ead030 123

Process finished with exit code 0



回答2:


Both your main executable, and the dynamic library links in static_lib.c. Therefore you will have two instances of char *SHARED;. If functions in the dynamic library will use their own instance, or use the one for the main executable, would i guess is implementation dependent. In your case, it looks as if it uses its own.

The solution is to drop char *SHARED; from the main executable and do this after dlopen:

char **shared = dlsym(handle, "SHARED");
if (!shared)
  {
     perror("dlsym failed");
     exit(EXIT_FAILURE);
  }
*shared = "123";

My memory of dlopen and dlsym is a little bit rusty, so the code may contain minor errors.

Additional comments

Do not use assert to test the return value from dlopen and dlsym. assert is for detecting logical errors in your code. It is not recommended for detecting other runtime errors. Depending on your toolchain and setup, it may even be a noop.

Don't use global variables for communication with library functions. Everything that a function need to know, should be reachable through its function parameters.



来源:https://stackoverflow.com/questions/59901279/how-to-share-a-global-variable-between-a-main-process-and-a-dynamic-library-via

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!