逼特逼开启智慧生活,点亮无限可能

核心内容摘要

甜还是“怪”?探寻“老少配”的魅力密码与“BGMBGM”的独特律动
倾听风吟,触摸星辰:孟若羽《天美》MV,一场视听的华丽蜕变

51吃瓜今日吃瓜-句句刨坟:深度揭秘,娱乐不止!

工程实践需求在开发的工程实践中经常会遇到这些情况在升级了动态库或相关的程序后项目整体就无法启动了。

甚至可能在运行时直接就崩溃了。

而在互联网的应用中更愿意使用热补丁即在不停止整体服务的情况下替换相关的库即不需要项目工程整体进行重新编译。

但很多情况下这种需求的可能性是无法实现的特别是在C的应用中更是如此。

一般来说动态库HELL和上面的说明也有密切的关系。

所以本文提到的问题主要是以动态库为主。

要想实现上述的问题就需要提到C的二进制兼容的问题也就是今天重点分析的技术点。

ABI和APIAPI可能广为开发者熟悉只要真正的做过开发的就一定接触过APIApplication Programming Interface应用程序编程接口。

API是对外的(供开发者调用)API的种类划分方式有很多包括按用途、系统、网络、硬件、范围等等。

其核心的作用是分层和模块化处理功能重用安全和解耦部署、测试等API并不是本文的重点主要是引出ABIApplication Binary Interface二进制应用接口。

ABI是对内的供机器调用的主要用来处理多个二进制程序可以共同启动并运行。

ABI在不同的平台上可能有不同的标准。

比如在常见的X64平台上就有Windows x64 ABIMSVC C ABI和 x

System V ABIItanium C ABI 。

不管是哪种ABI其本质都是为协调在平台上的二进制的兼容性。

它主要包括支持的处理器的指令集即各种CPU的支持机制处理的基础类型、内存大小、内存布局以及对齐方式等各种数据表示机制函数包括虚函数机制的调用机制、传参机制、栈的处理机制以及寄存器的应用方式如返回值的寄存器处理、函数参数的寄存器应用方式等等即函数调用约定机制二进制文件的格式这个非常重要如ELF还是PE即目标文件格式处理机制操作系统或底层库如Runtime Library系统接口调用机制开发者都知道要保证API的稳定性但稳定的API并不一定能保证ABI的稳定和兼容性。

API的升级可能导致ABI的不兼容从而导致程序运行的异常。

API不兼容只会导致代码无法编译而ABI不兼容则可能导致程序的无法运行。

只有深刻的掌握了上述的机制才能够从基础真正理解ABI的关键所在。

而所谓的跨平台开发中ABI兼容是一个非常被容易忽视的问题往往在出现问题后才引起开发者的重视。

ABI兼容的控制在了解了上述的相关ABI兼容的内容后很可能影响ABI接口的开发方式a)类的继承和多态增加或移除虚拟函数增加或移除继承修改继承体系内的类的成员顺序重写多态函数b)类的各种限定、修饰符及数据类型等如移除CV限定符或final、noexcept(当然如果确实无异常没有问题)等修改返回值类型、参数类型、扩展或缩减参数以及权限控制等等c)类成员函数的控制重载函数的增加及内联的控制普通转内联 对导出函数删除或更改函数签名等d)类成员变量控制增加或删除非静态成员变量改变其声明的顺序对静态成员移除或取消导出修改类型及更改CV限定符e)导出类的控制取消或删除导出类f)模板类任何修改模板参数的行为如增、删、重排序等不大可能影响ABI兼容性的开发方式a)类的继承在严格控制下如无多态可对其按照下面的类成员进行修改b)枚举的控制在现有的枚举内增加枚举定义c)类成员函数的控制添加普通函数含构造函数删除非导出函数修改函数参数默认值增加函数的noexcept修改函数名称增减友元函数的声明d)类成员变量控制修改成员变量名称添加新的静态成员变量不影响类的内存布局e)导出类的控制增加新的导出类f)宏控制不会影响到接口本身导出的宏的修改和增加

ABI兼容问题实践下面将针对影响ABI兼容控制相关的编程方法进行举例说明内存布局和内存对齐下面为导出的数据结构体定义//升级前structData{inti;charc;};//升级后structData{inti;charc;doubled;};//再升级后structData{charc;doubled;inti;};增加了导出变量和修改了成员定义顺序后占用空间不同都可能导致ABI的稳定性。

虚函数//升级前structData{virtualvoidgetData(){}};structSubData:public Data{virtualvoidgetData(){}};//升级后structData{virtualvoidsetData(){}virtualvoidgetData(){}};structSubData:public Data{virtualvoidsetData(){}virtualvoidgetData(){}};虚拟函数的修改会导致多态行为的接口改变影响ABI稳定性。

参数和返回值的寄存器处理//升级前structData{inta;intb;};class Demo{public:DatagetData(){}intsetValue(constDatad){}};//升级后structData{inta;intb;doubled;};class Demo{public:DatagetData(){}intsetValue(constDatad){}};这种情况增加了成员影响了返回值的处理。

特别是在传参处理上有些平台允许将16字节大小的结构体拆成两个8字节的部分使用寄存器传参。

编译器处理编译器选项的选择对ABI的影响也不小比如优化级别-O1,-O2等等RTTI控制-fno-rtti字节对齐-fpack-struct[n] 等等。

不同的平台可能相关编译选项也有不同可参看相关的文档工具查看

其它还有不少的细节可以控制和影响ABI兼容性如调用约定、其中有一个解决机制非常不错在前面也分析过0长度数组structData{inta;intb;charbuf[0];};这种方式就可以既兼容ABI的稳定又可以在一定程度上扩展相关的数据。

不过它不是在所有平台通用的需要注意。

常用的ABI兼容方法虽然实现C/C的ABI兼容难度很大但在工程实践中也

总结了几个不错的办法。

主要有使用位域直接操作位对于任何的平台来说基本都是一样的所以它可以最大可能的保持ABI兼容使用Pimpl机制尽最大可能的隔离隐藏。

这样就把ABI的兼容性隔离到了一个很小的范围内使用隔离隐藏机制如上面提到的零长度数组和类分布式的方法如消息、事件等隐藏ABI兼容的细节除了上述的开发上的技术应用外还可以在技术管理上进行控制应用静态库。

这个就不用解释了全包含了哪里还有什么升级、替换的问题。

不过静态库也有其缺点要慎重严格控制动态库的版本管理 防止DLL HELL的问题

六、

总结其实ABI兼容问题对于C/C这种中低级语言来说是一个大问题。

但对于更现代的高级语言ABI兼容已经被隔离在了开发之外像C#和Java等它们很多都运行在各自的虚拟环境上很多ABI兼容的细节问题自然已经被屏蔽掉。

有些问题会随着技术的演进而自然被解决掉。

这也是那句名言要用发展的眼光看问题。

妈妈叫我戴上避孕套的歌词是什么-妈妈叫我戴上避孕套的歌词是什么应用

百度百家号客服电话人工服务

123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123