【问题标题】:Why doesn't a linked binary file's _size symbol work correctly?为什么链接的二进制文件大小符号不能正常工作?
【发布时间】:2019-02-23 18:22:15
【问题描述】:

我使用 'ld -r -b binary -o binary.o foo.jpeg' 在我的程序中嵌入资源。效果很好。我只是想知道为什么 int _binary_size 符号永远不会正确读取,负数或太大的数字,但在程序运行之间保持不变。 我总是要做_binary_end - _binary_start,它完美无缺。它似乎对任何人都不起作用...like here .... 为什么会这样?

没有理由不使用 end-start,因为它取代了大小符号,但它仍然让我感到好奇。

编辑:代码示例。

extern const unsigned char _binary_scna4_jpg_start;
extern const unsigned char _binary_scna4_jpg_end;
extern const int _binary_scna4_jpg_size;

int size = &_binary_scna4_jpg_end - &_binary_scna4_jpg_start;
printf("Size is %d vs %d \n", size, _binary_scna4_jpg_size);

打印出来:

Size is 1192071 vs -385906356 

第一个数字是二进制文件的正确大小,我的所有图像都可以完美读取。

用于良好测量的 nm 输出:

0000000000123087 D _binary_scna4_jpg_end
0000000000123087 A _binary_scna4_jpg_size
0000000000000000 D _binary_scna4_jpg_start

【问题讨论】:

  • 你能展示你尝试使用_binary_size的代码吗?
  • @n.m.编辑问题以包含代码示例

标签: c gcc ld


【解决方案1】:

问题是由Position-Independent Executables (PIE) 引起的。早期的可执行文件被加载到相同的内存地址(在编译/链接时确定),这导致了可能的攻击,因为攻击者知道程序的特定部分在哪个地址。因此实现了Address Space Layout Randomization。这样做的副作用是,大小符号被定义为绝对地址(_binary_scna4_jpg_size 不是整数值,它是一个“指针”,就像 _start 和 _end 一样)在加载时也会重新定位.

如果您使用选项-no-pie 编译您的代码,您可以禁用位置无关,_binary_scna4_jpg_size 将输出正确的值,因为它不会被重新定位。由于这些天默认情况下 PIE 处于启用状态,因此指针的值基本上是垃圾。如果您知道重定位内存的开头,也可以使用它,但由于您已经拥有 _binary_scna4_jpg_start_binary_scna4_jpg_end,因此使用它们是一样的。

【讨论】:

  • 我认为 ASLR 与此无关(尝试禁用它,看看会发生什么)。
【解决方案2】:

您的_binary_scna4_jpg_size 符号不是整数。这是一个绝对地址符号。为了获得大小,您需要获取它的 地址 并转换为适当的整数类型:

printf("The real size is %td\n", (ptrdiff_t) &_binary_scna4_jpg_size);

但这仅在禁用 PIE (gcc -fPIC -no-pie) 或静态链接 (gcc -static) 时有效。

【讨论】:

  • 它有效,但我不明白。似乎 C 语义与这个特殊的外部符号的工作方式非常不同:& 这里的运算符不告诉内存中变量的地址,而是值本身。 _binary_<filename>_size 应该有哪个声明?它可以是size_t,它可以是int,它可以是size_t*void*...一切都可以。那么,哪种声明更合适呢?如果您可以通过更多解释来改进答案,我将非常高兴。
  • @ceztko foo.jpeg 不是 C 文件,无论 ld 使它与 C 无关。_binary_scna4_jpg_size 不是保存大小的变量。它是ld放置在地址处的符号,代表大小。该地址没有变量,只有符号。
  • 我只是指_size 符号。我在考虑 & 运算符仅在变量上工作的假设,并且返回的值始终是有效地址。在这种情况下,返回值是一个大小,根本不是有效地址。没关系,这可能只是一种适用于此类符号的奇怪语义。如果您建议我应该声明哪种类型的符号,我会很高兴,因为它不是变量,而且大多数类型对我来说没有多大意义(我不能只声明它void)。
  • @ceztko _size 符号 不是 就 C 而言的变量。它没有在任何 C 源文件中定义。它只是声明extern。它是由不是 C 代码的东西定义的。 C 语言没有指定在这种情况下应该发生什么。当然,就 C 程序而言,这不是一个有效的地址,因为它不是任何对象的地址。您可以随意声明它,只要它是对象类型,类型就无关紧要(所以没有void)。没关系,因为您没有访问您声明的(不存在的)对象,您只是获取它的地址。
  • & 获取对象的地址。它不采用所述对象的值。假设数据的大小是 100。链接器在地址 100 处创建一个_size 符号。该地址没有对象也没有值,但链接器欺骗编译器相信存在。因此,编译器创建了一条指令来获取地址 100 处不存在的对象的地址。如果您想将其视为链接器放置一个魔术值,编译器可以使用魔术& 访问该魔术值,您可以,但实际上,这只是一个普通符号,就像其他任何符号一样,也是一个普通的&
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-09-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多