diff gcc/ada/libgnat/s-dwalin.adb @ 111:04ced10e8804

gcc 7
author kono
date Fri, 27 Oct 2017 22:46:09 +0900
parents
children 84e7813d76e9
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gcc/ada/libgnat/s-dwalin.adb	Fri Oct 27 22:46:09 2017 +0900
@@ -0,0 +1,1627 @@
+------------------------------------------------------------------------------
+--                                                                          --
+--                         GNAT COMPILER COMPONENTS                         --
+--                                                                          --
+--                   S Y S T E M . D W A R F _ L I N E S                    --
+--                                                                          --
+--                                 B o d y                                  --
+--                                                                          --
+--          Copyright (C) 2009-2017, Free Software Foundation, Inc.         --
+--                                                                          --
+-- GNAT is free software;  you can  redistribute it  and/or modify it under --
+-- terms of the  GNU General Public License as published  by the Free Soft- --
+-- ware  Foundation;  either version 3,  or (at your option) any later ver- --
+-- sion.  GNAT is distributed in the hope that it will be useful, but WITH- --
+-- OUT ANY WARRANTY;  without even the  implied warranty of MERCHANTABILITY --
+-- or FITNESS FOR A PARTICULAR PURPOSE.                                     --
+--                                                                          --
+-- As a special exception under Section 7 of GPL version 3, you are granted --
+-- additional permissions described in the GCC Runtime Library Exception,   --
+-- version 3.1, as published by the Free Software Foundation.               --
+--                                                                          --
+-- You should have received a copy of the GNU General Public License and    --
+-- a copy of the GCC Runtime Library Exception along with this program;     --
+-- see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see    --
+-- <http://www.gnu.org/licenses/>.                                          --
+--                                                                          --
+-- GNAT was originally developed  by the GNAT team at  New York University. --
+-- Extensive contributions were provided by Ada Core Technologies Inc.      --
+--                                                                          --
+------------------------------------------------------------------------------
+
+pragma Polling (Off);
+--  We must turn polling off for this unit, because otherwise we can get
+--  elaboration circularities when polling is turned on
+
+with Ada.Characters.Handling;
+with Ada.Exceptions.Traceback; use Ada.Exceptions.Traceback;
+with Ada.Unchecked_Deallocation;
+with Ada.Containers.Generic_Array_Sort;
+
+with Interfaces; use Interfaces;
+
+with System;                   use System;
+with System.Storage_Elements;  use System.Storage_Elements;
+with System.Address_Image;
+with System.IO;                use System.IO;
+with System.Object_Reader;     use System.Object_Reader;
+with System.Traceback_Entries; use System.Traceback_Entries;
+with System.Mmap;              use System.Mmap;
+with System.Bounded_Strings;   use System.Bounded_Strings;
+
+package body System.Dwarf_Lines is
+
+   SSU : constant := System.Storage_Unit;
+
+   function String_Length (Str : Str_Access) return Natural;
+   --  Return the length of the C string Str
+
+   ---------------------------------
+   -- DWARF Parser Implementation --
+   ---------------------------------
+
+   procedure Read_Initial_Length
+     (S    : in out Mapped_Stream;
+      Len  :    out Offset;
+      Is64 :    out Boolean);
+   --  Read initial length as specified by Dwarf-4 7.2.2
+
+   procedure Read_Section_Offset
+     (S    : in out Mapped_Stream;
+      Len  :    out Offset;
+      Is64 :        Boolean);
+   --  Read a section offset, as specified by Dwarf-4 7.4
+
+   procedure Read_Aranges_Entry
+     (C     : in out Dwarf_Context;
+      Start :    out Integer_Address;
+      Len   :    out Storage_Count);
+   --  Read a single .debug_aranges pair
+
+   procedure Read_Aranges_Header
+     (C           : in out Dwarf_Context;
+      Info_Offset :    out Offset;
+      Success     :    out Boolean);
+   --  Read .debug_aranges header
+
+   procedure Aranges_Lookup
+     (C           : in out Dwarf_Context;
+      Addr        :        Address;
+      Info_Offset :    out Offset;
+      Success     :    out Boolean);
+   --  Search for Addr in .debug_aranges and return offset Info_Offset in
+   --  .debug_info.
+
+   procedure Skip_Form
+     (S      : in out Mapped_Stream;
+      Form   :        uint32;
+      Is64   :        Boolean;
+      Ptr_Sz :        uint8);
+   --  Advance offset in S for Form.
+
+   procedure Seek_Abbrev
+     (C             : in out Dwarf_Context;
+      Abbrev_Offset :        Offset;
+      Abbrev_Num    :        uint32);
+   --  Seek to abbrev Abbrev_Num (starting from Abbrev_Offset)
+
+   procedure Debug_Info_Lookup
+     (C           : in out Dwarf_Context;
+      Info_Offset :        Offset;
+      Line_Offset :    out Offset;
+      Success     :    out Boolean);
+   --  Search for stmt_list tag in Info_Offset and set Line_Offset to the
+   --  offset in .debug_lines. Only look at the first DIE, which should be
+   --  a compilation unit.
+
+   procedure Initialize_Pass (C : in out Dwarf_Context);
+   --  Seek to the first byte of the first prologue and prepare to make a pass
+   --  over the line number entries.
+
+   procedure Initialize_State_Machine (C : in out Dwarf_Context);
+   --  Set all state machine registers to their specified initial values
+
+   procedure Parse_Prologue (C : in out Dwarf_Context);
+   --  Decode a DWARF statement program prologue
+
+   procedure Read_And_Execute_Isn
+     (C    : in out Dwarf_Context;
+      Done :    out Boolean);
+   --  Read an execute a statement program instruction
+
+   function To_File_Name
+     (C    : in out Dwarf_Context;
+      Code :        uint32) return String;
+   --  Extract a file name from the prologue
+
+   type Callback is access procedure (C : in out Dwarf_Context);
+   procedure For_Each_Row (C : in out Dwarf_Context; F : Callback);
+   --  Traverse each .debug_line entry with a callback
+
+   procedure Dump_Row (C : in out Dwarf_Context);
+   --  Dump a single row
+
+   function "<" (Left, Right : Search_Entry) return Boolean;
+   --  For sorting Search_Entry
+
+   procedure Sort_Search_Array is new Ada.Containers.Generic_Array_Sort
+     (Index_Type   => Natural,
+      Element_Type => Search_Entry,
+      Array_Type   => Search_Array);
+
+   procedure Symbolic_Address
+     (C           : in out Dwarf_Context;
+      Addr        :        Address;
+      Dir_Name    :    out Str_Access;
+      File_Name   :    out Str_Access;
+      Subprg_Name :    out String_Ptr_Len;
+      Line_Num    :    out Natural);
+   --  Symbolize one address
+
+   -----------------------
+   --  DWARF constants  --
+   -----------------------
+
+   --  6.2.5.2 Standard Opcodes
+
+   DW_LNS_copy               : constant := 1;
+   DW_LNS_advance_pc         : constant := 2;
+   DW_LNS_advance_line       : constant := 3;
+   DW_LNS_set_file           : constant := 4;
+   DW_LNS_set_column         : constant := 5;
+   DW_LNS_negate_stmt        : constant := 6;
+   DW_LNS_set_basic_block    : constant := 7;
+   DW_LNS_const_add_pc       : constant := 8;
+   DW_LNS_fixed_advance_pc   : constant := 9;
+   DW_LNS_set_prologue_end   : constant := 10;
+   DW_LNS_set_epilogue_begin : constant := 11;
+   DW_LNS_set_isa            : constant := 12;
+
+   --  6.2.5.3 Extended Opcodes
+
+   DW_LNE_end_sequence : constant := 1;
+   DW_LNE_set_address  : constant := 2;
+   DW_LNE_define_file  : constant := 3;
+
+   --  From the DWARF version 4 public review draft
+
+   DW_LNE_set_discriminator : constant := 4;
+
+   --  Attribute encodings
+
+   DW_TAG_Compile_Unit : constant := 16#11#;
+
+   DW_AT_Stmt_List : constant := 16#10#;
+
+   DW_FORM_addr         : constant := 16#01#;
+   DW_FORM_block2       : constant := 16#03#;
+   DW_FORM_block4       : constant := 16#04#;
+   DW_FORM_data2        : constant := 16#05#;
+   DW_FORM_data4        : constant := 16#06#;
+   DW_FORM_data8        : constant := 16#07#;
+   DW_FORM_string       : constant := 16#08#;
+   DW_FORM_block        : constant := 16#09#;
+   DW_FORM_block1       : constant := 16#0a#;
+   DW_FORM_data1        : constant := 16#0b#;
+   DW_FORM_flag         : constant := 16#0c#;
+   DW_FORM_sdata        : constant := 16#0d#;
+   DW_FORM_strp         : constant := 16#0e#;
+   DW_FORM_udata        : constant := 16#0f#;
+   DW_FORM_ref_addr     : constant := 16#10#;
+   DW_FORM_ref1         : constant := 16#11#;
+   DW_FORM_ref2         : constant := 16#12#;
+   DW_FORM_ref4         : constant := 16#13#;
+   DW_FORM_ref8         : constant := 16#14#;
+   DW_FORM_ref_udata    : constant := 16#15#;
+   DW_FORM_indirect     : constant := 16#16#;
+   DW_FORM_sec_offset   : constant := 16#17#;
+   DW_FORM_exprloc      : constant := 16#18#;
+   DW_FORM_flag_present : constant := 16#19#;
+   DW_FORM_ref_sig8     : constant := 16#20#;
+
+   ---------
+   -- "<" --
+   ---------
+
+   function "<" (Left, Right : Search_Entry) return Boolean is
+   begin
+      return Left.First < Right.First;
+   end "<";
+
+   -----------
+   -- Close --
+   -----------
+
+   procedure Close (C : in out Dwarf_Context) is
+      procedure Unchecked_Deallocation is new Ada.Unchecked_Deallocation
+        (Object_File,
+         Object_File_Access);
+      procedure Unchecked_Deallocation is new Ada.Unchecked_Deallocation
+        (Search_Array,
+         Search_Array_Access);
+   begin
+      if C.Has_Debug then
+         Close (C.Lines);
+         Close (C.Abbrev);
+         Close (C.Info);
+         Close (C.Aranges);
+      end if;
+
+      Close (C.Obj.all);
+      Unchecked_Deallocation (C.Obj);
+
+      Unchecked_Deallocation (C.Cache);
+   end Close;
+
+   ----------
+   -- Dump --
+   ----------
+
+   procedure Dump (C : in out Dwarf_Context) is
+   begin
+      For_Each_Row (C, Dump_Row'Access);
+   end Dump;
+
+   --------------
+   -- Dump_Row --
+   --------------
+
+   procedure Dump_Row (C : in out Dwarf_Context) is
+      PC  : constant Integer_Address := Integer_Address (C.Registers.Address);
+      Off : Offset;
+   begin
+      Tell (C.Lines, Off);
+
+      Put (System.Address_Image (To_Address (PC)));
+      Put (" ");
+      Put (To_File_Name (C, C.Registers.File));
+      Put (":");
+
+      declare
+         Image : constant String := uint32'Image (C.Registers.Line);
+      begin
+         Put_Line (Image (2 .. Image'Last));
+      end;
+
+      Seek (C.Lines, Off);
+   end Dump_Row;
+
+   procedure Dump_Cache (C : Dwarf_Context) is
+      Cache : constant Search_Array_Access := C.Cache;
+      S     : Object_Symbol;
+      Name  : String_Ptr_Len;
+   begin
+      if Cache = null then
+         Put_Line ("No cache");
+         return;
+      end if;
+      for I in Cache'Range loop
+         Put (System.Address_Image (C.Low + Storage_Count (Cache (I).First)));
+         Put (" - ");
+         Put
+           (System.Address_Image
+              (C.Low + Storage_Count (Cache (I).First + Cache (I).Size)));
+         Put (" l@");
+         Put
+           (System.Address_Image
+              (To_Address (Integer_Address (Cache (I).Line))));
+         Put (": ");
+         S    := Read_Symbol (C.Obj.all, Offset (Cache (I).Sym));
+         Name := Object_Reader.Name (C.Obj.all, S);
+         Put (String (Name.Ptr (1 .. Name.Len)));
+         New_Line;
+      end loop;
+   end Dump_Cache;
+
+   ------------------
+   -- For_Each_Row --
+   ------------------
+
+   procedure For_Each_Row (C : in out Dwarf_Context; F : Callback) is
+      Done : Boolean;
+
+   begin
+      Initialize_Pass (C);
+
+      loop
+         Read_And_Execute_Isn (C, Done);
+
+         if C.Registers.Is_Row then
+            F.all (C);
+         end if;
+
+         exit when Done;
+      end loop;
+   end For_Each_Row;
+
+   ---------------------
+   -- Initialize_Pass --
+   ---------------------
+
+   procedure Initialize_Pass (C : in out Dwarf_Context) is
+   begin
+      Seek (C.Lines, 0);
+      C.Next_Prologue := 0;
+
+      Initialize_State_Machine (C);
+   end Initialize_Pass;
+
+   ------------------------------
+   -- Initialize_State_Machine --
+   ------------------------------
+
+   procedure Initialize_State_Machine (C : in out Dwarf_Context) is
+   begin
+      C.Registers :=
+        (Address        => 0,
+         File           => 1,
+         Line           => 1,
+         Column         => 0,
+         Is_Stmt        => C.Prologue.Default_Is_Stmt = 0,
+         Basic_Block    => False,
+         End_Sequence   => False,
+         Prologue_End   => False,
+         Epilogue_Begin => False,
+         ISA            => 0,
+         Is_Row         => False);
+   end Initialize_State_Machine;
+
+   ---------------
+   -- Is_Inside --
+   ---------------
+
+   function Is_Inside (C : Dwarf_Context; Addr : Address) return Boolean is
+   begin
+      return Addr >= C.Low and Addr <= C.High;
+   end Is_Inside;
+
+   ---------
+   -- Low --
+   ---------
+
+   function Low (C : Dwarf_Context) return Address is
+   begin
+      return C.Low;
+   end Low;
+
+   ----------
+   -- Open --
+   ----------
+
+   procedure Open
+     (File_Name :     String;
+      C         : out Dwarf_Context;
+      Success   : out Boolean)
+   is
+      Line_Sec, Info_Sec, Abbrev_Sec, Aranges_Sec : Object_Section;
+      Hi, Lo                                      : uint64;
+   begin
+      --  Not a success by default
+
+      Success := False;
+
+      --  Open file
+
+      C.Obj := Open (File_Name, C.In_Exception);
+
+      if C.Obj = null then
+         return;
+      end if;
+
+      Success := True;
+
+      --  Get memory bounds
+
+      Get_Memory_Bounds (C.Obj.all, Lo, Hi);
+      C.Low  := Address (Lo);
+      C.High := Address (Hi);
+
+      --  Create a stream for debug sections
+
+      if Format (C.Obj.all) = XCOFF32 then
+         Line_Sec    := Get_Section (C.Obj.all, ".dwline");
+         Abbrev_Sec  := Get_Section (C.Obj.all, ".dwabrev");
+         Info_Sec    := Get_Section (C.Obj.all, ".dwinfo");
+         Aranges_Sec := Get_Section (C.Obj.all, ".dwarnge");
+      else
+         Line_Sec    := Get_Section (C.Obj.all, ".debug_line");
+         Abbrev_Sec  := Get_Section (C.Obj.all, ".debug_abbrev");
+         Info_Sec    := Get_Section (C.Obj.all, ".debug_info");
+         Aranges_Sec := Get_Section (C.Obj.all, ".debug_aranges");
+      end if;
+
+      if Line_Sec = Null_Section
+        or else Abbrev_Sec = Null_Section
+        or else Info_Sec = Null_Section
+        or else Aranges_Sec = Null_Section
+      then
+         C.Has_Debug := False;
+         return;
+      end if;
+
+      C.Lines   := Create_Stream (C.Obj.all, Line_Sec);
+      C.Abbrev  := Create_Stream (C.Obj.all, Abbrev_Sec);
+      C.Info    := Create_Stream (C.Obj.all, Info_Sec);
+      C.Aranges := Create_Stream (C.Obj.all, Aranges_Sec);
+
+      --  All operations are successful, context is valid
+
+      C.Has_Debug := True;
+   end Open;
+
+   --------------------
+   -- Parse_Prologue --
+   --------------------
+
+   procedure Parse_Prologue (C : in out Dwarf_Context) is
+      Char : uint8;
+      Prev : uint8;
+      --  The most recently read character and the one preceding it
+
+      Dummy : uint32;
+      --  Destination for reads we don't care about
+
+      Buf : Buffer;
+      Off : Offset;
+
+      First_Byte_Of_Prologue : Offset;
+      Last_Byte_Of_Prologue  : Offset;
+
+      Max_Op_Per_Insn : uint8;
+      pragma Unreferenced (Max_Op_Per_Insn);
+
+      Prologue : Line_Info_Prologue renames C.Prologue;
+
+   begin
+      Tell (C.Lines, First_Byte_Of_Prologue);
+      Prologue.Unit_Length := Read (C.Lines);
+      Tell (C.Lines, Off);
+      C.Next_Prologue := Off + Offset (Prologue.Unit_Length);
+
+      Prologue.Version         := Read (C.Lines);
+      Prologue.Prologue_Length := Read (C.Lines);
+      Tell (C.Lines, Last_Byte_Of_Prologue);
+      Last_Byte_Of_Prologue :=
+        Last_Byte_Of_Prologue + Offset (Prologue.Prologue_Length) - 1;
+
+      Prologue.Min_Isn_Length := Read (C.Lines);
+
+      if Prologue.Version >= 4 then
+         Max_Op_Per_Insn := Read (C.Lines);
+      end if;
+
+      Prologue.Default_Is_Stmt := Read (C.Lines);
+      Prologue.Line_Base       := Read (C.Lines);
+      Prologue.Line_Range      := Read (C.Lines);
+      Prologue.Opcode_Base     := Read (C.Lines);
+
+      --  Opcode_Lengths is an array of Opcode_Base bytes specifying the number
+      --  of LEB128 operands for each of the standard opcodes.
+
+      for J in 1 .. uint32 (Prologue.Opcode_Base - 1) loop
+         Prologue.Opcode_Lengths (J) := Read (C.Lines);
+      end loop;
+
+      --  The include directories table follows. This is a list of null
+      --  terminated strings terminated by a double null. We only store
+      --  its offset for later decoding.
+
+      Tell (C.Lines, Prologue.Includes_Offset);
+      Char := Read (C.Lines);
+
+      if Char /= 0 then
+         loop
+            Prev := Char;
+            Char := Read (C.Lines);
+            exit when Char = 0 and Prev = 0;
+         end loop;
+      end if;
+
+      --  The file_names table is next. Each record is a null terminated string
+      --  for the file name, an unsigned LEB128 directory index, an unsigned
+      --  LEB128 modification time, and an LEB128 file length. The table is
+      --  terminated by a null byte.
+
+      Tell (C.Lines, Prologue.File_Names_Offset);
+
+      loop
+         --  Read the filename
+
+         Read_C_String (C.Lines, Buf);
+         exit when Buf (0) = 0;
+         Dummy := Read_LEB128 (C.Lines); --  Skip the directory index.
+         Dummy := Read_LEB128 (C.Lines); --  Skip the modification time.
+         Dummy := Read_LEB128 (C.Lines); --  Skip the file length.
+      end loop;
+
+      --  Check we're where we think we are. This sanity check ensures we think
+      --  the prologue ends where the prologue says it does. It we aren't then
+      --  we've probably gotten out of sync somewhere.
+
+      Tell (C.Lines, Off);
+
+      if Prologue.Unit_Length /= 0
+        and then Off /= Last_Byte_Of_Prologue + 1
+      then
+         raise Dwarf_Error with "Parse error reading DWARF information";
+      end if;
+   end Parse_Prologue;
+
+   --------------------------
+   -- Read_And_Execute_Isn --
+   --------------------------
+
+   procedure Read_And_Execute_Isn
+     (C    : in out Dwarf_Context;
+      Done :    out Boolean)
+   is
+      Opcode          : uint8;
+      Extended_Opcode : uint8;
+      uint32_Operand  : uint32;
+      int32_Operand   : int32;
+      uint16_Operand  : uint16;
+      Off             : Offset;
+
+      Extended_Length : uint32;
+      pragma Unreferenced (Extended_Length);
+
+      Obj : Object_File renames C.Obj.all;
+      Registers : Line_Info_Registers renames C.Registers;
+      Prologue : Line_Info_Prologue renames C.Prologue;
+
+   begin
+      Done             := False;
+      Registers.Is_Row := False;
+
+      if Registers.End_Sequence then
+         Initialize_State_Machine (C);
+      end if;
+
+      --  Read the next prologue
+
+      Tell (C.Lines, Off);
+      while Off = C.Next_Prologue loop
+         Initialize_State_Machine (C);
+         Parse_Prologue (C);
+         Tell (C.Lines, Off);
+         exit when Off + 4 >= Length (C.Lines);
+      end loop;
+
+      --  Test whether we're done
+
+      Tell (C.Lines, Off);
+
+      --  We are finished when we either reach the end of the section, or we
+      --  have reached zero padding at the end of the section.
+
+      if Prologue.Unit_Length = 0 or else Off + 4 >= Length (C.Lines) then
+         Done := True;
+         return;
+      end if;
+
+      --  Read and interpret an instruction
+
+      Opcode := Read (C.Lines);
+
+      --  Extended opcodes
+
+      if Opcode = 0 then
+         Extended_Length := Read_LEB128 (C.Lines);
+         Extended_Opcode := Read (C.Lines);
+
+         case Extended_Opcode is
+            when DW_LNE_end_sequence =>
+
+               --  Mark the end of a sequence of source locations
+
+               Registers.End_Sequence := True;
+               Registers.Is_Row       := True;
+
+            when DW_LNE_set_address =>
+
+               --  Set the program counter to a word
+
+               Registers.Address := Read_Address (Obj, C.Lines);
+
+            when DW_LNE_define_file =>
+
+               --  Not implemented
+
+               raise Dwarf_Error with "DWARF operator not implemented";
+
+            when DW_LNE_set_discriminator =>
+
+               --  Ignored
+
+               int32_Operand := Read_LEB128 (C.Lines);
+
+            when others =>
+
+               --  Fail on an unrecognized opcode
+
+               raise Dwarf_Error with "DWARF operator not implemented";
+         end case;
+
+      --  Standard opcodes
+
+      elsif Opcode < Prologue.Opcode_Base then
+         case Opcode is
+
+            --  Append a row to the line info matrix
+
+            when DW_LNS_copy =>
+               Registers.Basic_Block := False;
+               Registers.Is_Row      := True;
+
+            --  Add an unsigned word to the program counter
+
+            when DW_LNS_advance_pc =>
+               uint32_Operand    := Read_LEB128 (C.Lines);
+               Registers.Address :=
+                 Registers.Address +
+                 uint64 (uint32_Operand * uint32 (Prologue.Min_Isn_Length));
+
+            --  Add a signed word to the current source line
+
+            when DW_LNS_advance_line =>
+               int32_Operand  := Read_LEB128 (C.Lines);
+               Registers.Line :=
+                 uint32 (int32 (Registers.Line) + int32_Operand);
+
+            --  Set the current source file
+
+            when DW_LNS_set_file =>
+               uint32_Operand := Read_LEB128 (C.Lines);
+               Registers.File := uint32_Operand;
+
+            --  Set the current source column
+
+            when DW_LNS_set_column =>
+               uint32_Operand   := Read_LEB128 (C.Lines);
+               Registers.Column := uint32_Operand;
+
+            --  Toggle the "is statement" flag. GCC doesn't seem to set this???
+
+            when DW_LNS_negate_stmt =>
+               Registers.Is_Stmt := not Registers.Is_Stmt;
+
+            --  Mark the beginning of a basic block
+
+            when DW_LNS_set_basic_block =>
+               Registers.Basic_Block := True;
+
+            --  Advance the program counter as by the special opcode 255
+
+            when DW_LNS_const_add_pc =>
+               Registers.Address :=
+                 Registers.Address +
+                 uint64
+                   (((255 - Prologue.Opcode_Base) / Prologue.Line_Range) *
+                    Prologue.Min_Isn_Length);
+
+            --  Advance the program counter by a constant
+
+            when DW_LNS_fixed_advance_pc =>
+               uint16_Operand    := Read (C.Lines);
+               Registers.Address :=
+                 Registers.Address + uint64 (uint16_Operand);
+
+            --  The following are not implemented and ignored
+
+            when DW_LNS_set_prologue_end =>
+               null;
+
+            when DW_LNS_set_epilogue_begin =>
+               null;
+
+            when DW_LNS_set_isa =>
+               null;
+
+            --  Anything else is an error
+
+            when others =>
+               raise Dwarf_Error with "DWARF operator not implemented";
+         end case;
+
+      --  Decode a special opcode. This is a line and address increment encoded
+      --  in a single byte 'special opcode' as described in 6.2.5.1.
+
+      else
+         declare
+            Address_Increment : int32;
+            Line_Increment    : int32;
+
+         begin
+            Opcode := Opcode - Prologue.Opcode_Base;
+
+            --  The adjusted opcode is a uint8 encoding an address increment
+            --  and a signed line increment. The upperbound is allowed to be
+            --  greater than int8'last so we decode using int32 directly to
+            --  prevent overflows.
+
+            Address_Increment :=
+              int32 (Opcode / Prologue.Line_Range) *
+              int32 (Prologue.Min_Isn_Length);
+            Line_Increment :=
+              int32 (Prologue.Line_Base) +
+              int32 (Opcode mod Prologue.Line_Range);
+
+            Registers.Address :=
+              Registers.Address + uint64 (Address_Increment);
+            Registers.Line := uint32 (int32 (Registers.Line) + Line_Increment);
+            Registers.Basic_Block    := False;
+            Registers.Prologue_End   := False;
+            Registers.Epilogue_Begin := False;
+            Registers.Is_Row         := True;
+         end;
+      end if;
+
+   exception
+      when Dwarf_Error =>
+
+         --  In case of errors during parse, just stop reading
+
+         Registers.Is_Row := False;
+         Done             := True;
+   end Read_And_Execute_Isn;
+
+   ----------------------
+   -- Set_Load_Address --
+   ----------------------
+
+   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;
+   end Set_Load_Address;
+
+   ------------------
+   -- To_File_Name --
+   ------------------
+
+   function To_File_Name
+     (C    : in out Dwarf_Context;
+      Code :        uint32) return String
+   is
+      Buf : Buffer;
+      J   : uint32;
+
+      Dir_Idx : uint32;
+      pragma Unreferenced (Dir_Idx);
+
+      Mod_Time : uint32;
+      pragma Unreferenced (Mod_Time);
+
+      Length : uint32;
+      pragma Unreferenced (Length);
+
+   begin
+      Seek (C.Lines, C.Prologue.File_Names_Offset);
+
+      --  Find the entry
+
+      J := 0;
+      loop
+         J := J + 1;
+         Read_C_String (C.Lines, Buf);
+
+         if Buf (Buf'First) = 0 then
+            return "???";
+         end if;
+
+         Dir_Idx  := Read_LEB128 (C.Lines);
+         Mod_Time := Read_LEB128 (C.Lines);
+         Length   := Read_LEB128 (C.Lines);
+         exit when J = Code;
+      end loop;
+
+      return To_String (Buf);
+   end To_File_Name;
+
+   -------------------------
+   -- Read_Initial_Length --
+   -------------------------
+
+   procedure Read_Initial_Length
+     (S    : in out Mapped_Stream;
+      Len  :    out Offset;
+      Is64 :    out Boolean)
+   is
+      Len32 : uint32;
+      Len64 : uint64;
+   begin
+      Len32 := Read (S);
+      if Len32 < 16#ffff_fff0# then
+         Is64 := False;
+         Len  := Offset (Len32);
+      elsif Len32 < 16#ffff_ffff# then
+         --  Invalid length
+         raise Constraint_Error;
+      else
+         Is64  := True;
+         Len64 := Read (S);
+         Len   := Offset (Len64);
+      end if;
+   end Read_Initial_Length;
+
+   -------------------------
+   -- Read_Section_Offset --
+   -------------------------
+
+   procedure Read_Section_Offset
+     (S    : in out Mapped_Stream;
+      Len  :    out Offset;
+      Is64 :        Boolean)
+   is
+   begin
+      if Is64 then
+         Len := Offset (uint64'(Read (S)));
+      else
+         Len := Offset (uint32'(Read (S)));
+      end if;
+   end Read_Section_Offset;
+
+   --------------------
+   -- Aranges_Lookup --
+   --------------------
+
+   procedure Aranges_Lookup
+     (C           : in out Dwarf_Context;
+      Addr        :        Address;
+      Info_Offset :    out Offset;
+      Success     :    out Boolean)
+   is
+   begin
+      Seek (C.Aranges, 0);
+
+      while Tell (C.Aranges) < Length (C.Aranges) loop
+         Read_Aranges_Header (C, Info_Offset, Success);
+         exit when not Success;
+
+         loop
+            declare
+               Start : Integer_Address;
+               Len   : Storage_Count;
+            begin
+               Read_Aranges_Entry (C, Start, Len);
+               exit when Start = 0 and Len = 0;
+               if Addr >= To_Address (Start)
+                 and then Addr < To_Address (Start) + Len
+               then
+                  Success := True;
+                  return;
+               end if;
+            end;
+         end loop;
+      end loop;
+      Success := False;
+   end Aranges_Lookup;
+
+   ---------------
+   -- Skip_Form --
+   ---------------
+
+   procedure Skip_Form
+     (S      : in out Mapped_Stream;
+      Form   :        uint32;
+      Is64   :        Boolean;
+      Ptr_Sz :        uint8)
+   is
+      Skip : Offset;
+   begin
+      case Form is
+         when DW_FORM_addr =>
+            Skip := Offset (Ptr_Sz);
+         when DW_FORM_block2 =>
+            Skip := Offset (uint16'(Read (S)));
+         when DW_FORM_block4 =>
+            Skip := Offset (uint32'(Read (S)));
+         when DW_FORM_data2 | DW_FORM_ref2 =>
+            Skip := 2;
+         when DW_FORM_data4 | DW_FORM_ref4 =>
+            Skip := 4;
+         when DW_FORM_data8 | DW_FORM_ref8 | DW_FORM_ref_sig8 =>
+            Skip := 8;
+         when DW_FORM_string =>
+            while uint8'(Read (S)) /= 0 loop
+               null;
+            end loop;
+            return;
+         when DW_FORM_block | DW_FORM_exprloc =>
+            Skip := Offset (uint32'(Read_LEB128 (S)));
+         when DW_FORM_block1 | DW_FORM_ref1 =>
+            Skip := Offset (uint8'(Read (S)));
+         when DW_FORM_data1 | DW_FORM_flag =>
+            Skip := 1;
+         when DW_FORM_sdata =>
+            declare
+               Val : constant int32 := Read_LEB128 (S);
+               pragma Unreferenced (Val);
+            begin
+               return;
+            end;
+         when DW_FORM_strp | DW_FORM_ref_addr | DW_FORM_sec_offset =>
+            Skip := (if Is64 then 8 else 4);
+         when DW_FORM_udata | DW_FORM_ref_udata =>
+            declare
+               Val : constant uint32 := Read_LEB128 (S);
+               pragma Unreferenced (Val);
+            begin
+               return;
+            end;
+         when DW_FORM_flag_present =>
+            return;
+         when DW_FORM_indirect =>
+            raise Constraint_Error;
+         when others =>
+            raise Constraint_Error;
+      end case;
+      Seek (S, Tell (S) + Skip);
+   end Skip_Form;
+
+   -----------------
+   -- Seek_Abbrev --
+   -----------------
+
+   procedure Seek_Abbrev
+     (C             : in out Dwarf_Context;
+      Abbrev_Offset :        Offset;
+      Abbrev_Num    :        uint32)
+   is
+      Num       : uint32;
+      Abbrev    : uint32;
+      Tag       : uint32;
+      Has_Child : uint8;
+      pragma Unreferenced (Abbrev, Tag, Has_Child);
+   begin
+      Seek (C.Abbrev, Abbrev_Offset);
+
+      Num := 1;
+
+      loop
+         exit when Num = Abbrev_Num;
+
+         Abbrev    := Read_LEB128 (C.Abbrev);
+         Tag       := Read_LEB128 (C.Abbrev);
+         Has_Child := Read (C.Abbrev);
+
+         loop
+            declare
+               Name : constant uint32 := Read_LEB128 (C.Abbrev);
+               Form : constant uint32 := Read_LEB128 (C.Abbrev);
+            begin
+               exit when Name = 0 and Form = 0;
+            end;
+         end loop;
+
+         Num := Num + 1;
+      end loop;
+   end Seek_Abbrev;
+
+   -----------------------
+   -- Debug_Info_Lookup --
+   -----------------------
+
+   procedure Debug_Info_Lookup
+     (C           : in out Dwarf_Context;
+      Info_Offset :        Offset;
+      Line_Offset :    out Offset;
+      Success     :    out Boolean)
+   is
+      Unit_Length   : Offset;
+      Is64          : Boolean;
+      Version       : uint16;
+      Abbrev_Offset : Offset;
+      Addr_Sz       : uint8;
+      Abbrev        : uint32;
+      Has_Child     : uint8;
+      pragma Unreferenced (Has_Child);
+   begin
+      Success := False;
+
+      Seek (C.Info, Info_Offset);
+
+      Read_Initial_Length (C.Info, Unit_Length, Is64);
+
+      Version := Read (C.Info);
+      if Version not in 2 .. 4 then
+         return;
+      end if;
+
+      Read_Section_Offset (C.Info, Abbrev_Offset, Is64);
+
+      Addr_Sz := Read (C.Info);
+      if Addr_Sz /= (Address'Size / SSU) then
+         return;
+      end if;
+
+      --  Read DIEs
+
+      loop
+         Abbrev := Read_LEB128 (C.Info);
+         exit when Abbrev /= 0;
+      end loop;
+
+      --  Read abbrev table
+
+      Seek_Abbrev (C, Abbrev_Offset, Abbrev);
+
+      --  First ULEB128 is the abbrev code
+
+      if Read_LEB128 (C.Abbrev) /= Abbrev then
+         --  Ill formed abbrev table
+         return;
+      end if;
+
+      --  Then the tag
+
+      if Read_LEB128 (C.Abbrev) /= uint32'(DW_TAG_Compile_Unit) then
+         --  Expect compile unit
+         return;
+      end if;
+
+      --  Then the has child flag
+
+      Has_Child := Read (C.Abbrev);
+
+      loop
+         declare
+            Name : constant uint32 := Read_LEB128 (C.Abbrev);
+            Form : constant uint32 := Read_LEB128 (C.Abbrev);
+         begin
+            exit when Name = 0 and Form = 0;
+            if Name = DW_AT_Stmt_List then
+               case Form is
+                  when DW_FORM_sec_offset =>
+                     Read_Section_Offset (C.Info, Line_Offset, Is64);
+                  when DW_FORM_data4 =>
+                     Line_Offset := Offset (uint32'(Read (C.Info)));
+                  when DW_FORM_data8 =>
+                     Line_Offset := Offset (uint64'(Read (C.Info)));
+                  when others =>
+                     --  Unhandled form
+                     return;
+               end case;
+
+               Success := True;
+               return;
+            else
+               Skip_Form (C.Info, Form, Is64, Addr_Sz);
+            end if;
+         end;
+      end loop;
+
+      return;
+   end Debug_Info_Lookup;
+
+   -------------------------
+   -- Read_Aranges_Header --
+   -------------------------
+
+   procedure Read_Aranges_Header
+     (C           : in out Dwarf_Context;
+      Info_Offset :    out Offset;
+      Success     :    out Boolean)
+   is
+      Unit_Length : Offset;
+      Is64        : Boolean;
+      Version     : uint16;
+      Sz          : uint8;
+   begin
+      Success := False;
+
+      Read_Initial_Length (C.Aranges, Unit_Length, Is64);
+
+      Version := Read (C.Aranges);
+      if Version /= 2 then
+         return;
+      end if;
+
+      Read_Section_Offset (C.Aranges, Info_Offset, Is64);
+
+      --  Read address_size (ubyte)
+
+      Sz := Read (C.Aranges);
+      if Sz /= (Address'Size / SSU) then
+         return;
+      end if;
+
+      --  Read segment_size (ubyte)
+
+      Sz := Read (C.Aranges);
+      if Sz /= 0 then
+         return;
+      end if;
+
+      --  Handle alignment on twice the address size
+      declare
+         Cur_Off : constant Offset := Tell (C.Aranges);
+         Align   : constant Offset := 2 * Address'Size / SSU;
+         Space   : constant Offset := Cur_Off mod Align;
+      begin
+         if Space /= 0 then
+            Seek (C.Aranges, Cur_Off + Align - Space);
+         end if;
+      end;
+
+      Success := True;
+   end Read_Aranges_Header;
+
+   ------------------------
+   -- Read_Aranges_Entry --
+   ------------------------
+
+   procedure Read_Aranges_Entry
+     (C     : in out Dwarf_Context;
+      Start :    out Integer_Address;
+      Len   :    out Storage_Count)
+   is
+   begin
+      --  Read table
+      if Address'Size = 32 then
+         declare
+            S, L : uint32;
+         begin
+            S     := Read (C.Aranges);
+            L     := Read (C.Aranges);
+            Start := Integer_Address (S);
+            Len   := Storage_Count (L);
+         end;
+      elsif Address'Size = 64 then
+         declare
+            S, L : uint64;
+         begin
+            S     := Read (C.Aranges);
+            L     := Read (C.Aranges);
+            Start := Integer_Address (S);
+            Len   := Storage_Count (L);
+         end;
+      else
+         raise Constraint_Error;
+      end if;
+   end Read_Aranges_Entry;
+
+   ------------------
+   -- Enable_Cache --
+   ------------------
+
+   procedure Enable_Cache (C : in out Dwarf_Context) is
+      Cache : Search_Array_Access;
+   begin
+      --  Phase 1: count number of symbols. Phase 2: fill the cache.
+      declare
+         S               : Object_Symbol;
+         Sz              : uint32;
+         Addr, Prev_Addr : uint32;
+         Nbr_Symbols     : Natural;
+      begin
+         for Phase in 1 .. 2 loop
+            Nbr_Symbols := 0;
+            S           := First_Symbol (C.Obj.all);
+            Prev_Addr   := uint32'Last;
+            while S /= Null_Symbol loop
+               --  Discard symbols whose length is 0
+               Sz := uint32 (Size (S));
+
+               --  Try to filter symbols at the same address. This is a best
+               --  effort as they might not be consecutive.
+               Addr := uint32 (Value (S) - uint64 (C.Low));
+               if Sz > 0 and then Addr /= Prev_Addr then
+                  Nbr_Symbols := Nbr_Symbols + 1;
+                  Prev_Addr   := Addr;
+
+                  if Phase = 2 then
+                     C.Cache (Nbr_Symbols) :=
+                       (First => Addr,
+                        Size  => Sz,
+                        Sym   => uint32 (Off (S)),
+                        Line  => 0);
+                  end if;
+               end if;
+
+               S := Next_Symbol (C.Obj.all, S);
+            end loop;
+
+            if Phase = 1 then
+               --  Allocate the cache
+               Cache   := new Search_Array (1 .. Nbr_Symbols);
+               C.Cache := Cache;
+            end if;
+         end loop;
+         pragma Assert (Nbr_Symbols = C.Cache'Last);
+      end;
+
+      --  Sort the cache.
+      Sort_Search_Array (C.Cache.all);
+
+      --  Set line offsets
+      if not C.Has_Debug then
+         return;
+      end if;
+      declare
+         Info_Offset : Offset;
+         Line_Offset : Offset;
+         Success     : Boolean;
+         Ar_Start    : Integer_Address;
+         Ar_Len      : Storage_Count;
+         Start, Len  : uint32;
+         First, Last : Natural;
+         Mid         : Natural;
+      begin
+         Seek (C.Aranges, 0);
+
+         while Tell (C.Aranges) < Length (C.Aranges) loop
+            Read_Aranges_Header (C, Info_Offset, Success);
+            exit when not Success;
+
+            Debug_Info_Lookup (C, Info_Offset, Line_Offset, Success);
+            exit when not Success;
+
+            --  Read table
+            loop
+               Read_Aranges_Entry (C, Ar_Start, Ar_Len);
+               exit when Ar_Start = 0 and Ar_Len = 0;
+
+               Len   := uint32 (Ar_Len);
+               Start := uint32 (Ar_Start - To_Integer (C.Low));
+
+               --  Search START in the array
+               First := Cache'First;
+               Last  := Cache'Last;
+               Mid := First;  --  In case of array with one element
+               while First < Last loop
+                  Mid := First + (Last - First) / 2;
+                  if Start < Cache (Mid).First then
+                     Last := Mid - 1;
+                  elsif Start >= Cache (Mid).First + Cache (Mid).Size then
+                     First := Mid + 1;
+                  else
+                     exit;
+                  end if;
+               end loop;
+
+               --  Fill info.
+
+               --  There can be overlapping symbols
+               while Mid > Cache'First
+                 and then Cache (Mid - 1).First <= Start
+                 and then Cache (Mid - 1).First + Cache (Mid - 1).Size > Start
+               loop
+                  Mid := Mid - 1;
+               end loop;
+               while Mid <= Cache'Last loop
+                  if Start < Cache (Mid).First + Cache (Mid).Size
+                    and then Start + Len > Cache (Mid).First
+                  then
+                     --  MID is within the bounds
+                     Cache (Mid).Line := uint32 (Line_Offset);
+                  elsif Start + Len <= Cache (Mid).First then
+                     --  Over
+                     exit;
+                  end if;
+                  Mid := Mid + 1;
+               end loop;
+            end loop;
+         end loop;
+      end;
+   end Enable_Cache;
+
+   ----------------------
+   -- Symbolic_Address --
+   ----------------------
+
+   procedure Symbolic_Address
+     (C           : in out Dwarf_Context;
+      Addr        :        Address;
+      Dir_Name    :    out Str_Access;
+      File_Name   :    out Str_Access;
+      Subprg_Name :    out String_Ptr_Len;
+      Line_Num    :    out Natural)
+   is
+      procedure Set_Result (Match : Line_Info_Registers);
+      --  Set results using match
+
+      procedure Set_Result (Match : Line_Info_Registers) is
+         Dir_Idx : uint32;
+         J       : uint32;
+
+         Mod_Time : uint32;
+         pragma Unreferenced (Mod_Time);
+
+         Length : uint32;
+         pragma Unreferenced (Length);
+
+      begin
+         Seek (C.Lines, C.Prologue.File_Names_Offset);
+
+         --  Find the entry
+
+         J := 0;
+         loop
+            J         := J + 1;
+            File_Name := Read_C_String (C.Lines);
+
+            if File_Name (File_Name'First) = ASCII.NUL then
+               --  End of file list, so incorrect entry
+               return;
+            end if;
+
+            Dir_Idx  := Read_LEB128 (C.Lines);
+            Mod_Time := Read_LEB128 (C.Lines);
+            Length   := Read_LEB128 (C.Lines);
+            exit when J = Match.File;
+         end loop;
+
+         if Dir_Idx = 0 then
+            --  No directory
+            Dir_Name := null;
+
+         else
+            Seek (C.Lines, C.Prologue.Includes_Offset);
+
+            J := 0;
+            loop
+               J        := J + 1;
+               Dir_Name := Read_C_String (C.Lines);
+
+               if Dir_Name (Dir_Name'First) = ASCII.NUL then
+                  --  End of directory list, so ill-formed table
+                  return;
+               end if;
+
+               exit when J = Dir_Idx;
+
+            end loop;
+         end if;
+
+         Line_Num := Natural (Match.Line);
+      end Set_Result;
+
+      Addr_Int     : constant Integer_Address := To_Integer (Addr);
+      Previous_Row : Line_Info_Registers;
+      Info_Offset  : Offset;
+      Line_Offset  : Offset;
+      Success      : Boolean;
+      Done         : Boolean;
+      S            : Object_Symbol;
+   begin
+      --  Initialize result
+      Dir_Name    := null;
+      File_Name   := null;
+      Subprg_Name := (null, 0);
+      Line_Num    := 0;
+
+      if C.Cache /= null then
+         --  Look in the cache
+         declare
+            Addr_Off         : constant uint32 := uint32 (Addr - C.Low);
+            First, Last, Mid : Natural;
+         begin
+            First := C.Cache'First;
+            Last  := C.Cache'Last;
+            while First <= Last loop
+               Mid := First + (Last - First) / 2;
+               if Addr_Off < C.Cache (Mid).First then
+                  Last := Mid - 1;
+               elsif Addr_Off >= C.Cache (Mid).First + C.Cache (Mid).Size then
+                  First := Mid + 1;
+               else
+                  exit;
+               end if;
+            end loop;
+            if Addr_Off >= C.Cache (Mid).First
+              and then Addr_Off < C.Cache (Mid).First + C.Cache (Mid).Size
+            then
+               Line_Offset := Offset (C.Cache (Mid).Line);
+               S := Read_Symbol (C.Obj.all, Offset (C.Cache (Mid).Sym));
+               Subprg_Name := Object_Reader.Name (C.Obj.all, S);
+            else
+               --  Not found
+               return;
+            end if;
+         end;
+      else
+         --  Search symbol
+         S := First_Symbol (C.Obj.all);
+         while S /= Null_Symbol loop
+            if Spans (S, uint64 (Addr_Int)) then
+               Subprg_Name := Object_Reader.Name (C.Obj.all, S);
+               exit;
+            end if;
+
+            S := Next_Symbol (C.Obj.all, S);
+         end loop;
+
+         --  Search address in aranges table
+
+         Aranges_Lookup (C, Addr, Info_Offset, Success);
+         if not Success then
+            return;
+         end if;
+
+         --  Search stmt_list in info table
+
+         Debug_Info_Lookup (C, Info_Offset, Line_Offset, Success);
+         if not Success then
+            return;
+         end if;
+      end if;
+
+      Seek (C.Lines, Line_Offset);
+      C.Next_Prologue := 0;
+      Initialize_State_Machine (C);
+      Parse_Prologue (C);
+
+      --  Advance to the first entry
+
+      loop
+         Read_And_Execute_Isn (C, Done);
+
+         if C.Registers.Is_Row then
+            Previous_Row := C.Registers;
+            exit;
+         end if;
+
+         exit when Done;
+      end loop;
+
+      --  Read the rest of the entries
+
+      while Tell (C.Lines) < C.Next_Prologue loop
+         Read_And_Execute_Isn (C, Done);
+
+         if C.Registers.Is_Row then
+            if not Previous_Row.End_Sequence
+              and then Addr_Int >= Integer_Address (Previous_Row.Address)
+              and then Addr_Int < Integer_Address (C.Registers.Address)
+            then
+               Set_Result (Previous_Row);
+               return;
+
+            elsif Addr_Int = Integer_Address (C.Registers.Address) then
+               Set_Result (C.Registers);
+               return;
+            end if;
+
+            Previous_Row := C.Registers;
+         end if;
+
+         exit when Done;
+      end loop;
+   end Symbolic_Address;
+
+   -------------------
+   -- String_Length --
+   -------------------
+
+   function String_Length (Str : Str_Access) return Natural is
+   begin
+      for I in Str'Range loop
+         if Str (I) = ASCII.NUL then
+            return I - Str'First;
+         end if;
+      end loop;
+      return Str'Last;
+   end String_Length;
+
+   ------------------------
+   -- Symbolic_Traceback --
+   ------------------------
+
+   procedure Symbolic_Traceback
+     (Cin          :        Dwarf_Context;
+      Traceback    :        AET.Tracebacks_Array;
+      Suppress_Hex :        Boolean;
+      Symbol_Found : in out Boolean;
+      Res          : in out System.Bounded_Strings.Bounded_String)
+   is
+      use Ada.Characters.Handling;
+      C    : Dwarf_Context := Cin;
+      Addr : Address;
+
+      Dir_Name    : Str_Access;
+      File_Name   : Str_Access;
+      Subprg_Name : String_Ptr_Len;
+      Line_Num    : Natural;
+      Off         : Natural;
+   begin
+      if not C.Has_Debug then
+         Symbol_Found := False;
+         return;
+      else
+         Symbol_Found := True;
+      end if;
+
+      for J in Traceback'Range loop
+         --  If the buffer is full, no need to do any useless work
+         exit when Is_Full (Res);
+
+         Addr := PC_For (Traceback (J));
+         Symbolic_Address
+           (C,
+            To_Address (To_Integer (Addr) + C.Load_Slide),
+            Dir_Name,
+            File_Name,
+            Subprg_Name,
+            Line_Num);
+
+         if File_Name /= null then
+            declare
+               Last   : constant Natural := String_Length (File_Name);
+               Is_Ada : constant Boolean :=
+                 Last > 3
+                 and then
+                   To_Upper (String (File_Name (Last - 3 .. Last - 1))) =
+                   ".AD";
+               --  True if this is an Ada file. This doesn't take into account
+               --  nonstandard file-naming conventions, but that's OK; this is
+               --  purely cosmetic. It covers at least .ads, .adb, and .ada.
+
+               Line_Image : constant String := Natural'Image (Line_Num);
+            begin
+               if Subprg_Name.Len /= 0 then
+                  --  For Ada code, Symbol_Image is in all lower case; we don't
+                  --  have the case from the original source code. But the best
+                  --  guess is Mixed_Case, so convert to that.
+
+                  if Is_Ada then
+                     declare
+                        Symbol_Image : String :=
+                          Object_Reader.Decoded_Ada_Name
+                            (C.Obj.all,
+                             Subprg_Name);
+                     begin
+                        for K in Symbol_Image'Range loop
+                           if K = Symbol_Image'First
+                             or else not
+                             (Is_Letter (Symbol_Image (K - 1))
+                              or else Is_Digit (Symbol_Image (K - 1)))
+                           then
+                              Symbol_Image (K) := To_Upper (Symbol_Image (K));
+                           end if;
+                        end loop;
+                        Append (Res, Symbol_Image);
+                     end;
+                  else
+                     Off := Strip_Leading_Char (C.Obj.all, Subprg_Name);
+
+                     Append
+                       (Res,
+                        String (Subprg_Name.Ptr (Off .. Subprg_Name.Len)));
+                  end if;
+                  Append (Res, ' ');
+               end if;
+
+               Append (Res, "at ");
+               Append (Res, String (File_Name (1 .. Last)));
+               Append (Res, ':');
+               Append (Res, Line_Image (2 .. Line_Image'Last));
+            end;
+         else
+            if Suppress_Hex then
+               Append (Res, "...");
+            else
+               Append_Address (Res, Addr);
+            end if;
+
+            if Subprg_Name.Len > 0 then
+               Off := Strip_Leading_Char (C.Obj.all, Subprg_Name);
+
+               Append (Res, ' ');
+               Append (Res, String (Subprg_Name.Ptr (Off .. Subprg_Name.Len)));
+            end if;
+
+            Append (Res, " at ???");
+         end if;
+
+         Append (Res, ASCII.LF);
+      end loop;
+   end Symbolic_Traceback;
+end System.Dwarf_Lines;