正在转换结构数据的字节序
Converting endianness of struct-Data
我拥有的是:
- 一个十六进制文件,其中包含c指令的字节,以big-endian排序
- 结构定义为*.h文件
- 结构信息为侏儒2调试信息
- 我的应用程序必须用C/C++编写。使用例如python的中间脚本就可以了
我要做的是读取十六进制文件的字节,并将其转换为系统上的结构类型,该系统是小端序。在这个过程中,我必须反转每个结构成员的字节。
显而易见的解决方案是编写一个转换函数,为每个结构成员进行字节交换,但由于结构有多个层和大约1200个成员,它们的变化速度比我更新转换函数的速度快,因此手动编写转换函数不是解决方案。
因此,我可以通过以下方式自动生成转换函数:
- 查找和解析多个*.h文件中的类型
- 迭代所有结构类型的成员并为它们生成交换->而不需要某种反射api——这并不容易)
- 通过转换函数加载结构
由于这个解决方案似乎需要做很多工作,我想知道是否有更简单的方法,比如告诉编译器交换它或以某种方式使用调试信息。
有人知道在这种情况下可能有用的技巧吗?
谢谢和问候!
备注:无法更改导致此情况的任何流程/更改输入条件或将责任委托给其他相关开发人员。
- 将十六进制文件的某些内容更改为输入是不可能的。此文件来自其他系统,这些系统不会在此处更改以解决此问题
- 填充、数据类型大小等是相同的。其他措施也确保了这一点。因此,endianes是唯一的防御问题。这也是为什么我认为没有理由反对使用侏儒2信息来识别每个结构成员的字节。
- 我同意结构的布局非常糟糕。但它之所以会这样,有一些原因,简而言之,由于流程原因和向后兼容性,我无论如何都可以/不允许不更改
要提供更多范围:
所有这些所使用的软件都部署到多个不同的嵌入式设备(多种类型)。十六进制文件包含软件的校准信息,因此存储在只能输出该十六进制文件的特定系统中。我现在正在将软件移植到一个小的endian设备上,我必须使用软件的"主"分支(big-endian)提供的十六进制文件作为输入。
没有办法告诉C或C++编译器自动将字节从LE交换到BE,反之亦然。你真的必须自己做。如果您的数据结构非常庞大,那么最好的方法可能是实现自动转换代码生成。
据我所知,这个问题很棘手,但很容易处理。据我所知,数据提取不会在嵌入式设备上运行,因此不会受到资源限制。我说——接受桌面硬件允许的运行时低效率,转而选择易于调试的方式。
与其将源文件视为">几乎我需要模化几个小调整的东西",不如将其视为"具有开放端、不断发展的模式的通用二进制文件"。模式描述是DWARF数据。
我要做的是:启动一个Python项目。使用pyelftools
PyPI模块解析DWARF。滚动查找编译单位(CU)。在每个CU中,滚动浏览顶级条目(DIE)。查找具有特定值DW_AT_name
的DW_TAG_structure_type
DIE(我希望结构名称提前知道)。然后通过DW_TAG_member
子DIE。DW_AT_data_member_location
将为您提供偏移量,让您围绕填充进行操作。查看DW_AT_type
以检测成员类型(为此,您必须解析DIE引用)。根据需要递归到结构类型和数组类型的成员中。
由此,为struct.unpack方法生成一个格式字符串——它可以无缝地读取big-endian-int。然后使用struct.pack
将其格式化为C++使用者期望的任何格式。
这取决于您是否能够将数据文件跟踪到生成可执行文件的DWARF信息,完全相同的构建。我希望该组织的程序允许这样做。
GCC的最新版本允许为使用杂注scalar_storage_order
的源代码段或使用具有相同标识符的属性的特定类型声明所需的endianness,而不考虑目标平台。主要捕获:g++不支持此操作。此外,这并不是在所有情况下都有效。例如,将指针指向具有透明endianness转换的成员会导致错误。除非您同意使用C进行结构访问(这完全取决于您当前的代码库),否则这不是一个选项。
持久性布局是基于原始结构布局的,那就这样吧。然而,应该首选更明确的序列化结构的方法,这正是你提到这一点的原因。除了endianness问题外,结构打包还影响兼容性,应该显式指定。对于持久性,1
的封装将是最佳的。对于内存中的数据结构,就性能和并发特性而言,这种对齐远非最佳。此外,不同的平台可能具有不兼容的数据类型(例如,64位Linux/Windows上的sizeof(long)
-LP64与LLP64)。因此,保持持久性布局与内存中的数据结构分离往往有一长串优点,因此通常会超过必须单独维护序列化代码的缺点。特别是,如果可移植性是一个主要问题。
您可以利用基于C/C++的反射库,也可以自己实现一个。在C的情况下,这肯定需要宏(例如Metaresc)。在C++的情况下,您实际上可能会放弃原来的结构定义(例如Boost.Precisive和Flat Reflection)。
如果反射不是一个选项,则可以通过解析标头或调试符号来生成序列化代码。一般来说,解析C/C++比较复杂。通过将所涉及的结构移动到专用的头中,您可以使用一个简单的C/C++解析器。为了让事情变得更容易,您可以通过基于调试符号处理ptype
的gdb输出来简化解析。或者,您可以直接解析调试符号。对于像Python这样的脚本语言,这两种方法都应该是可行的(想到pygccxml
和pyelftools
)。
与其坚持将生成序列化代码作为构建过程的一部分,您可以一次性生成该代码,并在未来结构发生变化时需要更新。这就是我在多平台场景中要做的。这样做也可以省去实现一个可以处理各种C/C++输入的完美解析器的痛苦,它只需要足够好就可以一次性生成。
- 如何使用CAPL的诊断功能获取CAN传输的数据(256字节)?
- 如何在 XML 中正确存储原始字节数据并恢复它?
- 正在转换结构数据的字节序
- 一种将 Dart 中的字节数据转换为 C++ 中的无符号字符*的有效方法?
- 我能确定从文件中读取的 32 字节二进制数据等于 256 位吗?
- 将constexpr字节数组与缓冲区的一部分(指向数据的指针)进行比较
- 什么是 16 字节有符号整数数据类型?
- 交换未定义数据类型中的字节顺序
- 读取C++的原始字节数据
- 准备要传递给 jni 的位图字节 [] 数据
- 根据C++代码在 PHP 中创建字节数据并将其传递给套接字
- 生成不同格式的图像字节数据
- C++中是否有"字节"数据类型?
- 如何在C++中附加字节数据
- 将1字节数据转换为4字节时会发生什么详细情况
- 添加字节*数据到双精度向量
- 如何在c++中读取以空格分隔的十六进制字节数据
- 无法从输入流读取字节数据
- c++ String of Bytes转换为字节数据类型并计算字节数
- 将包含 uint 和 floats 的 4 字节数据转换为 float