【发布时间】:2017-08-08 09:01:03
【问题描述】:
我正在尝试从汇编代码 (Linux x86-64) 调用 C 函数。在汇编代码中,类型由 3 LSB 编码。 C 函数的目的只是简单地打印键入的值。这是函数:
int print(long i) {
int type = i & 7;
long val = i >> 3;
double c;
switch(type) {
case 0:
printf("%d\n", val);
break;
case 1:
if (!val)
printf("false\n");
else if (val == 1)
printf("true\n");
else if (val == 2)
printf("nil\n");
break;
case 2:
printf("%s\n", (char*)(val + 8));
break;
case 6:
c = *(double*) val;
//printf("%d\n", (int)c); /* no seg fault */
//printf("%f\n", 1.0); /* seg fault */
printf("%f\n", c); /* seg fault */
break;
}
return 1;
}
到目前为止,我一直在使用字符串指针没有任何问题,所以我知道这不是内存问题,但这是我第一次使用%xmm 寄存器,所以我可能在那里做错了。以下是相关的调用片段:
movsd _DB1(%rip), %xmm4
movsd _DB2(%rip), %xmm5
addsd %xmm5, %xmm4
movsd %xmm4, (%r12)
leaq 6(, %r12, 8), %rdi
addq $8, %r12
call print
##### ... #####
.data
_DB1:
.double 2
_DB2:
.double 3
%r12 指向堆中分配的内存。最奇怪的是,如果我在调用 printf 之前将双精度数转换为整数,则不会出现分段错误(取消注释案例 6 中的行并注释下面的行)并且它会得出正确的结果 (5)。在调试过程中,我发现在看不到代码的printf 函数的子程序中出现了分段错误。此外,即使我定义了一个随机双精度,我也无法打印它。
在这一点上,任何帮助将不胜感激,谢谢。
【问题讨论】:
-
在案例 0 和案例 1 中,您将
val作为常规变量处理,例如:printf("%d\n", val);但在案例 6 中作为指针:c = *(double*) val;我认为您的意思是:c = val;。此外,这段代码充满了幻数。您应该考虑为您的类型使用枚举。 -
由于我将内存中双精度的位置传递给 C 函数,我需要将其转换为指针,如果我这样做
c = val我不会得到双精度跨度> -
即使演员表是
*(double *)&val,它也会有未定义的行为。不要这样做。 -
@Hadron:好吧,如果没有解释,提供的代码是看不到的。如果您传递地址,您会通过以下方式破坏它:
long val = i >> 3;或者您之前是否对它做过一些我们在这里没有看到的事情? -
@Andre Kampling 我在这里加载地址
leaq 6(, %r12, 8), %rdi,你可以看到我将它向左移动 3,然后添加 6,当我在参数中得到它时,我可以将它移回而不会丢失一部分