在某些 linux 系統上,這有效。我通常可以設計基于插件的應用程式,這樣就沒有庫,只有頭檔案和可執行檔案嗎?
Afaik 如果介面類是介面,因為它們只包含純虛函式,那么這總是有效的。但是我是否也可以在包含符號的介面中定義類,這些符號必須通過鏈接到包含它們的可執行檔案來系結?
用例:一個可執行的 foo,應用程式,通過共享庫 libfoo 為插件提供一個介面。插件(共享庫)在運行時加載。應用程式和插件都鏈接到 libfoo 以決議它們都使用的類中的符號。這是必要的還是可以將類放在可執行目標中并讓插件鏈接可執行檔案?
uj5u.com熱心網友回復:
我最熟悉 Windows,對您提出的嚴格問題的答案是否定的,但您仍然可以做您想做的事。
在 Windows 上,EXE 和 DLL 檔案都使用相同的檔案格式,“便攜式可執行檔案”。但它們仍然有非常重要的區別:
- 重定位表最常從 EXE 檔案中丟失
- 入口點不同
由于這些差異,嘗試通過LoadLibrary()將失敗加載本機 EXE 檔案(LoadLibraryEx(LOAD_LIBRARY_AS_DATAFILE)很好,可以與 Windows 資源 API 一起使用)。(.NET 程式集 EXE 檔案是一個例外——它們不包含任何實際代碼,僅包含中間語言,并且代碼地址始終是動態確定的,因此不需要重定位修復)
但是,您的方案“讓插件鏈接可執行檔案”仍然有效,因為 EXE 檔案確實支持匯出表,并且加載程式可以將 DLL 的匯入系結到 EXE 的匯出。
不幸的是,C ABI 在 Windows 上沒有標準化,因此匯出 C 類非常脆弱,并導致鎖定到特定的編譯器。為了保持松耦合,您需要匯出普通的 C 函式或 COM 介面。
使用介面可以避免整個問題——您可以在實作頭檔案中描述的介面的可執行檔案中定義一個類,將介面指標傳遞給插件,插件可以保存該指標用于所有回呼到可執行檔案,而無需任何匯入條目。例如,前身COM,“物件鏈接和嵌入”,定義IObjectWithSite和IOleClientSite介面。
uj5u.com熱心網友回復:
我最熟悉 Linux(或其他基于 ELF 的系統)。
如果您使用 PIE 可執行檔案并使用 構建--export-symbols,則可以跳過(例如)libfoo.so
可執行檔案將加載插件并向插件提供任何 API 所需的符號
換句話說,插件就不會需要知道如何foo執行得到API符號。它們可以在不參考任何庫的情況下進行鏈接。
下面是一個完整的測驗用例給你......
請注意,我將 API 呼叫直接系結到可執行檔案中。但是,可執行檔案可以從共享的libfoo.so[如果需要]加載 API 呼叫。但是,插件對這個庫一無所知。
請注意,您可以添加/減少一些選項。從###在Makefile,我是做了一些相當大的黑客攻擊,直到我偶然發現,得到成功的組合。
檔案:生成檔案
# pieplugin/Makefile -- make file for pieplugin
#
# SO: can we use an executable file as shared library on all platformswindows
# SO: mac l
# SITE: stackoverflow.com
# SO: 70370572
XFILE = foo
XOBJ = foo.o
PLUGINS = libplug1.so
PLUGINS = libplug2.so
CFLAGS = -Wall -Werror -I.
###CFLAGS = -g
PIEFLAGS = -fpie
PIEFLAGS = -fPIC
###PIEFLAGS = -fpic
PICFLAGS = -fPIC
###PICFLAGS = -fpic
PICFLAGS = -nostdlib
PICFLAGS = -nodefaultlibs
PLUG_CFLAGS = $(CFLAGS)
PLUG_CFLAGS = $(PICFLAGS)
PLUG_LFLAGS = -shared
###PLUG_LFLAGS = $(PICFLAGS)
###PLUG_LFLAGS = -no-pie
CC = gcc
###CC = clang
LDSO = $(CC)
LDSO = ld
XFILE_LFLAGS = -Wl,--export-dynamic
all: $(PLUGINS) $(XFILE)
foo.o: foo.c
$(CC) $(CFLAGS) $(XFILE_CFLAGS) -c foo.c
$(XFILE): foo.o
$(CC) -o $(XFILE) $(XFILE_LFLAGS) foo.o -ldl
file $(XFILE)
plug1.o: plug1.c
$(CC) $(PLUG_CFLAGS) -c plug1.c
libplug1.so: plug1.o
$(LDSO) $(PLUG_LFLAGS) -o libplug1.so plug1.o
file libplug1.so
plug2.o: plug2.c
$(CC) $(PLUG_CFLAGS) -c plug2.c
libplug2.so: plug2.o
$(LDSO) $(PLUG_LFLAGS) -o libplug2.so plug2.o
file libplug2.so
test:
./$(XFILE) $(PLUGINS)
xtest: clean all test
clean:
rm -f $(XFILE) $(PLUGINS) *.o
檔案:foo.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <dlfcn.h>
#include <foopriv.h>
#define PLUGSYM(_fnc) \
plug->plug_##_fnc = plugsym(plug,"plugin_" #_fnc)
// plugsym -- load symbol from plugin file
void *
plugsym(plugin_t *plug,const char *sym)
{
void *fnc = dlsym(plug->plug_so,sym);
int sverr = errno;
printf("plugsym: loading %s from %s at %p\n",
sym,plug->plug_file,fnc);
if (fnc == NULL) {
printf("plugsym: failed -- %s\n",strerror(sverr));
exit(1);
}
return fnc;
}
// plugload -- load plugin file
void
plugload(const char *tail)
{
char file[1000];
plugin_t *plug = calloc(1,sizeof(*plug));
strcpy(plug->plug_file,tail);
sprintf(file,"./%s",tail);
printf("plugload: dlopen of %s ...\n",file);
//plug->plug_so = dlopen(file,RTLD_LOCAL);
//plug->plug_so = dlopen(file,RTLD_GLOBAL);
plug->plug_so = dlopen(file,RTLD_LAZY);
int sverr = errno;
printf("plugload: plug_so=%p\n",plug->plug_so);
#if 1
if (plug->plug_so == NULL) {
printf("plugload: failed -- %s\n",strerror(sverr));
exit(1);
}
#endif
PLUGSYM(fncint);
PLUGSYM(fncflt);
plug->plug_next = plugin_list;
plugin_list = plug;
}
int
main(int argc,char **argv)
{
--argc;
argv;
// NOTE: in production code, maybe we use opendir/readdir to find plugins
for (; argc > 0; --argc, argv)
plugload(*argv);
for (plugin_t *plug = plugin_list; plug != NULL; plug = plug->plug_next) {
printf("main: calling plugin %s fncint ...\n",plug->plug_file);
plug->plug_fncint(NULL);
}
for (plugin_t *plug = plugin_list; plug != NULL; plug = plug->plug_next) {
printf("main: calling plugin %s fncint ...\n",plug->plug_file);
plug->plug_fncflt(NULL);
}
return 0;
}
// functions provided by foo executable to plugins ...
void
foo_fncint(fooint_t *ptr,const char *who)
{
printf("foo_fncint: called from %s ...\n",who);
}
void
foo_fncflt(fooflt_t *ptr,const char *who)
{
printf("foo_fncflt: called from %s ...\n",who);
}
檔案:plug1.c
// plug1.c -- a plugin
#include <foopub.h>
void ctors
initme(void)
{
}
void
plugin_fncint(fooint_t *ptr)
{
foo_fncint(ptr,"plug1_fncint");
}
void
plugin_fncflt(fooflt_t *ptr)
{
foo_fncflt(ptr,"plug1_fncflt");
}
檔案:plug2.c
// plug2.c -- a plugin
#include <foopub.h>
void ctors
initme(void)
{
}
void
plugin_fncint(fooint_t *ptr)
{
foo_fncint(ptr,"plug2_fncint");
}
void
plugin_fncflt(fooflt_t *ptr)
{
foo_fncflt(ptr,"plug2_fncflt");
}
這是輸出make xtest:
rm -f foo libplug1.so libplug2.so *.o
gcc -Wall -Werror -I. -fPIC -nostdlib -nodefaultlibs -c plug1.c
ld -shared -o libplug1.so plug1.o
file libplug1.so
libplug1.so: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, not stripped
gcc -Wall -Werror -I. -fPIC -nostdlib -nodefaultlibs -c plug2.c
ld -shared -o libplug2.so plug2.o
file libplug2.so
libplug2.so: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, not stripped
gcc -Wall -Werror -I. -c foo.c
gcc -o foo -Wl,--export-dynamic foo.o -ldl
file foo
foo: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=d136c53c818056fdbec75294ea472ab8c056ca52, not stripped
./foo libplug1.so libplug2.so
plugload: dlopen of ./libplug1.so ...
plugload: plug_so=0x7fd320
plugsym: loading plugin_fncint from libplug1.so at 0x7fad7f93e037
plugsym: loading plugin_fncflt from libplug1.so at 0x7fad7f93e059
plugload: dlopen of ./libplug2.so ...
plugload: plug_so=0x7fd940
plugsym: loading plugin_fncint from libplug2.so at 0x7fad7f939037
plugsym: loading plugin_fncflt from libplug2.so at 0x7fad7f939059
main: calling plugin libplug2.so fncint ...
foo_fncint: called from plug2_fncint ...
main: calling plugin libplug1.so fncint ...
foo_fncint: called from plug1_fncint ...
main: calling plugin libplug2.so fncint ...
foo_fncflt: called from plug2_fncflt ...
main: calling plugin libplug1.so fncint ...
foo_fncflt: called from plug1_fncflt ...
uj5u.com熱心網友回復:
當然,對于某些檔案格式。
Mono、.NET Core 和 .NET 5.0 程式集將在 Windows、Linux 和 Mac 上運行。
Java .class 和 .jar 檔案也是如此。
還有其他人。
轉載請註明出處,本文鏈接:https://www.uj5u.com/gongcheng/386441.html
上一篇:c#-如何為每個插入字典定義行為?就像每次插入都寫入日志
下一篇:對sizeof的無效寫入
