2

我的一个朋友问我这个问题,我不知道这个函数的含义。也许就像他们上面的注释一样/* sign-extend to 32 bits */。但我想知道这个函数如何实现角色“符号扩展至32位”的细节。

来自 Linux 内核的函数。谢谢大家。

就像@unwind 所说,函数的完整定义是这样的:

/* Convert a prel31 symbol to an absolute address */
#define prel31_to_addr(ptr)                         \
({                                                  \
    /* sign-extend to 32 bits */                    \
    long offset = (((long)*(ptr)) << 1) >> 1;       \
    (unsigned long)(ptr) + offset;                  \
})

它将在函数中使用:

int __init unwind_init(void)
{
    struct unwind_idx *idx;

    /* Convert the symbol addresses to absolute values */
    for (idx = __start_unwind_idx; idx < __stop_unwind_idx; idx++)
        idx->addr = prel31_to_addr(&idx->addr);

    pr_debug("unwind: ARM stack unwinding initialised\n");

    return 0;
}
4

2 回答 2

3

以这里调用它的地方为例

else if ((idx->insn & 0x80000000) == 0)
    /* prel31 to the unwind table */
    ctrl.insn = (unsigned long *)prel31_to_addr(&idx->insn);

因此,我们知道ptr传入的取消引用对未设置最高(第 31 位)位的某个值。如果与prel31名称相关,则表示该值仅使用(低)31 位。

要将有符号的 31 位值转换为有符号的 32 位值,我们需要修复最高位:仍然只有 31个有效位,但负值应该设置最高位。设置最高位以匹配现有 31 位值的符号是符号扩展

通过左移一位,丢弃现有的最高位;当我们再次右移时,最高位将被填充以保留符号(因此如果原始 31 位值为负,则它将为 1,否则为零)。

例如。解释为 31 位值 (-1) 时为负数,但0x7FFFFFFF解释32 位值 (2,147,483,647) 时为正数。要获得与31 位版本含义相同的 32 位编码,我们:

  • 左移以丢弃未使用的最高位:(0x7FFFFFFF << 1 => 0xFFFFFFFE现在是负 32 位值)
  • 再次右移以恢复低 31 位中的原始模式,但根据符号填充最高位0xFFFFFFFE >> 1 => 0xFFFFFFFF = -1

请注意,此(符号扩展)行为是特定于平台的,但所有这些代码也是如此。要理解为什么这样做是有意义的(而不仅仅是符号扩展的含义,以及位模式会发生什么),您需要研究正在使用的寻址方案。

于 2012-09-25T08:49:28.373 回答
2

我认为这就是它的作用:

*ptr包含一个带符号的 31 位值,符号位位于第 30 位(比 MSB 少一个),因此当您将其向左移动时,符号位变为第 31 位(MSB),当您将其移回右侧时,符号位将被“扩展”并显示在第 30 位和第 31 位。

简而言之:它将第 30 位复制到第 31 位。

于 2012-09-25T08:48:55.913 回答