【问题标题】:Can I export functions of a static library when building a dynamic library linking against that static library?在构建链接静态库的动态库时,我可以导出静态库的函数吗?
【发布时间】:2017-12-13 10:56:31
【问题描述】:

在 win32 上,我构建了一个名为 A.dll 的动态库,它链接到一个名为 B.lib 的静态库,还构建了一个名为 C 的可执行文件依赖于A.dll的.exe

但是现在,如果我想在 C.exe 中使用仅在 B.lib 中定义的函数 foo,我必须再次将 C.exeB.lib 链接起来。

问题是我可以在构建 A.dll 时将 fooB.lib 直接导出到 A.dll 中吗?强>,如何?

我也想知道与 GCC 打交道时会发生什么。

【问题讨论】:

  • 只需在您的应用程序中包含标头并说链接器使用 B.lib
  • 在 A.dll 中添加一个调用 B.lib 的 foo() 的函数。可能性是无限的
  • 如果 A.dll 包含 foo() 符号,为什么我还需要 B.lib?因为我已经将 A,dll 链接到 B.lib.@teivaz
  • 是的,这可能是一个解决方案,但不是最好的。在我看来,这会降低一些性能导致额外的函数调用。@MichaëlRoy
  • 链接器不知道它需要在最终的可执行文件中包含该函数。而且它不知道它需要被导出。最基本的解决方案是使用 .def 文件。 #pragma comment(linker, "/export:foo") 是一种在源代码中执行此操作的方法。

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


【解决方案1】:

可以从 DLL 导出函数 foo,前提是:-

  • 当您使用 __declspec(dllexport) 属性声明 foo 将函数编译成一个目标文件,比如foo.obj

  • 您将foo.obj 链接到DLL。

foo.obj 如何链接到 DLL 并不重要。

也许您在 DLL 的链接中明确指定 foo.obj

也许你已经把foo.obj 放在一个静态库中,比如foobar.lib,并且链接 DLL 包含对函数foo 的一些引用。然后链接器将 从foobar.lib 中提取foo.obj 并将其链接到DLL 中,以解决 那个参考。

如果 foo.obj 通过从静态库中提取来链接,则链接 与foo.obj 按名称链接完全相同。静态库很简单 一袋目标文件,链接器可以从中选择它需要携带的文件 在联动上。链接完成后,生成的程序或DLL没有依赖关系 在静态库上。如果它需要包中的任何目标文件,它现在已经得到它们。它不需要袋子。

这是一个使用适用于 Windows 的 GCC mingw-w64 工具链的插图,说明如何 DLL 导出的函数是从静态库中链接的:

C:\mingw-w64\x86_64-7.2.0-posix-seh-rt_v5-rev0>echo off
Microsoft Windows [Version 10.0.15063]
(c) 2017 Microsoft Corporation. All rights reserved.

C:\>cd develop\so\scrap
C:\develop\so\scrap>dir
 Volume in drive C has no label.
 Volume Serial Number is 16C7-F955

 Directory of C:\develop\so\scrap

16/12/2017  17:50    <DIR>          .
16/12/2017  17:50    <DIR>          ..
16/12/2017  12:41               116 greeting.cpp
16/12/2017  12:42                99 greeting.h
16/12/2017  12:10               109 hello.cpp
16/12/2017  12:12                90 hello.h
16/12/2017  16:21               197 main.cpp
16/12/2017  12:25               117 niceday.cpp
16/12/2017  12:26                96 niceday.h
               7 File(s)            824 bytes
               2 Dir(s)  101,878,943,744 bytes free

hello.cpp

#include "hello.h"
#include <iostream>

void hello()
{
    std::cout << "Hello world!" << std::endl;
}

hello.h

#ifndef HELLO_H
#define HELLO_H

extern __declspec(dllexport) void hello();

#endif

我们将编译hello.cpp:

C:\develop\so\scrap>g++ -Wall -c -o hello.obj hello.cpp

同样:

niceday.cpp

#include "niceday.h"
#include <iostream>

void niceday()
{
    std::cout << "What a nice day!" << std::endl;
}

niceday.h

#ifndef NICEDAY_H
#define NICEDAY_H

extern __declspec(dllexport) void niceday();

#endif

我们将编译niceday.cpp

C:\develop\so\scrap>g++ -Wall -c -o niceday.obj niceday.cpp

现在我们将这两个目标文件放在一个静态库中。 libgreet.lib

ar rcs libgreet.lib hello.obj niceday.obj

另一个源文件和头文件:

greeting.cpp

#include "greeting.h"
#include "hello.h"
#include "niceday.h"

void greeting()
{
    hello();
    niceday();
}

greeting.h

#ifndef GREETING_H
#define GREETING_H

extern __declspec(dllexport) void greeting();

#endif

我们还将编译:

g++ -Wall -c -o greeting.obj greeting.cpp

现在我们将创建一个 DLL,libgreeting.dll,使用 greeting.obj 和静态 图书馆libgreet.lib:

C:\develop\so\scrap>g++ -shared -o libgreeting.dll greeting.obj libgreet.lib

这里有一个程序源文件:

ma​​in.cpp

#include "hello.h"
#include "niceday.h"
#include "greeting.h"
#include <iostream>

int main()
{
    hello();
    niceday();
    std::cout << "I said..." << std::endl;
    greeting();
    return 0;
}

我们也会编译:

C:\develop\so\scrap>g++ -Wall -c -o  main.obj main.cpp

最后,我们将通过链接main.objlibgreeting.dll 来制作一个程序。 libgreeting.dll

C:\develop\so\scrap>g++ -o prog main.obj libgreeting.dll

运行程序:

C:\develop\so\scrap>prog
Hello world!
What a nice day!
I said...
Hello world!
What a nice day!

所有三个 DLL 导出函数,hellonicedaygreeting, 被称为。 所有重要的,要发生这种情况,就是他们所有人都是 声明 __declspec(dllexport) 并且所有这些都链接到 libgreeting.dll 不知何故。碰巧的是,其中两个(helloniceday)是从静态库链接的,另一个(greeting)是直接从目标文件链接的:这没关系。

如果您有兴趣...

以下是使用 Visual Studio 2017 工具链执行相同操作的方法:

**********************************************************************
** Visual Studio 2017 Developer Command Prompt v15.4.3
** Copyright (c) 2017 Microsoft Corporation
**********************************************************************
[vcvarsall.bat] Environment initialized for: 'x64'

C:\Users\mikek\source>cd C:\develop\so\scrap

C:\develop\so\scrap>dir
 Volume in drive C has no label.
 Volume Serial Number is 16C7-F955

 Directory of C:\develop\so\scrap

16/12/2017  18:31    <DIR>          .
16/12/2017  18:31    <DIR>          ..
16/12/2017  12:41               116 greeting.cpp
16/12/2017  12:42                99 greeting.h
16/12/2017  12:10               109 hello.cpp
16/12/2017  12:12                90 hello.h
16/12/2017  16:21               197 main.cpp
16/12/2017  12:25               117 niceday.cpp
16/12/2017  12:26                96 niceday.h
               7 File(s)            824 bytes
               2 Dir(s)  101,877,473,280 bytes free

编译hello.cpp:

C:\develop\so\scrap>cl /W4 /EHsc /c /Fohello.obj hello.cpp
Microsoft (R) C/C++ Optimizing Compiler Version 19.11.25547 for x64
Copyright (C) Microsoft Corporation.  All rights reserved.

hello.cpp

编译niceday.cpp:

C:\develop\so\scrap>cl /W4 /EHsc /c /Foniceday.obj niceday.cpp
Microsoft (R) C/C++ Optimizing Compiler Version 19.11.25547 for x64
Copyright (C) Microsoft Corporation.  All rights reserved.

niceday.cpp

制作静态库:

C:\develop\so\scrap>lib /out:libgreet.lib hello.obj niceday.obj
Microsoft (R) Library Manager Version 14.11.25547.0
Copyright (C) Microsoft Corporation.  All rights reserved.

编译greeting.cpp:

C:\develop\so\scrap>cl /W4 /EHsc /c /Fogreeting.obj greeting.cpp
Microsoft (R) C/C++ Optimizing Compiler Version 19.11.25547 for x64
Copyright (C) Microsoft Corporation.  All rights reserved.

greeting.cpp

链接libgreeting.dll:

C:\develop\so\scrap>link /dll /out:libgreeting.dll greeting.obj libgreet.lib
Microsoft (R) Incremental Linker Version 14.11.25547.0
Copyright (C) Microsoft Corporation.  All rights reserved.

   Creating library libgreeting.lib and object libgreeting.exp

这里,如您所知,Microsoft 链接器创建了一个导入库 libgreeting.lib (不要与静态库 libgreet.lib 混淆)用于 将 libgreeting.dll 链接到程序或另一个 DLL。

编译main.cpp:

C:\develop\so\scrap>cl /W4 /EHsc /c /Fomain.obj main.cpp
Microsoft (R) C/C++ Optimizing Compiler Version 19.11.25547 for x64
Copyright (C) Microsoft Corporation.  All rights reserved.

main.cpp

链接我们的程序(使用导入库 libgreeting.lib 代替 libgreeting.dll):

C:\develop\so\scrap>link /out:prog.exe main.obj libgreeting.lib
Microsoft (R) Incremental Linker Version 14.11.25547.0
Copyright (C) Microsoft Corporation.  All rights reserved.

然后运行:

C:\develop\so\scrap>prog
Hello world!
What a nice day!
I said...
Hello world!
What a nice day!

稍后

如果 foo 首先是在没有 __declspec(dllexport) 的情况下编译的,并且在我构建 B.lib 时有这样的声明 void foo()。 但是当我构建 C.exe 时,我将 foo 的声明更改为 __declspec(dllexport) void foo()。问题是我还能做到以上吗?

原则上,如果你编译一个在其头文件中带有foo的特定声明的目标文件 然后将该声明 更改#include 中的头文件 编译其他一些目标文件,那么你很可能是在对编译器撒谎 第二次编译,当您将这些目标文件链接到某个程序或 DLL 时,您 可以预料到坏事会发生。

但是,在这种情况下,您可以侥幸逃脱。在上面的示例中,您 可以先编译,比如说,hello.cpp,声明在hello.h

extern void hello(void);

稍后,当你编译greeting.cpp,其中#includes hello.h,你 可以将声明更改为:

extern __declspec(dllexport) void hello(void);

当您链接libgreeting.dll 时,hello 被 DLL_exported。

带有__declspec(dllexport) 的声明改进而不是矛盾 没有的。在链接libgreeting.dll 时,链接器看到一个非 DLL 导出的引用 到hello.obj 中的hellogreeting.obj 中的DLL 导出引用。它 DLL 导出符号,因为它至少看到了一个 DLL 导出的引用。

毫无疑问,这是一个hack

【讨论】:

  • 感谢您的详细回答。还有一个问题,如果 foo 首先是在没有 __declspec(dllexport) 的情况下编译的,并且在我构建 B.lib 时有这样的声明 void foo()。但是当我构建 C.exe 时,我将 foo 的声明更改为 __declspec(dllexport) void foo()。问题是我还能实现上述目标吗?
  • 再次感谢。顺便说一句,我修复了以前的写作错误。查看我的编辑。@Mike Kinghan
猜你喜欢
  • 2020-04-11
  • 1970-01-01
  • 2012-02-07
  • 1970-01-01
  • 2013-06-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多