Bug 241287

Summary: Calling sprintf/printf from Ada segfaults on x86_64
Product: [Fedora] Fedora Reporter: Orion Poplawski <orion>
Component: gccAssignee: Jakub Jelinek <jakub>
Status: CLOSED NOTABUG QA Contact:
Severity: medium Docs Contact:
Priority: medium    
Version: rawhide   
Target Milestone: ---   
Target Release: ---   
Hardware: x86_64   
OS: Linux   
Whiteboard:
Fixed In Version: Doc Type: Bug Fix
Doc Text:
Story Points: ---
Clone Of: Environment:
Last Closed: 2007-05-24 21:12:34 UTC Type: ---
Regression: --- Mount Type: ---
Documentation: --- CRM:
Verified Versions: Category: ---
oVirt Team: --- RHEL 7.3 requirements from Atomic Host:
Cloudforms Team: --- Target Upstream Version:
Embargoed:

Description Orion Poplawski 2007-05-24 20:42:40 UTC
Description of problem:

(This is somewhat confusing because I *think* this must have worked on April 16
which is when I built plplot-5.7.3-2.fc7, and the test that fails should have
been run then, but the build logs appear to be gone.)

I'm afraid I know almost nothing about Ada.  This code is adapted from a PlPlot
example test case.  Perhaps there is an obvious programming error, but it seems
like the interface declaration is correct.

with
    Ada.Text_IO,
    Ada.Numerics,
    Ada.Numerics.Long_Elementary_Functions,
    Ada.Strings.Bounded,
    Interfaces.C,
    Interfaces.C.Pointers,
    System,
    Ada.Strings.Unbounded;
use
    Interfaces.C,
    Ada.Text_IO,
    Ada.Strings.Bounded,
    Ada.Strings.Unbounded,
    Ada.Numerics,
    Ada.Numerics.Long_Elementary_Functions;

procedure x12b is
    y0 : array (Integer range 0 .. 9) of Long_Float;
    procedure Printf( format : in char_array; variable: in Long_Float );
    pragma Import(C, Printf, "printf" );

    begin
       y0(0) := 5.0;
       y0(1) := 15.0;
       y0(2) := 12.0;
       y0(3) := 24.0;
       y0(4) := 28.0;
       y0(5) := 30.0;
       y0(6) := 20.0;
       y0(7) := 8.0;
       y0(8) := 12.0;
       y0(9) := 3.0;

       for i in y0'Range loop
          printf(To_C("%.0f"), y0(i));
          printf(To_C("%.0f"), Long_Float(1980 + i));
       end loop;

end x12b;

When compiled and run on a Fedora Linux x86_64 machine it segfaults (or hangs in
an endless loop of segfaults as shown by strace).  Runs fine on i686. 
Originally the code used sprintf:

    string : char_array(0 .. 20);
    y0 : array (Integer range 0 .. 9) of Long_Float;
    procedure Sprintf( buffer : out char_array; format : in char_array;
variable: in Long_Float );
    pragma Import(C, Sprintf, "sprintf" );

and:

          sprintf(string, To_C("%.0f"), y0(i));

and that also crashes.

This is with gcc-gnat-4.1.1-51.fc5 through gcc-gnat-4.1.2-12.  Tested on FC5/6/7.

Comment 1 Jakub Jelinek 2007-05-24 21:12:34 UTC
This is clearly invalid code.
procedure Printf( format : in char_array; variable: in Long_Float );
pragma Import(C, Printf, "printf" );
You are lying to the compiler, printf is not printf (const char *, double)
but printf (const char *, ...).  While on i386 these two happen to have the
same calling convention, on x86_64 they don't (vararg functions need to
additionally set %rax register to the number of used %xmm* argument).
With the incorrect declaration, %rax contains some random value and if it
is bigger than 7, the code to save %xmm0..%xmm[%rax-1] registers to stack
(see x86_64 ABI) will just jump either to a middle of some instruction or some
unrelated instruction.
Not sure if Ada allows you to specify a vararg function some way, if yes,
use it, if not, you can't call these functions directly, you'd need to write
a short wrapper function in C that will do that:
int printf_arg_double (const char *fmt, double d) { return printf (fmt, d); }
and call that from Ada instead.

Comment 2 Orion Poplawski 2007-05-24 21:14:25 UTC
Thanks for the explanation Jakub - much appreciated.