我试图从汇编程序代码(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
之前将double转换为整数,则没有分段错误(在情况6中取消注释行并在下面注释该行)并且它得出正确的结果(5) 。在调试过程中,我发现在printf
函数的子例程中发生了分段错误,我无法看到代码。即使我定义了一个随机双,我也无法打印它。
此时任何帮助都将不胜感激,谢谢。
答案 0 :(得分:2)
代码片段的行为取决于指向double
和字符串的指针如何编码为long
值i
以及其他考虑因素。它非常不便携。
您为long val = i >> 3;
的负值计算具有实现定义行为的i
,并在以后转换为{{1时生成具有0
或1
高位的指针值}或(double*)
。虽然在常见架构中忽略了指针表示中的一些高阶位,但依赖此行为是不安全的,并且在32位intel PC上会失败。最好确保指针在8字节边界上对齐(至少),然后屏蔽低位,或者在转换为(char*)
或(double*)
之前调整值。
如果(char*)
小于uintptr_t
,使用long
代替long
也会更加轻松,就像在64位Windows系统上一样。