Можно ли экспортировать функцию из статической библиотеки, которая используется в динамической библиотеке?

На 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, который #includes 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-ссылку.

Не сомневайтесь, что это взлом.

Другие вопросы по тегам