Можно ли экспортировать функцию из статической библиотеки, которая используется в динамической библиотеке?
На win32 я построил динамическую библиотеку A.dll, которая использовала статическую библиотеку B.lib, а также создал исполняемый файл, который использовал только A.dll - C.exe.
И вот, я хочу использовать функцию B.libfoo в C.exe, поэтому я должен экспортировать foo из B.lib.
Тогда возникает вопрос: можно ли это сделать и как, без перекомпиляции B.lib в динамическую библиотеку из исходного кода.
И я также хочу знать, что я могу сделать в соответствующем сценарии (совместно используемая статическая библиотека) при использовании GCC.
1 ответ
Функция foo
может быть экспортирован из DLL при условии, что:
Вы заявляете
foo
с__declspec(dllexport)
атрибут, когда вы компилируете функцию в объектный файл, скажем,foo.obj
Вы ссылку
foo.obj
в DLL.
Не важно как foo.obj
становится связанным в DLL.
Может быть, вы указываете foo.obj
явно в связи с DLL.
Может быть, вы положили foo.obj
скажем, внутри статической библиотеки, скажем, foobar.lib
и связь DLL содержит некоторую ссылку на функцию foo
, Тогда компоновщик будет извлекать foo.obj
от foobar.lib
и связать его в DLL, чтобы разрешить эту ссылку.
Если foo.obj
связан с извлечением из статической библиотеки, связь точно такая же, как если бы foo.obj
был связан по имени. Статическая библиотека - это просто пакет объектных файлов, из которых компоновщик может выбрать те, которые ему нужны, для переноса связей. Когда связь установлена, созданная программа или DLL не зависят от статической библиотеки. Если ему понадобились какие-либо объектные файлы в сумке, он получил их сейчас. Ему не нужна сумка.
Вот пример использования инструментария GCC mingw-w64 для Windows того, как экспортированная из 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
Здесь у нас есть исходный файл программы:
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;
}
который мы также скомпилируем:
C:\develop\so\scrap>g++ -Wall -c -o main.obj main.cpp
Наконец, мы сделаем программу, связав нашу main.obj
с libgreeting.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-экспортируемых функций, hello
, niceday
а также greeting
, называются. Все, что имеет значение для того, чтобы это произошло, это то, что все они были объявлены __declspec(dllexport)
и все они были связаны в libgreeting.dll
как угодно. Как это бывает, два из них (hello
, niceday
) были связаны из статической библиотеки и другой (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)
и имеет такую декларациюvoid foo()
когда я построил B.lib. Но когда я собрал C.exe, я изменил объявление foo на__declspec(dllexport) void foo()
, Вопрос в том, могу ли я все-таки сделать выше, чтобы это произошло?
В принципе, если вы компилируете объектный файл с определенным объявлением foo
в его заголовочном файле, а затем измените это объявление впоследствии #include
заголовочный файл в компиляции какого-либо другого объектного файла, тогда вы, вероятно, будете лгать компилятору во второй компиляции, и когда вы связываете эти объектные файлы в какую-то программу или DLL, вы можете ожидать, что произойдут плохие вещи.
В этом случае, однако, вы можете сойти с рук. В приведенном выше примере вы можете сначала скомпилировать, скажем, hello.cpp
с декларацией в hello.h
:
extern void hello(void);
Позже, когда вы компилируете greeting.cpp
, который #include
s hello.h
, вы можете изменить объявление на:
extern __declspec(dllexport) void hello(void);
в результате чего hello
будет DLL_exported при ссылке libgreeting.dll
,
Декларация с __declspec(dllexport)
уточняет, а не противоречит тому, что без. В связи libgreeting.dll
, компоновщик видит не экспортированную в DLL ссылку на hello
в hello.obj
и экспортированная в DLL ссылка в greeting.obj
, Это DLL-экспорт символа, потому что он видел по крайней мере одну экспортированную DLL-ссылку.
Не сомневайтесь, что это взлом.