MSVC/GCC/NDK:将二进制文件生成obj文件

本文介绍两种将二进制文件嵌入程序的方法:通过工具将文件转换为C代码或使用GNU objcopy/ld生成obj文件。适用于不同平台,包括NDK及MSVC,并提供了cmake集成方案。

如何将二进制文件作为数据添加到自己程序中?这是我最近遇到的问题,google上找到这两篇说得已经很清楚:

《Embedding of binary data into programs》
《Embedding Blobs in Binaries》

一种方法是将想二进制文件生成c代码,与项目一起编译,这方法对于小数据没问题,但如果二进制文件太大,生成的c代码更是巨大,增加编译器负担,好处就是完全没有跨平台问题。网上可以找到相关的工具(搜索 bin2cbin2h),比如这个embed-resource

第二种方式就是用GNU的objcopy工具来实现。
objcopy本身就提供了二进制数据转成obj文件的能力
执行如下指令即可将 file.bin 生成 elf32-i386 格式的obj文件 file.o

objcopy -I binary -O elf32-i386 -B i386 file.bin file.o

另外GNU的ld也可以实现,

ld -r -b binary -o file.o file.bin

objcopy不同,ld不需要指定输出格式,默认即为当前平台的obj格式,比如Windows平台下的MinGW ld则默认为pe-x86-64,NDK 的ld默认为elf32-littlearm
可以通过执行 ld --print-output-format 显示ld默认的输出格式

NDK

在NDK(NDK 19c)下需要找到正确的ld或objcopy才行,
之前我使用 $android-ndk-r19c/toolchains/llvm/prebuilt\windows-x86_64/bin/x86_64-linux-android-objcopy$android-ndk-r19c/toolchains/llvm/prebuilt\windows-x86_64/bin/llvm-objcopy都不能生成结果。
使用$android-ndk-r19c/toolchains/arm-linux-androideabi-4.9/prebuilt/windows-x86_64/arm-linux-androideabi/bin/objcopy$android-ndk-r19c/toolchains/llvm/prebuilt/windows-x86_64/arm-linux-androideabi/bin/objcopy执行正常

如下执行NDK 的objcopy生成 elf32-littlearm格式的obj

:: 将ndk objcopy位置添加到搜索路径
$ SET PATH=${android-ndk-r19c}/toolchains/arm-linux-androideabi-4.9/prebuilt/windows-x86_64/arm-linux-androideabi/bin;%PATH%

$ objcopy -I binary -O elf32-littlearm -B arm fall.jpg fall_elf32_littlearm.o

或如下使用ld来生成obj文件

ld -r -b binary -o fall_elf32_littlearm.o fall.jpg 

执行objdump查看

$ objdump -t -f fall_elf32_littlearm.o

fall_elf32_littlearm.o:     file format elf32-littlearm
architecture: arm, flags 0x00000010:
HAS_SYMS
start address 0x00000000

SYMBOL TABLE:
00000000 l    d  .data  00000000 .data
00000000 g       .data  00000000 _binary_fall_jpg_start
000085b9 g       .data  00000000 _binary_fall_jpg_end
000085b9 g       *ABS*  00000000 _binary_fall_jpg_size

MSVC

msvc并没有提供类型GNU objcopy的命令行工具可以直接将二进制文件转为obj文件.怎么办?有两个解决方案:

  • 如这篇文章中所说《Embedding of binary data into programs》,在网上搜索 bin2coffbin2obj可以找到第三方的工具.比如这个github上的bin2coff(我没测试)。
  • 如果你已经安装了MinGW编译器,使用MinGW提供的objcopyld就可以生成MS COFF格式的obj文件了

如下生成MSVC所需的PE格式obj文件

objcopy -I binary -O pe-x86-64 -B i386 fall.jpg fall_pe-x86-64.obj

或如下使用ld来生成obj文件

ld -r -b binary -o fall_pe-x86-64.obj fall.jpg 

执行objdump查看生成的fall_pe-x86-64.obj

>objdump -t fall_pe-x86-64.obj

fall_pe-x86-64.obj:     file format pe-x86-64

SYMBOL TABLE:
[  0](sec  1)(fl 0x00)(ty   0)(scl   2) (nx 0) 0x0000000000000000 _binary_fall_jpg_start
[  1](sec  1)(fl 0x00)(ty   0)(scl   2) (nx 0) 0x00000000000085b9 _binary_fall_jpg_end
[  2](sec -1)(fl 0x00)(ty   0)(scl   2) (nx 0) 0x00000000000085b9 _binary_fall_jpg_size

也可执行MSVC的dumpbin查看生成的obj文件,可以确认生成的是MSVC格式obj

>dumpbin /symbols /headers fall_pe-x86-64.obj
Microsoft (R) COFF/PE Dumper Version 14.00.24215.1
Copyright (C) Microsoft Corporation.  All rights reserved.


Dump of file fall_pe-x86-64.obj

File Type: COFF OBJECT

FILE HEADER VALUES
            8664 machine (x64)
               1 number of sections
               0 time date stamp
            85F8 file pointer to symbol table
               3 number of symbols
               0 size of optional header
               5 characteristics
                   Relocations stripped
                   Line numbers stripped

SECTION HEADER #1
   .data name
       0 physical address
       0 virtual address
    85B9 size of raw data
      3C file pointer to raw data (0000003C to 000085F4)
       0 file pointer to relocation table
       0 file pointer to line numbers
       0 number of relocations
       0 number of line numbers
C0100040 flags
         Initialized Data
         1 byte align
         Read Write

COFF SYMBOL TABLE
000 00000000 SECT1  notype       External     | _binary_fall_jpg_start
001 000085B9 SECT1  notype       External     | _binary_fall_jpg_end
002 000085B9 ABS    notype       External     | _binary_fall_jpg_size

String Table Size = 0x46 bytes

  Summary

        85B9 .data

CMAKE

习惯于使用cmake进行项目集成编译的人会想如何将生成obj的过程集成到cmake脚本中自动化执行。关于这个问题可以参考这篇文章 《Embedding binary resources with CMake and C++11》
我就是参考它来实现的

参考资料
《Embedding of binary data into programs》
《Embedding Blobs in Binaries》
《objcopy》
《llvm-objcopy - object copying and editing tool》

<think>我们使用qmake的时候,可以通过预定义的变量和配置来检测系统架构、构建模式(Debug/Release)以及编译器类型(MinGW/MSVC/GCC)。以下是如何进行这些判别: ### 1. 判别系统架构(32位/64位) 在qmake中,我们可以通过`QMAKE_TARGET.arch`或检查`QMAKE_HOST.arch`来获取目标或宿主机的架构信息。同时,qmake也提供了一些内置变量用于检测指针大小(这反映了是32位还是64位程序)。 ```qmake # 检测目标架构(通常指编译出的程序运行的架构) contains(QMAKE_TARGET.arch, x86_64) { message("目标架构: 64位 (x86_64)") } else: contains(QMAKE_TARGET.arch, i386) { message("目标架构: 32位 (i386)") } else: contains(QMAKE_TARGET.arch, arm64) { message("目标架构: 64位 (ARM64)") } else: contains(QMAKE_TARGET.arch, arm) { message("目标架构: 32位 (ARM)") } else { message("未知目标架构") } # 或者通过指针大小检测(更直接) message("指针大小(字节): $$QMAKE_PREFIX_SHLIB_SIZE") # 对于动态库前缀,但通常用于指针大小? # 另一种方法是使用配置变量 contains(QT_ARCH, i386) { message("目标架构: 32位 (QT_ARCH=i386)") } else: contains(QT_ARCH, x86_64) { message("目标架构: 64位 (QT_ARCH=x86_64)") } else: contains(QT_ARCH, arm) { message("目标架构: 32位ARM (QT_ARCH=arm)") } else: contains(QT_ARCH, arm64) { message("目标架构: 64位ARM (QT_ARCH=arm64)") } ``` 注意:`QT_ARCH`是Qt5引入的变量,表示目标架构。而`QMAKE_HOST.arch`表示宿主机的架构。 ### 2. 判别Debug/Release模式 在qmake中,可以通过`CONFIG`变量检查是否包含`debug`或`release`选项。 ```qmake # 检测构建模式 contains(CONFIG, debug) { message("构建模式: Debug") } else: contains(CONFIG, release) { message("构建模式: Release") } else { message("构建模式: 未知") } ``` 另外,有时也会区分`debug_and_release`配置,表示同时构建debug和release版本,但最终构建时通过`CONFIG`指定具体模式。 ### 3. 判别编译器类型(MinGW/MSVC/GCC) qmake通过`QMAKE_SPEC`和`CONFIG`变量来反映当前使用的编译器。 - **MSVC**:检查`CONFIG`中是否包含`msvc`。 - **MinGW**:检查`CONFIG`中是否包含`g++`,同时平台是Windows(通过`win32`检测)。 - **GCC**:在非Windows平台上,`g++`通常表示GCC编译器。 ```qmake # 检测编译器类型 win32 { contains(CONFIG, msvc) { message("编译器: Microsoft Visual C++ (MSVC)") } else: contains(CONFIG, g++) { message("编译器: MinGW") } else { message("编译器: 未知 (Windows)") } } else: unix { contains(CONFIG, g++) { message("编译器: GCC") } else: contains(CONFIG, clang) { message("编译器: Clang") } else { message("编译器: 未知 (Unix-like)") } } ``` 此外,也可以使用`QMAKE_CXX`变量来查看C++编译器的路径或名称,从而判断编译器类型。 ```qmake # 通过编译器可执行文件名称判断 !isEmpty(QMAKE_CXX) { # 获取编译器名称(不带路径) compiler_name = $$basename(QMAKE_CXX) contains(compiler_name, g\\+\\+) { message("编译器: GCC系列 (g++)") } else: contains(compiler_name, cl) { message("编译器: MSVC (cl.exe)") } else: contains(compiler_name, clang\\+\\+) { message("编译器: Clang (clang++)") } } ``` ### 完整示例 以下是一个完整的示例,在.pro文件中添加这些检测: ```qmake # 输出构建信息 message("--- 开始检测构建环境 ---") # 1. 检测目标架构 contains(QT_ARCH, i386) { message("目标架构: 32位 (i386)") } else: contains(QT_ARCH, x86_64) { message("目标架构: 64位 (x86_64)") } else: contains(QT_ARCH, arm) { message("目标架构: 32位ARM") } else: contains(QT_ARCH, arm64) { message("目标架构: 64位ARM") } else { message("目标架构: 未知 (QT_ARCH=$${QT_ARCH})") } # 2. 检测构建模式 contains(CONFIG, debug) { message("构建模式: Debug") } else: contains(CONFIG, release) { message("构建模式: Release") } else { message("构建模式: 未知 (CONFIG=$${CONFIG})") } # 3. 检测编译器 win32 { contains(CONFIG, msvc) { message("编译器: MSVC") } else: contains(CONFIG, g++) { message("编译器: MinGW") } else { message("编译器: 未知 (Windows)") } } else: unix { contains(CONFIG, g++) { message("编译器: GCC") } else: contains(CONFIG, clang) { message("编译器: Clang") } else { message("编译器: 未知 (Unix-like)") } } # 额外:通过编译器命令判断 !isEmpty(QMAKE_CXX) { compiler_name = $$basename(QMAKE_CXX) contains(compiler_name, g\\+\\+) { message("编译器可执行文件: g++ ($${QMAKE_CXX})") } else: contains(compiler_name, cl) { message("编译器可执行文件: MSVC cl ($${QMAKE_CXX})") } else: contains(compiler_name, clang\\+\\+) { message("编译器可执行文件: clang++ ($${QMAKE_CXX})") } else { message("编译器可执行文件: $${QMAKE_CXX}") } } message("--- 检测完成 ---") ``` ### 注意事项 - 这些检测通常在.pro文件被解析时执行,因此输出信息会在执行`qmake`命令时显示。 - 在Qt Creator中,这些消息会出现在“General Messages”面板中。 - 条件判断中,`win32`和`unix`是qmake的内置平台检测变量,分别表示Windows平台和类Unix平台(包括Linux和macOS)。 ### 引用说明 在qmake中,通过内置变量和`CONFIG`变量实现环境检测,具体可参考Qt官方文档关于qmake的章节[^1]。 §§相关问题§§ 1. 如何在qmake中根据不同的编译器设置不同的编译选项? 2. 如何在qmake中为不同的架构(32位/64位)链接不同的库? 3. 如何在qmake中同时支持Debug和Release模式,并设置不同的输出目录? </think>### 使用 qmake 判别系统架构、构建模式和编译器类型 在 qmake 项目配置中,可以通过内置变量和条件判断实现系统架构、构建模式(Debug/Release)和编译器类型(MinGW/MSVC/GCC)的检测。以下是具体方法: #### 1. 判别系统架构(32位/64位) ```qmake # 检查目标架构 contains(QMAKE_TARGET.arch, x86_64) { message("目标架构: 64位 (x86_64)") } else: contains(QMAKE_TARGET.arch, i386) { message("目标架构: 32位 (i386)") } # 检查主机架构 (开发机) contains(QMAKE_HOST.arch, x86_64) { message("主机架构: 64位") } else: contains(QMAKE_HOST.arch, i386) { message("主机架构: 32位") } ``` #### 2. 判别构建模式(Debug/Release) ```qmake # 检测构建模式 contains(CONFIG, debug) { message("构建模式: Debug") DEFINES += DEBUG_MODE } else: contains(CONFIG, release) { message("构建模式: Release") DEFINES += RELEASE_MODE } ``` #### 3. 判别编译器类型 ```qmake # MSVC 编译器检测 contains(CONFIG, msvc) { message("编译器: Microsoft Visual C++ (MSVC)") QMAKE_CXXFLAGS += /std:c++latest } # MinGW 编译器检测 win32:contains(CONFIG, g++) { message("编译器: MinGW") QMAKE_CXXFLAGS += -std=c++17 } # GCC 编译器检测 (Linux/macOS) unix:!macx:contains(CONFIG, g++) { message("编译器: GCC") QMAKE_CXXFLAGS += -std=c++17 } # Clang 编译器检测 contains(CONFIG, clang) { message("编译器: Clang") QMAKE_CXXFLAGS += -std=c++17 } ``` ### 完整配置示例 ```qmake # 架构检测 contains(QMAKE_TARGET.arch, x86_64) { ARCH_TYPE = x64 message("目标架构: 64位") } else { ARCH_TYPE = x86 message("目标架构: 32位") } # 构建模式检测 contains(CONFIG, debug) { BUILD_TYPE = debug message("构建模式: Debug") DEFINES += DEBUG_BUILD } else { BUILD_TYPE = release message("构建模式: Release") } # 编译器检测 contains(CONFIG, msvc) { COMPILER = msvc message("编译器: MSVC") } else: win32:contains(CONFIG, g++) { COMPILER = mingw message("编译器: MinGW") } else: contains(CONFIG, g++) { COMPILER = gcc message("编译器: GCC") } # 组合配置示例 message("当前配置: $$ARCH_TYPE-$$BUILD_TYPE-$$COMPILER") ``` ### 关键变量说明 | **检测类型** | **qmake 变量** | **典型值** | |------------|------------------------|-------------------------| | 目标架构 | `QMAKE_TARGET.arch` | `x86_64`, `i386`, `arm` | | 主机架构 | `QMAKE_HOST.arch` | `x86_64`, `i386` | | 构建模式 | `CONFIG` | `debug`, `release` | | 编译器类型 | `CONFIG` | `msvc`, `g++` | | 操作系统 | 内置作用域 | `win32`, `unix`, `macx` | ### 验证方法 1. 在 `.pro` 文件中添加 `message()` 输出语句 2. 运行 `qmake` 查看输出信息 3. 在 Qt Creator 的 "General Messages" 面板查看标志输出: ``` Project MESSAGE: 目标架构: 64位 Project MESSAGE: 构建模式: Debug Project MESSAGE: 编译器: MSVC ``` ### 注意事项 1. `win32` 作用域适用于所有 Windows 平台(MSVC 和 MinGW) 2. `CONFIG` 变量可同时包含多个值(如 `debug_and_release`) 3. 使用 `$$` 语法引用自定义变量值(如 `$$ARCH_TYPE`) 4. 架构检测优先使用 `QMAKE_TARGET.arch`,它反映目标平台而非构建平台 通过组合这些检测方法,可实现跨平台的差异化配置[^1]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

10km

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值