ELF如何填补它的“短路”?

时间:2012-09-05 22:08:12

标签: c assembly x86 inline-assembly

我知道函数参数被填充到目标字大小,但是用什么?

特别是在x86 Linux GNU工具链的上下文中,这些函数返回了什么?

int iMysteryMeat(short x)
{
    return *((int *)&x);
}
unsigned uMysteryMeat(unsigned short x)
{
    return *((unsigned *)&x);
}

问题在于,当在汇编中对函数进行手动编码时,是否有必要通过屏蔽或对其进行符号扩展来消除“小”参数,然后才能在“大”上下文中使用它们andl {{ 1}})。

我也对这种情况是否有更多通用或跨平台标准感兴趣。

3 个答案:

答案 0 :(得分:2)

这取决于ABI。 ABI需要指定调用者或被调用者(以及如何)扩展小参数的选择。不幸的是,ABI的这一部分经常被指定,导致不同编译器的不同选择。因此,为了防止使用不同的遗留编译器编译的代码之间的不兼容性,大多数现代编译器(我特别知道gcc上的i386)在谨慎方面是错误的,并且两者兼而有之。

int a(short x) {
  return x;
}
int b(int x);
int c(short x) {
  b(x);
}

gcc -m32 -O3 -S tmp.c -o tmp.s

_a:
pushl   %ebp
movl    %esp, %ebp
movswl  8(%ebp),%eax
leave
ret

_c:
pushl   %ebp
movl    %esp, %ebp
movswl  8(%ebp),%eax
movl    %eax, 8(%ebp)
leave
jmp _b

请注意,a不会对其参数采用任何扩展规则,而是对其进行扩展。类似地,c确保在将其传递给b之前扩展其参数(通过尾调用)。

答案 1 :(得分:0)

int iMysteryMeat(short x)
{
    return *((int *)&x);
}

这是C中未定义的行为,这违反了别名规则,也可能违反了对齐要求。在short中不要这样做。

答案 2 :(得分:0)

虽然基思的回答与我的问题精神一致,但根据亚历克斯的要求,我以为我会为自己试一试。

有趣的是,在这种情况下,我的例子的更直接的答案是"垃圾"。

#include <stdio.h>

int iMysteryMeat(short x)
{
    return *((int *)&x);
}
unsigned uMysteryMeat(unsigned short x)
{
    return *((unsigned *)&x);
}
int main()
{
    printf("iMeat: 0x%08x\n", iMysteryMeat(-23));
    printf("uMeat: 0x%08x\n", uMysteryMeat(-23));
    return 0;
}

gcc -m32 -S meat.c

iMysteryMeat:
    pushl   %ebp
    movl    %esp, %ebp
    subl    $4, %esp
    movl    8(%ebp), %eax
    movw    %ax, -4(%ebp)
    leal    -4(%ebp), %eax
    movl    (%eax), %eax
    leave
    ret
uMysteryMeat:
    pushl   %ebp
    movl    %esp, %ebp
    subl    $4, %esp
    movl    8(%ebp), %eax
    movw    %ax, -4(%ebp)
    leal    -4(%ebp), %eax
    movl    (%eax), %eax
    leave
    ret

./a.out
iMeat: 0x0804ffe9
uMeat: 0x0043ffe9

正如您所看到的,不仅通常的符号扩展协议被覆盖(即与Keith&#39; s a()比较),它实际上将x移动到未初始化的堆栈空间movw,渲染无论main()给出什么,返回值垃圾的上半部分。

所以,再次,正如ouah所说,永远不会在C中进行此操作,在汇编中(或者一般来说,真的如此),始终对您的输入进行消毒