【问题标题】:How to compile C programs such that binaries differ only in different return value?如何编译 C 程序,使二进制文件仅在不同的返回值上有所不同?
【发布时间】:2025-12-24 04:05:11
【问题描述】:

如果您编译两个仅在返回值上有所不同的 C 程序,我希望二进制文件仅在此值的位上有所不同。但是,如果我使用 GCC 编译以下程序,转储二进制位(使用 xxd)并区分转储,我会得到另一个不同。

文件

return127.c

int main() {
    return 127;
}

return128.c

int main() {
    return 128;
}

编译、转储和差异

# compile
gcc -Os -fdata-sections -ffunction-sections -fipa-pta -Wl,--gc-sections -Wl,-O1 -Wl,--as-needed -Wl,--strip-all return127.c -o return127
gcc -Os -fdata-sections -ffunction-sections -fipa-pta -Wl,--gc-sections -Wl,-O1 -Wl,--as-needed -Wl,--strip-all return128.c -o return128
# dump
xxd -b return127 > return127.xxd-bits
xxd -b return128 > return128.xxd-bits
# diff
diff return127.xxd-bits return128.xxd-bits

注意:我使用this 的编译命令注释一个关于C 程序最小二进制的问题。

差异

108,111c108,111
< 00000282: 01010101 00000000 01101011 11011010 11101100 11100011  U.k...
< 00000288: 00111010 10001111 00101111 00101100 01100001 00111100  :./,a<
< 0000028e: 10010010 11001011 00011000 11101010 11100111 00100011  .....#
< 00000294: 01001010 00111011 11111001 11111010 00000001 00000000  J;....
---
> 00000282: 01010101 00000000 00011101 11000011 10101000 00011001  U.....
> 00000288: 11011011 00110001 10100000 01001101 01000110 10010011  .1.MF.
> 0000028e: 00101101 01011101 11101001 00001000 01010101 11111101  -]..U.
> 00000294: 11011011 01000011 11010100 10101011 00000001 00000000  .C....
211c211
< 000004ec: 00000000 00000000 00000000 00000000 10111000 01111111  ......
---
> 000004ec: 00000000 00000000 00000000 00000000 10111000 10000000  ......

有两个不同之处。底部的差异显示了返回值的(预期)差异。这些行仅在最后一个字节/块中有所不同。二进制 01111111 是十进制 127。二进制10000000 是十进制128

顶部有什么区别?

【问题讨论】:

  • 如果您从相同的源代码构建两次,二进制文件是否相同?我的猜测是构建的日期和时间可能存储在可执行文件的标头中的某个位置。您正在创建什么类型的二进制文件? ELF?如果前 4 个字节的值为 7F 45 4C 46,则它是一个 ELF 文件。
  • 是的,如果我构建两次它们是相同的(即使我在构建之间等待几分钟)。这是一个 ELF 文件。
  • google.com/search?q=gcc+reproducible+builds | -Wl,-O1 为什么将 O1 传递给链接器,而将 Os 传递给 gcc?
  • 如果您从名为 return127.c 的文件中执行 return 128 会怎样?
  • @SteveSummit 我得到了相同的二进制文件。

标签: c gcc compiler-optimization


【解决方案1】:

顶部有什么区别?

这是构建 ID 的差异。安装diffoscope(或比较两个库的readelf --wide --notes 输出),你会很好地看到:

│  Displaying notes found in: .note.gnu.build-id
│    Owner                Data size     Description
│ -  GNU                  0x00000014    NT_GNU_BUILD_ID (unique build ID bitstring)     Build ID: 817d41c45a09c3822337307250bdb9410a1959b4
│ +  GNU                  0x00000014    NT_GNU_BUILD_ID (unique build ID bitstring)     Build ID: de5fb81907549af3332e8136d6bd7ab4d884e0ce

如何编译 C 程序,使二进制文件仅在不同的返回值上有所不同?

  1. 您必须在两个 gcc 上将 __TIME____DATE__ 设置为相同的时间。
  2. 您必须为这两个调用创建唯一的 build-id。

以下脚本:

export SOURCE_DATE_EPOCH=$(date +%s)
f() {
    gcc -Wl,--build-id=none \
       -Os -fdata-sections -ffunction-sections -fipa-pta \
       -Wl,--gc-sections -Wl,--as-needed -Wl,--strip-all \
       -xc - -o "$1"
}
echo 'main(){return 127;}' | f /tmp/1
echo 'main(){return 128;}' | f /tmp/2
diffoscope /tmp/1 /tmp/2

diffoscope 输出:

│  0000000000001020 <.text>:
│ - mov    $0x7f,%eax
│ + mov    $0x80,%eax
│   retq   

【讨论】:

    最近更新 更多