diff mbox series

[Ada] Fix handling of load addresses for backtraces through SO

Message ID 20171205115844.GA121498@adacore.com
State New
Headers show
Series [Ada] Fix handling of load addresses for backtraces through SO | expand

Commit Message

Pierre-Marie de Rodat Dec. 5, 2017, 11:58 a.m. UTC
The processing of shared library load addresses for backtrace symbolization
suffers from disconnects between various parts in the runtime library regarding
where run-time vs shared-object-file-relative addresses are at hand.

At some points the code processes a local copy of each address coming from a
backtrace, adjusted by the load address when it comes from a shared library,
while other places keep referring to the run-time (unadjusted) address within
the backtrace array, at spots where module-relative addresses are needed as
well, e.g. when looking up debug info mmaped from the module file.

The first obvious "fix" idea for this would be to adjust the addresses in the
backtrace array as well, but we need to keep the unadjusted address someplace,
at least be able to display it to the user. It corresponds to real run-time
addresses that can be looked-up and examined while the process is running, so
is generally valuable.

This change introduces more explicit and systematic use of the load address
shift instead.

Tested on x86_64-pc-linux-gnu, committed on trunk

2017-12-05  Olivier Hainque  <hainque@adacore.com>

libgnat/

	* s-trasym__dwarf.adb (spec of Module_Name.Get): Instead of
	possibly adjusting the lookup address by a load address, expect
	a extra argument through which the load address can be conveyed
	separately.
	(Multi_Module_Symbolic_Traceback): Adjust accordingly. Pass the
	retrieved load address to Init_Module.
	* s-tsmona__linux.adb (Get): Honor the new interface.
	* s-tsmona__mingw.adb (Get): Likewise.
	* s-dwalin.ads: Adjust comments to be explicit about which
	addresses are from module info and which are run-time addresses,
	offsetted by the module load address.
	* s-dwalin.adb (Set_Load_Address): Simply set C.Load_Slide.
	Do not alter the module Low and High (relative) addresses.
	(Is_Inside): Improve documentation regarding the kinds of addresses
	at hand and correct the test.
	(Symbolic_Traceback): Use separate variables with explicit names
	for the address in traceback (run-time value) and the address to
	lookup within the shared object (module-relative). Adjust the
	computation of address passed to Symbolic_Address for symbolization.
diff mbox series

Patch

Index: libgnat/s-tsmona__mingw.adb
===================================================================
--- libgnat/s-tsmona__mingw.adb	(revision 255408)
+++ libgnat/s-tsmona__mingw.adb	(working copy)
@@ -50,15 +50,20 @@ 
    -- Get --
    ---------
 
-   function Get (Addr : access System.Address) return String is
+   function Get (Addr : System.Address;
+                 Load_Addr : access System.Address)
+     return String
+   is
       Res     : DWORD;
       hModule : aliased HANDLE;
       Path    : String (1 .. 1_024);
 
    begin
+      Load_Addr.all := System.Null_Address;
+
       if GetModuleHandleEx
            (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
-            Addr.all,
+            Addr,
             hModule'Access) = Win32.TRUE
       then
          Res := GetModuleFileName (hModule, Path'Address, Path'Length);
Index: libgnat/s-tsmona__linux.adb
===================================================================
--- libgnat/s-tsmona__linux.adb	(revision 255408)
+++ libgnat/s-tsmona__linux.adb	(working copy)
@@ -32,8 +32,6 @@ 
 --  This is the GNU/Linux specific version of this package
 with Interfaces.C;              use Interfaces.C;
 
-with System.Address_Operations; use System.Address_Operations;
-
 separate (System.Traceback.Symbolic)
 
 package body Module_Name is
@@ -134,7 +132,10 @@ 
    -- Get --
    ---------
 
-   function Get (Addr : access System.Address) return String is
+   function Get (Addr : System.Address;
+                 Load_Addr : access System.Address)
+     return String
+   is
 
       --  Dl_info record for Linux, used to get sym reloc offset
 
@@ -154,13 +155,15 @@ 
       info : aliased Dl_info;
 
    begin
-      if dladdr (Addr.all, info'Access) /= 0 then
+      Load_Addr.all := System.Null_Address;
 
+      if dladdr (Addr, info'Access) /= 0 then
+
          --  If we have a shared library we need to adjust the address to
          --  be relative to the base address of the library.
 
          if Is_Shared_Lib (info.dli_fbase) then
-            Addr.all := SubA (Addr.all, info.dli_fbase);
+            Load_Addr.all := info.dli_fbase;
          end if;
 
          return Value (info.dli_fname);
Index: libgnat/s-trasym__dwarf.adb
===================================================================
--- libgnat/s-trasym__dwarf.adb	(revision 255408)
+++ libgnat/s-trasym__dwarf.adb	(working copy)
@@ -132,10 +132,12 @@ 
       procedure Build_Cache_For_All_Modules;
       --  Create the cache for all current modules
 
-      function Get (Addr : access System.Address) return String;
-      --  Returns the module name for the given address, Addr may be updated
-      --  to be set relative to a shared library. This depends on the platform.
-      --  Returns an empty string for the main executable.
+      function Get (Addr : System.Address;
+                    Load_Addr : access System.Address) return String;
+      --  Returns the module name for the given address Addr, or an empty
+      --  string for the main executable.  Load_Addr is set to the shared
+      --  library load address if this information is available, or to
+      --  System.Null_Address otherwise.
 
       function Is_Supported return Boolean;
       pragma Inline (Is_Supported);
@@ -499,12 +501,14 @@ 
 
          --  Otherwise, try a shared library
          declare
-            Addr    : aliased System.Address := Traceback (F);
-            M_Name  : constant String        := Module_Name.Get (Addr'Access);
+            Load_Addr : aliased System.Address;
+            M_Name  : constant String :=
+              Module_Name.Get (Addr => Traceback (F),
+                               Load_Addr => Load_Addr'Access);
             Module  : Module_Cache;
             Success : Boolean;
          begin
-            Init_Module (Module, Success, M_Name, System.Null_Address);
+            Init_Module (Module, Success, M_Name, Load_Addr);
             if Success then
                Multi_Module_Symbolic_Traceback
                  (Traceback,
Index: libgnat/s-dwalin.adb
===================================================================
--- libgnat/s-dwalin.adb	(revision 255408)
+++ libgnat/s-dwalin.adb	(working copy)
@@ -372,7 +372,8 @@ 
 
    function Is_Inside (C : Dwarf_Context; Addr : Address) return Boolean is
    begin
-      return Addr >= C.Low and Addr <= C.High;
+      return (Addr >= To_Address (To_Integer (C.Low) + C.Load_Slide)
+                and Addr <= To_Address (To_Integer (C.High) + C.Load_Slide));
    end Is_Inside;
 
    ---------
@@ -771,15 +772,7 @@ 
 
    procedure Set_Load_Address (C : in out Dwarf_Context; Addr : Address) is
    begin
-      if Addr = Null_Address then
-         return;
-      else
-         C.Load_Slide :=
-           To_Integer (Addr) - Integer_Address (Get_Load_Address (C.Obj.all));
-
-         C.Low  := To_Address (To_Integer (C.Low) + C.Load_Slide);
-         C.High := To_Address (To_Integer (C.High) + C.Load_Slide);
-      end if;
+      C.Load_Slide := To_Integer (Addr);
    end Set_Load_Address;
 
    ------------------
@@ -1523,9 +1516,11 @@ 
       Res          : in out System.Bounded_Strings.Bounded_String)
    is
       use Ada.Characters.Handling;
-      C    : Dwarf_Context := Cin;
-      Addr : Address;
+      C : Dwarf_Context := Cin;
 
+      Addr_In_Traceback : Address;
+      Addr_To_Lookup    : Address;
+
       Dir_Name    : Str_Access;
       File_Name   : Str_Access;
       Subprg_Name : String_Ptr_Len;
@@ -1543,10 +1538,14 @@ 
          --  If the buffer is full, no need to do any useless work
          exit when Is_Full (Res);
 
-         Addr := PC_For (Traceback (J));
+         Addr_In_Traceback := PC_For (Traceback (J));
+
+         Addr_To_Lookup := To_Address
+           (To_Integer (Addr_In_Traceback) - C.Load_Slide);
+
          Symbolic_Address
            (C,
-            To_Address (To_Integer (Addr) + C.Load_Slide),
+            Addr_To_Lookup,
             Dir_Name,
             File_Name,
             Subprg_Name,
@@ -1608,7 +1607,7 @@ 
             if Suppress_Hex then
                Append (Res, "...");
             else
-               Append_Address (Res, Addr);
+               Append_Address (Res, Addr_In_Traceback);
             end if;
 
             if Subprg_Name.Len > 0 then
Index: libgnat/s-dwalin.ads
===================================================================
--- libgnat/s-dwalin.ads	(revision 255408)
+++ libgnat/s-dwalin.ads	(working copy)
@@ -73,11 +73,11 @@ 
 
    function Is_Inside (C : Dwarf_Context; Addr : Address) return Boolean;
    pragma Inline (Is_Inside);
-   --  Return true iff Addr is within the module
+   --  Return true iff a run-time address Addr is within the module
 
    function Low (C : Dwarf_Context) return Address;
    pragma Inline (Low);
-   --  Return the lowest address of C
+   --  Return the lowest address of C, from the module object file
 
    procedure Dump (C : in out Dwarf_Context);
    --  Dump each row found in the object's .debug_lines section to standard out
@@ -165,7 +165,7 @@ 
    type Dwarf_Context (In_Exception : Boolean := False) is record
       Load_Slide : System.Storage_Elements.Integer_Address := 0;
       Low, High  : Address;
-      --  Bounds of the module
+      --  Bounds of the module, per the module object file
 
       Obj : SOR.Object_File_Access;
       --  The object file containing dwarf sections