d导出的讨论
原文
以下是导出的所有问题的列表:32位和64位问题:
1)导出全局变量会产生链接器错误
2)导出线本变量应该是错误(至少在C++中是这样)(.1)
3)应在模块有导出符号后立即导出模块信息.
4)不导出类的__gshared成员
5),未导出TestClass的TypeInfo对象
6),未导出TestStruct的TypeInfo对象
请见随附复制案例.如果此重现案例实际编译并运行dll.应可支持共享运行时.
也应该导出导出符号引用的如ModuleInfos,TypeInfos,opEquals等编译器生成符号.
.1不,至少ELF支持链接/访问另一个共享库的TLS值.
对TLS访问,窗口还使用每个DLL的索引,因此应该可链接/重定位它们.
问题似乎是导出使用类似C的规则来区分声明和定义.对声明,导出表明__declspec(dllimport),对定义,导出表明__declspec(dllexport).
export void foo(); // 当作导入
export void foo() {} // 当作导出
要导出全局变量,需要初化器.
export __gshared int bar; //当作导入
export __gshared int bar = 0; //当作导出
该规则没有意义,因为它是定义变量并默认初化.
要标记仅声明,应用外(extern).
此外,此行为总是需要包含导入模块声明的头文件.
1)导出全局变量会使链接器错误,由#10059漏洞修复.
2问题,不能链接到包含线本变量定义的DLL吗?
3问题,符号,对我,很有意义.
如果导出用户定义类型,则也应导出所有编译器生成的元数据,vtables,TypeInfo,RTInfo!Type等.
如果导出UDT,并不肯定导出是否表明导出所有成员.
要在druntime/phobos中添加导出注解,需要修复922错误.
变量?抱歉,不确定是否可使线本变量跨dll边界工作,所以应先让它成为错误,以后再允许它.
一些随机评论:
0,对静态和动态链接,想用相同代码时,不可用"导出".建议根据编译开关按模块粒度导出每个公共符号.
1,但是,不能简单判断是从静态库还是动态库导入模块中的符号.但该区别是必要的,因为两种情况下,代码不同.也许模块级的版本指示可控制它.
2,可用其他DLL中的TLS变量,但当前工具链可能不行.需要导出的是TLS节中符号偏移,及导出符号DLL中_tls_index变量地址.然后如下读取变量:
mov EAX,[_imp_variable.tls_index];
//读导入表中`tls_index`的地址
mov EAX,[EAX]; //读`DLL`的`tls_index`
mov EBX,FS:[2C]; // tls_array
mov EBX,[EBX+4*EAX]; // `dll`的tls_start
mov ECX,[_imp_variable.offset];
//读`DLL`的`TLS`中变量的偏移
mov EDX,[EBX+ECX];//读变量
如果不可导出偏移,则DLL也需要_tls_start.
对每个只返回变量值地址的TLS变量,实现getter函数,不是更容易吗?
你是对的,函数会更简单.由于间接跳转,效率可能稍低,但避免了通过导入表的两次间接访问数据.
没有混淆保护和导出.如,可拆分库为两个DLL,此时,可能需要导出/导入,私和包保护符号.
通过静态库,访问链接变量时,编译器会生成直接访问.但是,如果通过动态库链接,则编译器会通过导入表通过直接引用_imp_符号而不是原始符号来,生成另一级间接.也即,编译器必须知道符号是从动态库还是静态库中导入.
访问数据时,添加了些额外重定位数据来实现类似"自动导入".启动程序时,修复重定位,导入表地址.
可惜,这不适合64位应用,因为dmd生成的二进制文件中的重定位仅与32位PC相关.不能在另一个DLL,传地址给变量,因为它可能在32位到不了的64位地址空间.
仅指的是"公共符号"?对我一切正常,但因为禁止从另一个模块访问,对私有符号,不重要.
也许我对窗口机制理解错了,但导出符号可以相同.类似ELF的PLT,在导入库中绑定运行时.引用导入符号的代码,最后,静态链接到导入库.
对函数符号,是的,这是正确的,但如前所述,要特殊处理数据符号.
好吧,问题是,是否可用"导出"来注解符号,并仍然创建静态库.
优化很好,但Walter表示他更喜欢经典(另一级间接)方式.总结:
0,导出何时表示DLL导入,何时表示DLL导出.讨论提出,可用命令行开关,对每个模块(包括子模块)启用dllimport/dllexport.
1,是否想要导出所有公开符号(C++正摆脱它,也许也应这样)
2,是否可导出整个类/构吗?如按导出标记类,导出其vtable,类型信息,所有保护/公开函数,静态成员等.
3,需要导出模块哪些信息?一旦模块,有单个导出的函数/类/变量,就会自动导出吗?
目前:没有标记.但是,应该创建方法,除非用编译器的命令行开关指定,"导出"应是无操作.
当前实现有些问题:
export void foo() {} // 声明=> dllexport
export void foo(); // 定义=> dllimport
export int a = 0; // 定义=> dllexport
export int a; // 声明=> dllimport
//因为它实际上是个定义,失败
export extern int a; // 声明=> dllimport
如果能避免额外的.di标头,那就太好了.
在Unix上进入另一个方向.
此处有关推导更多信息.链接,
是的,因为无法注解编译器生成数据.
再次请避免滥用导出保护,因为会对未来语言扩展带来太多问题如:维护ABI兼容的私有符号,多个文件/对象中的模块定义,部分类.链接和符号保护是根本不同的概念,应该提供正交控制.
是的,它是隐藏的编译器数据,可能需要链接到ModuleInfo及其他符号.
在既创建,导出符号,又从另一个DLL导入符号的DLL中,这不管用.别名导出.
总结别名建议.
0,对每个导出的函数定义,还产生_imp_funcname别名符号.
1,对每个导出的数据定义,产生弱链接的T*_imp_var=&var只读指针.
2,每当调用或访问导出符号时,使用_imp_*符号完成操作.
3,此类代码与导入库链接时,同DLL正确工作.
4,代码与静态库链接时,引用正确的定义.
5,简单导出属性,可满足所有用例,不用担心dllimport/dllexport/无操作.
6,如果可用整个程序优化,则链接器可在静态链接时,优化额外间接寻址的访问数据.
最后一点不重要,因为导出数据很少,且不好.
此外,这仅适合不应成为性能热点的API边界.
对ELF导出,只需使符号可见,否则默认应隐藏符号.
Mach-O的想法?和ELF一样?
你想用"导出"来注解所有火卫一和druntime来构建共享版本吗?已在注解地狱中,有nothrow,纯,@safe等
是的,这就是意图.手动这样很重要,因为导出符号,表明致力于ABI稳定性,这应该是长期目标.
由于导出符号是库的已知入口,有了是否更多推导非导出符号问题.
dmd很可能不能用微软链接器的LTO.
此外,这不仅会影响API的边界,还会影响访问全局数据:__gshared,共享变量和所有编译器内置全局数据符号,如模块信息,类型信息,vtables等.
这是因为,在不知道从DLL导入哪些符号时,就必须对这些符号添加间接寻址.如果决定用别名,必须对全局数据访问加上额外间接级.
完全同意.为了方便,要提供-exportall开关.
它会起作用.因为编译器命令行开关会指定模块名,所以不会转换所有导出为dllexport.如,如果构建火卫一,会在命令行加上"-export std".这会把std模块中导出转换为dllexport.
因此,对同时导出和导入符号的DLL非常有效.对dllimport,需要指定链接到共享的phobos库时,命令行有的等效的,如"-import std"开关.然后可添加到默认的sc.ini,这样用户就不必手动指定.
不!仅适合标记为导出的数据.所以很清楚可导入什么.
导出的UDT(vtable,rtti)的元数据和带导出成员模块的模块信息.注意,由于窗口不支持插入符号,因此直接从模块内访问数据是安全的.
拥有源码,但不想改时,编译器开关有意义.
提供-export=public,package,private可能有意义,因为没人维护ABI.
不是vtables,因为它们已通过类实例间接寻址.
浙公网安备 33010602011771号