【问题标题】:(((long)*(ptr)) << 1) >> 1;(((long)*(ptr)) << 1) >> 1;
【发布时间】:2012-09-25 08:25:29
【问题描述】:

我的一个朋友问我这个问题,我不知道这个函数的含义。也许就像他们上面的注释/* 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;
}

【问题讨论】:

标签: c linux kernel


【解决方案1】:

here 为例:

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,否则为零)。

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

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

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

【讨论】:

  • 感谢您的详细解答!
【解决方案2】:

我认为这就是它的作用:

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-04-11
    • 2016-05-20
    • 2021-05-18
    • 1970-01-01
    • 2020-08-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多