【问题标题】:Unable to printf floating point numbers from executable shared library无法从可执行共享库中打印浮点数
【发布时间】:2012-08-08 09:22:45
【问题描述】:

我正在开发一个共享库,可以独立执行以打印它自己的版本号。

我已将自定义入口点定义为:

const char my_interp[] __attribute__((section(".interp"))) = "/lib64/ld-linux-x86-64.so.2";

void my_main() {
   printf("VERSION: %d\n", 0);
   _exit(0);
}

我用

编译
gcc -o list.os -c -g -Wall -fPIC list.c
gcc -o liblist.so -g -Wl,-e,my_main -shared list.os -lc

这段代码编译运行完美。

我的问题是当我将 printf 的参数更改为浮点数或双精度数(%f 或 %lf)时。然后库将编译,但运行时segfault

有人有什么想法吗?

edit1:

这是段错误的代码:

const char my_interp[] __attribute__((section(".interp"))) = "/lib64/ld-linux-x86-64.so.2"; 

void my_main() { 
    printf("VERSION: %f\n", 0.1f); 
    _exit(0); 
} 

edit2:

其他环境细节:

uname -a

Linux mjolnir.site 3.1.10-1.16-desktop #1 SMP PREEMPT Wed Jun 27 05:21:40 UTC 2012 (d016078) x86_64 x86_64 x86_64 GNU/Linux

gcc --version

gcc (SUSE Linux) 4.6.2

/lib64/libc.so.6

为 x86_64-suse-linux 配置。 由 GNU CC 版本 4.6.2 编译。 于 2012 年 3 月 30 日在 Linux 3.1.0 系统上编译。

编辑 3:

在段错误时在 /var/log/messages 中输出:

8 月 11 日 08:27:45 mjolnir 内核:[10560.068741] liblist.so[11222] 一般保护 ip:7fc2b3cb2314 sp:7fff4f5c7de8 错误:0 in libc-2.14.1.so[7fc2b3c63000+187000]

【问题讨论】:

  • 这在我的 32 位机器上完美运行。它也应该在 64 位上工作。你是否包含了
  • @TOC 我做到了。您是否尝试打印浮点数作为版本号?我发布的代码工作正常,除了打印浮点数。
  • 你能显示打印浮动的代码吗?
  • @kobrien:是的,代码在 Linux(32 位)上可以正常工作,用于浮点和双精度
  • “重新创建 /dev/null 作为解决方案”?我完全不明白。

标签: c linux gcc shared-libraries


【解决方案1】:

想通了。 :)

x86_64 上的浮点运算使用 xmm 向量寄存器。对这些的访问必须在 16 字节边界上对齐。这就解释了为什么 32 位平台不受影响,而整数和字符打印工作正常。

我已将我的代码编译为:

gcc -W list.c -o list.S -shared -Wl,-e,my_main -S -fPIC

然后将“my_main”函数更改为拥有更多堆栈空间。

之前:

my_main:
 .LFB6:
 .cfi_startproc
 pushq   %rbp
 .cfi_def_cfa_offset 16
 .cfi_offset 6, -16
 movq    %rsp, %rbp
 .cfi_def_cfa_register 6
 movl    $.LC0, %eax
 movsd   .LC1(%rip), %xmm0
 movq    %rax, %rdi
 movl    $1, %eax
 call    printf
 movl    $0, %edi
 call    _exit
 .cfi_endproc

之后:

my_main:
 .LFB6:
 .cfi_startproc
 pushq   %rbp
 .cfi_def_cfa_offset 16
 .cfi_offset 6, -16
 subq    $8, %rsp ;;;;;;;;;;;;;;; ADDED THIS LINE
 movq    %rsp, %rbp
 .cfi_def_cfa_register 6
 movl    $.LC0, %eax
 movsd   .LC1(%rip), %xmm0
 movq    %rax, %rdi
 movl    $1, %eax
 call    printf
 movl    $0, %edi
 call    _exit
 .cfi_endproc

然后我通过以下方式编译了这个.S文件:

gcc list.S -o liblist.so -Wl,-e,my_main -shared

这解决了问题,但我会将这个帖子转发到 GCC 和 GLIBC 邮件列表,因为它看起来像一个错误。

edit1:

根据 gcc irc 中的 noshadow,这是一种非标准的方法。他说如果要使用 gcc -e 选项,要么手动初始化 C 运行时,要么不使用 libc 函数。说得通。

【讨论】:

  • ISTR 表示 gcc 有一个选项可以在比默认边界更大的边界上对齐堆栈。在修复错误之前,您可以将其用作解决方法,而不是修改程序集。
  • 听起来不错。另一种方法是不使用 libc 函数并直接使用系统调用,但请注意这是更多的代码和更少的可移植性。
  • 将单个浮点值加载到 XMM 寄存器中的指令不需要 16 字节对齐。它们只要求单精度四字节对齐和双精度八字节对齐。您可能遇到了其他问题,例如调用子例程时违反了应用程序二进制接口要求(可能未正确对齐堆栈,可能未设置指示传递浮点参数的位或其他问题)。跨度>
猜你喜欢
  • 2019-11-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-06-29
  • 1970-01-01
相关资源
最近更新 更多