2
votes

How do I fix this Seg Fault in my DLL?

I'm generating a Windows DLL (in Ada) and using the DLL from an Ada program. I’m using AdaCore’s GNAT GPS v6.0.1 IDE for both the DLL and an Ada program to test the DLL, running on a Windows 7 machine. Two separate project files are used, one for the DLL, the other for the test driver. The DLL does not have any DLLMain nor initialization or finalization routines.

As a first step (because I've never created a DLL or used GPS prior to this, do know some Ada though), I coded two very simple functions for the DLL. One function returns a pointer to a string, the other function returns a fixed length string.

The test program successfully calls the DLL function that returns a fixed-length string, however when calling the function that returns a string pointer, a segmentation fault occurs. Here is the gcc debug output:

Program received signal SIGSEGV, Segmentation fault.
0x6b81dd2c in system.secondary_stack.ss_mark () from C:\GNAT\2014\bin\libgnat-2014.dll
(gdb) quit

Here is the code:

DLL Spec

with Ada.Strings.Fixed;            use Ada.Strings.Fixed;

package String_Utils is
   type String_Ptr_T is access String;
   type Spec_Str is new String (1..7);

   function Int_Trim_Left( IntToTrim : Integer) return String_Ptr_T;
   pragma Export(DLL, Int_Trim_Left, "String_Utils__Int_Trim_Left");

   function Spec( Input_Int : Integer) return Spec_Str;
   pragma Export(DLL, Spec, "String_Utils__Spec");

end String_Utils;

DLL Body

package body String_Utils is

   function Int_Trim_Left( IntToTrim : Integer) return String_Ptr_T is
      String_Ptr   : String_Ptr_T;
   begin
      Text_IO.Put_Line("About to call new String in DLL.");
      String_Ptr := new String'(
                               Ada.Strings.Fixed.Trim(Integer'Image(IntToTrim),
                                  Ada.Strings.Left));
      return String_Ptr;
   end;
   --
   function Spec( Input_Int : Integer) return Spec_Str
   is
      Result_Spec : String := "ABC-UNK";
   begin
      case Input_Int is
         when 1 => return "ABC-STD"; -- Standard
         when 2 => return "ABC-PRF"; -- Performance
         when 3 => return "DEF-DTL"; -- Detailed
         when Others => return "ABC-UNK";
      end case;
   end;

DLL Project File

project HAGUtils is

   for Library_Name use "HAGUtils";
   for Library_Dir use "libdir";
   for Library_Version use "0.01";
   for Library_Kind use "dynamic";
   for Object_Dir use "obj";
   for Source_Dirs use ("src");
   for Source_Files use ("string_utils.adb", "string_utils.ads");

end HAGUtils;

Test Driver

-- Driver for DLL
with Text_IO;                       use Text_IO;

procedure test_ada_dll is

   type String_Ptr_T is access String;
   subtype String7 is String(1..7);

   input_val      : Integer := 0;
   Spec_Str       : String7 := (Others => ' ');
   Int_String_Ptr : String_Ptr_T:= null;

   -- Import
   function Int_Trim_Left ( IntToTrim : Integer) return String_Ptr_T
   is
      function Inner_Int_Trim_Left ( IntToTrim : Integer) return String_Ptr_T;
      pragma Import (DLL, Inner_Int_Trim_Left, "String_Utils__Int_Trim_Left");

   begin
      return Inner_Int_Trim_Left (IntToTrim);
   end Int_Trim_Left;

   -- Import
   function Spec ( Input_Int : Integer) return String7
   is
      function Inner_Spec ( Input_Int : Integer) return String7;
      pragma Import (DLL, Inner_Spec, "String_Utils__Spec");

   begin
      return Inner_Spec (Input_Int);
   end Spec;

begin
   input_val := 3;
   Spec_Str := Spec(input_val);
   Text_IO.Put_Line("The Spec is -- " & Spec_Str);

   Text_IO.Put_Line("Calling Int_Trim_Left with --" & Integer'Image(input_val));
   Int_String_Ptr :=  Int_Trim_Left(input_val);
   Text_IO.Put_Line("After call  --" & Int_String_Ptr.all);
end;
1

1 Answers

4
votes

I think that the SEGV happened because your DLL wasn’t initialized. The Ada runtime system needs initialization, which in the absence of DLLs would be called up in the GNAT bind process (you may have seen calls to gnatbind or gprbind flashing up the screen).

However, you have a DLL that requires the RTS to be initialized (the part that deals with the secondary stack, which is where GNAT constructs temporary unconstrained objects such as strings); but the binder isn’t aware of this because of the way you’ve linked your program (you don’t say, but I suspect you’ve specified the DLL via -lHAGutils?).

The way to get GNAT to handle this for you is to write a project file for the test program and have it with your DLL’s project:

with "HAGutils";
project Test_Ada_Dll is
for Main use ("test_ada_dll.adb");
  for Exec_Dir use ".";
  for Source_Files use ("test_ada_dll.adb");
  for Object_Dir use ".build";
end Test_Ada_Dll;

This then makes the interfaces of HAGlib visible to test_ada_dll, so you can change it to say

with Text_IO;                       use Text_IO;
with String_Utils;

procedure test_ada_dll is

   input_val      : Integer := 0;
   Spec_Str       : String_Utils.Spec_Str := (Others => ' ');
   Int_String_Ptr : String_Utils.String_Ptr_T:= null;

begin
   input_val := 3;
   Spec_Str := String_Utils.Spec(input_val);
   Text_IO.Put_Line("The Spec is -- " & String (Spec_Str));

   Text_IO.Put_Line("Calling Int_Trim_Left with --" & Integer'Image(input_val));
   Int_String_Ptr :=  String_Utils.Int_Trim_Left(input_val);
   Text_IO.Put_Line("After call  --" & Int_String_Ptr.all);
end;

(note, the conversion in Text_IO.Put_Line("The Spec is -- " & String (Spec_Str)); is because Spec_Str is a derived type; I think it’s be more normal in this case to make it a subtype).

Further, you no longer need to use the pragma Exports in String_Utils’s spec.


The result of this is that the binder is aware of the properties of your HAGutils DLL, and can arrange for the necessary initializations to happen.


There is a way in which you can make your original code work, which is to use the GPR attribute Library_Auto_Init in HAGutils.gpr:

for Library_Auto_Init use “true”;

but I think you’d have to make HAGlib a proper standalone library. This is quite complex to get right, and not necessary to get the library working to start with.