【问题标题】:Access static class members from dynamic library in c++从 C++ 中的动态库访问静态类成员
【发布时间】:2021-01-21 07:19:46
【问题描述】:

首先,这是我的第一个问题,我通常会找到答案,如果我犯了一些错误,请见谅。

我正在尝试从动态库访问静态类的成员。问题是,我不知道为什么,这个静态类成员的内存不一样,取决于我访问它的方式。

我有一个主程序执行多项操作并调用多个动态库(使用 dlopen / dlsym)。这些动态库应该访问一些属于主程序的变量。如果这些变量不是静态的,我可以共享一个指针,所以我没有这些问题。但是,当这些变量是静态的时,我发现了一些问题。

我在 linux 下运行这个程序,使用 g++ 7.5.0 和 glibc 2.27。

这是一个显示我遇到的问题的小例子: 主程序main.cpp修改静态变量value,调用动态库libShare.so创建ShareDerived对象。这个对象访问静态变量value来打印它。

/*** Main.cpp ***/
#include <dlfcn.h>
#include "ShareBase.hpp"

int StaticClass::value = 1;

int main() {
    StaticClass::value = 3;
    void* hdl = dlopen("./lib/libShare.so", RTLD_LAZY | RTLD_GLOBAL);
    ShareBase*(*obj)() = (ShareBase*(*)())dlsym(hdl,"createObject");
    obj()->BaseValue();
    obj()->DerivedValue();
    delete (obj());
    dlclose(hdl);
    return 0;
}

/*** ShareBase.hpp ***/
#pragma once

class StaticClass {
    public:
    StaticClass() = delete;
    ~StaticClass() = delete;
    static int value;
};
class ShareBase {
    public:
    ShareBase();
    virtual ~ShareBase();
    void BaseValue();
    virtual void DerivedValue() = 0;
};

/*** ShareBase.cpp ***/
#include "ShareBase.hpp"
#include <stdio.h>

ShareBase::ShareBase() {}
ShareBase::~ShareBase() {}
void ShareBase::BaseValue() {
    printf("Base: %d --> %p\n", StaticClass::value, (void*)&StaticClass::value);
}

/*** ShareDerived.hpp ***/
#pragma once
#include "ShareBase.hpp"

class ShareDerived:public ShareBase {
    public:
    ShareDerived();
    virtual ~ShareDerived();
    void DerivedValue();
};

extern "C" {
    ShareBase* createObject() {
        auto *m = new ShareDerived();
        return (ShareBase*)m;
    }
}

/*** ShareDerived.cpp ***/
#include "ShareDerived.hpp"
#include <stdio.h>

ShareDerived::ShareDerived() {}
ShareDerived::~ShareDerived() {}
void ShareDerived::DerivedValue() {
    printf("Derived: %d --> %p\n", StaticClass::value, (void*)&StaticClass::value);
    printf("Derived: ");
    BaseValue();
}

这是我用来编译这个例子的makefile:

CXX         := g++
CXX_FLAGS   := -Wall -Wextra -std=c++17 -ggdb

BIN         := bin
SRC         := src
INC         := include
LIB         := lib

EXE         := test

all: main lib

main: $(BIN)/$(EXE)
    ar rcs $(LIB)/libMain.a $(BIN)/Main.o $(BIN)/ShareBase.o

lib: $(LIB)/libShare.so

$(BIN)/$(EXE): $(BIN)/Main.o $(BIN)/ShareBase.o
    $(CXX) $(CXX_FLAGS) -I$(INC) -L$(LIB) $^ -o $@ -ldl

$(LIB)/libShare.so: $(BIN)/ShareDerived.o
    $(CXX) $(CXX_FLAGS) -I$(INC) -L$(LIB) -shared $^ -o $@ -lMain

$(BIN)/%.o: $(SRC)/%.cpp
    $(CXX) $(CXX_FLAGS) -I$(INC) -L$(LIB) -fPIC -c -o $@ $<

clean:
    -rm $(BIN)/* $(LIB)/*

这是我得到的输出:

Base: 3 --> 0x55e8df87f010 <-- Calling base function from main, static member value is OK.
Derived: 1 --> 0x7f40b5918090 <-- Why this value (and pointer) is not the same as previous one?
Derived: Base: 1 --> 0x7f40b5918090 <-- Accessing to this value calling Base function from Derived class is neither OK.

那么,我的错在哪里?我可以这样做吗?为什么行为会根据您访问静态成员的方式而有所不同?使用相同的指针,调用任何派生类函数,静态变量的值是调用 main 之前保存的值。但是,调用基函数时,静态变量的值是您在执行过程中修改的值。

如果问题是概念性的;如何让主程序执行多项操作并调用需要访问某些主程序变量的动态库?

这是我的第一篇文章,所以可能不清楚。在这种情况下,请告诉我,我会进行编辑以使其更清楚。

编辑: 之后,@sam 回答,我修改了 makefile,现在它按预期工作了

CXX         := g++
CXX_FLAGS   := -Wall -Wextra -std=c++17 -ggdb

BIN         := bin
SRC         := src
INC         := include
LIB         := lib

EXE         := test

all: main lib exec

exec: $(BIN)/$(EXE)

main: $(LIB)/libMain.so

lib: $(LIB)/libShare.so

$(BIN)/$(EXE): $(BIN)/Main.o
    $(CXX) $(CXX_FLAGS) -I$(INC) -L$(LIB) -Wl,-rpath,$(LIB) $^ -o $@ -ldl -lMain

$(LIB)/libMain.so: $(BIN)/ShareBase.o
    $(CXX) $(CXX_FLAGS) -I$(INC) -L$(LIB) -shared $^ -o $@

$(LIB)/libShare.so: $(BIN)/ShareDerived.o
    $(CXX) $(CXX_FLAGS) -I$(INC) -L$(LIB) -Wl,-rpath,$(LIB) -shared $^ -o $@ -lMain

$(BIN)/%.o: $(SRC)/%.cpp
    $(CXX) $(CXX_FLAGS) -I$(INC) -L$(LIB) -fPIC -c -o $@ $<

clean:
    -rm $(BIN)/* $(LIB)/*

【问题讨论】:

标签: c++ static shared-libraries dlopen dynamic-library


【解决方案1】:
$(CXX) $(CXX_FLAGS) -I$(INC) -L$(LIB) -shared $^ -o $@ -lMain

您正在将Main.cpp 直接链接到共享库。

您正在运行 Main.cpp 作为可执行文件的一部分。

因此,静态类成员存在重复的实例。这违反了单一定义规则,并导致未定义的行为。这是对您观察到的行为的解释。

您很可能这样做是因为没有链接-lMain 显示的程序段错误。发生这种情况的原因完全不相关——内联 C 链接函数永远不会在共享库中实例化,因为编译器在编译共享库时没有理由实例化它。您可能无法弄清楚原因,但发现链接 Main 解决了段错误,但导致了这种行为。这是一个经典的 XY 问题。你的问题应该是你最初为什么会遇到段错误。

【讨论】:

  • 好的。我看到了问题。我正在尝试更改我的 makefile 以避免重复的实例,但是,目前,我无法做到。如果你能帮助我,我会感激的。我将共享库与-lMain 链接,因为它使用主程序成员和函数(在本例中为ShareBase.cpp),因此共享库需要它。没错,如果我去掉它,就会出现分段错误,但在我看来,这很正常。
  • 我做到了。我将使用新的 Makefile 更新我的问题。谢谢@sam!
猜你喜欢
  • 2018-10-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-06-15
  • 1970-01-01
相关资源
最近更新 更多