【问题标题】:Least significant bits in function pointer函数指针中的最低有效位
【发布时间】:2014-04-07 22:47:21
【问题描述】:

我正在玩一种编程语言实现,我想知道(不)建议它如何将函数指针的最低有效位压入服务以存储数据。

是否有任何主要平台(AMD64/{Windows/Linux/MacOS}、Arm/{iOS、Android})在函数指针中的 2 个最低有效位一直非零?也就是主要平台上代码的对齐方式至少是4吗?

【问题讨论】:

  • 这是非常糟糕的做法,实际上不需要。为什么需要这个?
  • @MichaelWalz,标记它是函数指针,还是指向闭包结构的指针。也许您可以创建一个答案来解释为什么这是不好的做法,请记住我正在实现 PL 编译器/运行时。
  • "Arm/{iOS,Android})"拇指功能?
  • 既然你在实现它,你就不能make对齐吗?
  • @harold,我无法控制工具链的某些部分。例如,链接器。在这种情况下,我也无法控制特定于目标的代码生成器。

标签: c pointers assembly compiler-construction cpu


【解决方案1】:

我可以告诉您,Apple 的 64 位运行时(我认为是 ARM64 和 Intel)广泛使用最低有效位作为您建议的标志。在 Objective-C 中,一切都是对象,为了与 C 兼容,几乎每个对象都存在于堆上并由其指针记录。在 64 位模式下,它们允许非常小的对象存在于堆栈中,方法是将它们装入 62 位并使用低两位表示这不是真正的指针而是文字对象。因此,您可以将短字符串、对象包装的 32 位及以下数字等直接放入“指针”中,而不会将任何内容放在堆上。

但是,Apple 不会在 32 位运行时执行此操作(事件是 iOS 上的“现代”运行时)。所以可能值得研究为什么会这样。诚然,这可能只是因为 PowerPC 继承了一些架构上的怪癖。

正如在 cmets 中向我指出的那样(以及为什么现在将其标记为社区 wiki),C 标准区分了 function 指针的存储和所有其他类型的指针。所以上面的评论可能是相关的,也可能是不相关的——不过我相信这是因为闭包又是一个独立于数据和函数的东西,在编译语言中,代码本身通常是预先编译好的,而闭包本身只是数据填补空白。但我要说明的一点是,有一些可靠的系统,假设它们可以在需要对齐的系统上重用指针的最低有效位。

【讨论】:

  • 请注意,OP 正在谈论 function 指针,AFAIK 是完全不同的野兽。
  • 但这表明现有操作系统确实“滥用”指针,虽然这种滥用目前可能不会用于函数指针,但这表明假设它们将来不会是一个坏主意。
  • 是的,但是Objective-C是C的严格超集,所以指针就是指针就是指针,包括函数指针。类型化的指针是为了程序员的利益。一些项目,如 QEMU,通过将其他代码位串在一起来动态地利用这一事实来编译代码(尽管有一些特定于编译器的假设来确定函数在哪里结束并假设他们知道函数如何开始)。
  • @Tommy “指针就是指针就是指针,包括函数指针”——在谈论 C 时,这是错误的。函数指针在几个方面有所不同:它们被隐式构造和取消引用(f == &ffp(x) == (*fp)(x)),它们没有大小(sizeof(fp) 是非法的),它们不能保证适合 @ 987654324@ 或void *。这些完全有可能是 C 标准的特质,并且在这些平台上没有区别,但这是一个值得指出的事实。
  • @delnan 函数没有大小,但 AFAIK 函数指针肯定有。你有这方面的资料吗?
【解决方案2】:

没有一个主要的现代平台不需要其指令至少 4 字节对齐,而且我不知道有任何 C 编译器将低字节用于其自身目的。 Blah blah blah 关于在 C 中对强制转换的指针进行操作的未定义行为,但你是安全的。

编辑:正如下面所指出的,对于 ARM Thumb,您只能获得一位,并且您需要确保在进行跳转之前清除它。对于 i386,某些链接器在禁用优化时不会进行对齐。

【讨论】:

  • “没有主要的现代平台不需要其指令至少 4 字节对齐” - 在嵌入式世界中工作(每年数十亿处理器)肯定不是“至少 4-字节对齐”。
  • 这不是重点。我正在为 Sneftel 的开场白提供一个反例。有人可以说英特尔几乎不是现代的:)
  • PowerPC VLE 和 ARM Thumb 仅 2 字节对齐。 ARM 甚至使用函数指针的最低位来区分 Thumb 模式和传统模式。
  • 出于性能原因,即使是 i386 链接器也会将函数对齐到 4 字节边界。不过,关于拇指的好点。
  • 只有在使用“-Os”开关时,GNU C 才会将函数与字节边界对齐。我只是尝试了一个小程序;有地址奇数的函数!
【解决方案3】:

ARM 有两种模式 - legacy(也称为“ARM”)和 Thumb。在 ARM 模式下,指令在 4 字节边界上对齐,在 Thumb 中 - 在 2 字节上。 CPU 将第零位用于切换模式的调用:要从 ARM 转到 Thumb,您需要向最右边的位设置为 1 的地址发出分支和切换模式命令。

在两个最流行的基于 ARM 的平台(iOS 和 Android)上,本地用户空间代码的首选模式恰好是 Thumb。然而,必须支持与 ARM 的互通。所以地址中实际上没有未使用的位。

【讨论】:

  • 更糟糕的是:最低位是用来区分两种模式的!这意味着:这两个位都被使用了!
  • 任一模式下的 B/BL 命令都会忽略最低位。只有 BX/BLX 使用它来进行模式切换。所以是和不是 :) 在 Thumb 中,第二低位是地址的一部分。
【解决方案4】:

在 ARM 上,低位有一个特殊的含义:它在 Thumb 模式和传统模式之间切换。在 Thumb 模式下,指令是 16 位对齐的,因此两个位都被使用。

在 AMD64 和 x86 上,取决于优化模式,函数可能位于奇数地址。这意味着低两位总是在使用中。

【讨论】:

    猜你喜欢
    • 2015-05-09
    • 1970-01-01
    • 1970-01-01
    • 2020-09-07
    • 2014-05-20
    • 1970-01-01
    • 2011-09-13
    • 2010-10-19
    • 1970-01-01
    相关资源
    最近更新 更多