On win32, I built a dynamic library called A.dll which linked against a static library called B.lib, and also built a executable called C.exe which only dependent on A.dll.
But now, in C.exe if I want use a function foo which only has definition in B.lib, I have to link C.exe against B.lib again.
The question is can I export foo from B.lib directly into A.dll when building A.dll, and how?
Also I want to know what will it be when dealing with GCC.
A function foo
can be exported from a DLL provided that:-
You declare foo
with the __declspec(dllexport)
attribute when you
compile the function into an object file, say foo.obj
You link foo.obj
into the DLL.
It doesn't matter how foo.obj
gets linked into the DLL.
Maybe you specify foo.obj
explicitly in the linkage of the DLL.
Maybe you've put foo.obj
is inside a static library, say foobar.lib
, and the linkage
of the DLL contains some reference to the function foo
. Then the linker will
extract foo.obj
from the foobar.lib
and link it into the DLL, to resolve
that reference.
If foo.obj
is linked by being extracted from a static library, the linkage
is exactly the same as if foo.obj
was linked by name. A static library is simply
a bag of object files from which the linker can pick the ones it needs carry
on the linkage. When the linkage is done, the program or DLL produced has no dependency
on a static library. If it needed any object files in the bag, it has got them now. It doesn't need the bag.
Here's a illustration using the GCC mingw-w64 toolchain for Windows of how a DLL-exported function is linked from within a static library:
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
We'll compile hello.cpp
:
C:\develop\so\scrap>g++ -Wall -c -o hello.obj hello.cpp
Similarly:
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
we'll compile niceday.cpp
C:\develop\so\scrap>g++ -Wall -c -o niceday.obj niceday.cpp
Now we'll put these two object files in a static library. libgreet.lib
ar rcs libgreet.lib hello.obj niceday.obj
Another source file and header:
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
Which we'll also compile:
g++ -Wall -c -o greeting.obj greeting.cpp
Now we'll make a DLL, libgreeting.dll
, using greeting.obj
and the static
library libgreet.lib
:
C:\develop\so\scrap>g++ -shared -o libgreeting.dll greeting.obj libgreet.lib
Here we have a program source file:
main.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;
}
which we'll also compile:
C:\develop\so\scrap>g++ -Wall -c -o main.obj main.cpp
Finally, we'll make a program by linking our main.obj
with libgreeting.dll
.
Only with libgreeting.dll
.
C:\develop\so\scrap>g++ -o prog main.obj libgreeting.dll
Run the program:
C:\develop\so\scrap>prog
Hello world!
What a nice day!
I said...
Hello world!
What a nice day!
All three of the DLL-exported functions, hello
, niceday
and greeting
,
are called. All that matters, for this to happen, is that all of them were
declared __declspec(dllexport)
and all of them were linked into libgreeting.dll
somehow. As it happens, two of them (hello
, niceday
) were linked from a static library and the other (greeting
) was linked directly from an object file: this doesn't matter.
And if you're interested...
Here's how you do the same thing with the Visual Studio 2017 toolchain:
**********************************************************************
** 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
Compile 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
Compile 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
Make the static library:
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.
Compile 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
Link 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
Here, as you know, the Microsoft linker creates an import library libgreeting.lib
(not to be confused with the static library libgreet.lib
) which is used for
linking libgreeting.dll
to a program or another DLL.
Compile 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
Link our program (using the import library libgreeting.lib
in lieu of
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.
And run:
C:\develop\so\scrap>prog
Hello world!
What a nice day!
I said...
Hello world!
What a nice day!
Later
If
foo
firstly was compiled without__declspec(dllexport)
and has a declaration like thisvoid foo()
when I built B.lib. But when I built C.exe I changed foo's declaration to__declspec(dllexport) void foo()
. The question is can I still make above happen?
In principle, if you compile an object file with a certain declaration of foo
in its header file
and then change that declaration subsequently to #include
the header file in the
compilation of some other object file, then you are likely to be lying to the compiler in the
second compilation and when you link these object files into some program or DLL you
can expect bad things to happen.
In this case however, you can get away with it. In the example above, you
may first compile, say, hello.cpp
with the declaration in hello.h
:
extern void hello(void);
Later, when you compile greeting.cpp
, which #include
s hello.h
, you
may change the declaration to:
extern __declspec(dllexport) void hello(void);
with the result that hello
will be DLL_exported when you link libgreeting.dll
.
The declaration with __declspec(dllexport)
refines rather than contradicts the
the one without. In linking libgreeting.dll
, the linker sees a non-DLL-exported reference
to hello
in hello.obj
and a DLL-exported reference in greeting.obj
. It
DLL-exports the symbol because it has seen at least one DLL-exported reference.
Be in no doubt that this is a hack.