blob: 788be41c99184de131a2422cd6d318b7bd8965e8 [file] [log] [blame]
------------------------------------------------------------------------------
-- --
-- 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-2022, 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. --
-- --
------------------------------------------------------------------------------
with Ada.Characters.Handling;
with Ada.Containers.Generic_Array_Sort;
with Ada.Unchecked_Deallocation;
with Interfaces; use Interfaces;
with System.Address_Image;
with System.Bounded_Strings; use System.Bounded_Strings;
with System.IO; use System.IO;
with System.Mmap; use System.Mmap;
with System.Object_Reader; use System.Object_Reader;
with System.Storage_Elements; use System.Storage_Elements;
package body System.Dwarf_Lines is
SSU : constant := System.Storage_Unit;
function Get_Load_Displacement (C : Dwarf_Context) return Storage_Offset;
-- Return the displacement between the load address present in the binary
-- and the run-time address at which it is loaded (i.e. non-zero for PIE).
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 7.2.2
procedure Read_Section_Offset
(S : in out Mapped_Stream;
Len : out Offset;
Is64 : Boolean);
-- Read a section offset, as specified by 7.4
procedure Read_Entry_Format_Array
(S : in out Mapped_Stream;
A : out Entry_Format_Array;
Len : uint8);
-- Read an entry format array, as specified by 6.2.4.1
procedure Read_Aranges_Entry
(C : in out Dwarf_Context;
Start : out 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 header 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_Header (C : in out Dwarf_Context);
-- Decode a DWARF statement program header
procedure Read_And_Execute_Insn
(C : in out Dwarf_Context;
Done : out Boolean);
-- Read an execute a statement program instruction
function To_File_Name
(C : in out Dwarf_Context;
File : uint32) return String;
-- Extract a file name from the header
type Callback is not null 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 --
-----------------------
-- 3.1.1 Full and Partial Compilation Unit Entries
DW_TAG_Compile_Unit : constant := 16#11#;
DW_AT_Stmt_List : constant := 16#10#;
-- 6.2.4.1 Standard Content Descriptions (DWARF 5)
DW_LNCT_path : constant := 1;
DW_LNCT_directory_index : constant := 2;
-- DW_LNCT_timestamp : constant := 3;
-- DW_LNCT_size : constant := 4;
DW_LNCT_MD5 : constant := 5;
DW_LNCT_lo_user : constant := 16#2000#;
DW_LNCT_hi_user : constant := 16#3fff#;
-- 6.2.5.2 Standard Opcodes
DW_LNS_extended_op : constant := 0;
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;
DW_LNE_set_discriminator : constant := 4;
-- 7.5.5 Classes and Forms
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_strx : constant := 16#1a#;
DW_FORM_addrx : constant := 16#1b#;
DW_FORM_ref_sup4 : constant := 16#1c#;
DW_FORM_strp_sup : constant := 16#1d#;
DW_FORM_data16 : constant := 16#1e#;
DW_FORM_line_strp : constant := 16#1f#;
DW_FORM_ref_sig8 : constant := 16#20#;
DW_FORM_implicit_const : constant := 16#21#;
DW_FORM_loclistx : constant := 16#22#;
DW_FORM_rnglistx : constant := 16#23#;
DW_FORM_ref_sup8 : constant := 16#24#;
DW_FORM_strx1 : constant := 16#25#;
DW_FORM_strx2 : constant := 16#26#;
DW_FORM_strx3 : constant := 16#27#;
DW_FORM_strx4 : constant := 16#28#;
DW_FORM_addrx1 : constant := 16#29#;
DW_FORM_addrx2 : constant := 16#2a#;
DW_FORM_addrx3 : constant := 16#2b#;
DW_FORM_addrx4 : constant := 16#2c#;
---------
-- "<" --
---------
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
declare
E : Search_Entry renames Cache (I);
Base_Address : constant System.Address :=
To_Address (Integer_Address (C.Low + Storage_Count (E.First)));
begin
Put (System.Address_Image (Base_Address));
Put (" - ");
Put (System.Address_Image (Base_Address + Storage_Count (E.Size)));
Put (" l@");
Put (System.Address_Image (To_Address (Integer_Address (E.Line))));
Put (": ");
S := Read_Symbol (C.Obj.all, Offset (E.Sym));
Name := Object_Reader.Name (C.Obj.all, S);
Put (String (Name.Ptr (1 .. Name.Len)));
New_Line;
end;
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_Insn (C, Done);
if C.Registers.Is_Row then
F.all (C);
end if;
exit when Done;
end loop;
end For_Each_Row;
---------------------------
-- Get_Load_Displacement --
---------------------------
function Get_Load_Displacement (C : Dwarf_Context) return Storage_Offset is
begin
if C.Load_Address /= Null_Address then
return C.Load_Address - Address (Get_Load_Address (C.Obj.all));
else
return 0;
end if;
end Get_Load_Displacement;
---------------------
-- Initialize_Pass --
---------------------
procedure Initialize_Pass (C : in out Dwarf_Context) is
begin
Seek (C.Lines, 0);
C.Next_Header := 0;
Initialize_State_Machine (C);
end Initialize_Pass;
------------------------------
-- Initialize_State_Machine --
------------------------------
procedure Initialize_State_Machine (C : in out Dwarf_Context) is
begin
-- Table 6.4: Line number program initial state
C.Registers :=
(Address => 0,
File => 1,
Line => 1,
Column => 0,
Is_Stmt => C.Header.Default_Is_Stmt /= 0,
Basic_Block => False,
End_Sequence => False,
Is_Row => False);
end Initialize_State_Machine;
---------------
-- Is_Inside --
---------------
function Is_Inside (C : Dwarf_Context; Addr : Address) return Boolean is
Disp : constant Storage_Offset := Get_Load_Displacement (C);
begin
return Addr >= C.Low + Disp and then Addr <= C.High + Disp;
end Is_Inside;
-----------------
-- Low_Address --
-----------------
function Low_Address (C : Dwarf_Context) return Address is
begin
return C.Low + Get_Load_Displacement (C);
end Low_Address;
----------
-- Open --
----------
procedure Open
(File_Name : String;
C : out Dwarf_Context;
Success : out Boolean)
is
Abbrev, Aranges, Lines, Info, Line_Str : Object_Section;
Hi, Lo : uint64;
begin
-- Not a success by default
Success := False;
-- Open file with In_Exception set so we can control the failure mode
C.Obj := Open (File_Name, In_Exception => True);
if C.Obj = null then
if C.In_Exception then
return;
else
raise Dwarf_Error with "could not open file";
end if;
end if;
Success := True;
-- Get address bounds for executable code. Note that such code
-- might come from multiple sections.
Get_Xcode_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
Abbrev := Get_Section (C.Obj.all, ".dwabrev");
Aranges := Get_Section (C.Obj.all, ".dwarnge");
Info := Get_Section (C.Obj.all, ".dwinfo");
Lines := Get_Section (C.Obj.all, ".dwline");
Line_Str := Get_Section (C.Obj.all, ".dwlistr");
else
Abbrev := Get_Section (C.Obj.all, ".debug_abbrev");
Aranges := Get_Section (C.Obj.all, ".debug_aranges");
Info := Get_Section (C.Obj.all, ".debug_info");
Lines := Get_Section (C.Obj.all, ".debug_line");
Line_Str := Get_Section (C.Obj.all, ".debug_line_str");
end if;
if Abbrev = Null_Section
or else Aranges = Null_Section
or else Info = Null_Section
or else Lines = Null_Section
then
pragma Annotate
(CodePeer, False_Positive,
"test always true", "codepeer got confused");
C.Has_Debug := False;
return;
end if;
C.Abbrev := Create_Stream (C.Obj.all, Abbrev);
C.Aranges := Create_Stream (C.Obj.all, Aranges);
C.Info := Create_Stream (C.Obj.all, Info);
C.Lines := Create_Stream (C.Obj.all, Lines);
-- The .debug_line_str section may be available in DWARF 5
if Line_Str /= Null_Section then
C.Line_Str := Create_Stream (C.Obj.all, Line_Str);
end if;
-- All operations are successful, context is valid
C.Has_Debug := True;
end Open;
------------------
-- Parse_Header --
------------------
procedure Parse_Header (C : in out Dwarf_Context) is
Header : Line_Info_Header renames C.Header;
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_Header : Offset;
Last_Byte_Of_Header : Offset;
Standard_Opcode_Lengths : Opcode_Length_Array;
pragma Unreferenced (Standard_Opcode_Lengths);
begin
Tell (C.Lines, First_Byte_Of_Header);
Read_Initial_Length (C.Lines, Header.Unit_Length, Header.Is64);
Tell (C.Lines, Off);
C.Next_Header := Off + Header.Unit_Length;
Header.Version := Read (C.Lines);
if Header.Version >= 5 then
Header.Address_Size := Read (C.Lines);
Header.Segment_Selector_Size := Read (C.Lines);
else
Header.Address_Size := 0;
Header.Segment_Selector_Size := 0;
end if;
Header.Header_Length := Read (C.Lines);
Tell (C.Lines, Last_Byte_Of_Header);
Last_Byte_Of_Header :=
Last_Byte_Of_Header + Offset (Header.Header_Length) - 1;
Header.Minimum_Insn_Length := Read (C.Lines);
if Header.Version >= 4 then
Header.Maximum_Op_Per_Insn := Read (C.Lines);
else
Header.Maximum_Op_Per_Insn := 0;
end if;
Header.Default_Is_Stmt := Read (C.Lines);
Header.Line_Base := Read (C.Lines);
Header.Line_Range := Read (C.Lines);
Header.Opcode_Base := Read (C.Lines);
-- Standard_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 .. Integer (Header.Opcode_Base - 1) loop
Standard_Opcode_Lengths (J) := Read (C.Lines);
end loop;
-- The Directories table follows. Up to DWARF 4, this is a list of null
-- terminated strings terminated by a null byte. In DWARF 5, this is a
-- sequence of Directories_Count entries which are encoded as described
-- by the Directory_Entry_Format field. We store its offset for later.
if Header.Version <= 4 then
Tell (C.Lines, Header.Directories);
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;
else
Header.Directory_Entry_Format_Count := Read (C.Lines);
Read_Entry_Format_Array (C.Lines,
Header.Directory_Entry_Format,
Header.Directory_Entry_Format_Count);
Header.Directories_Count := Read_LEB128 (C.Lines);
Tell (C.Lines, Header.Directories);
for J in 1 .. Header.Directories_Count loop
for K in 1 .. Integer (Header.Directory_Entry_Format_Count) loop
Skip_Form (C.Lines,
Header.Directory_Entry_Format (K).Form,
Header.Is64,
Header.Address_Size);
end loop;
end loop;
end if;
-- The File_Names table is next. Up to DWARF 4, this is a list of record
-- containing a null terminated string for the file name, an unsigned
-- LEB128 directory index in the Directories table, an unsigned LEB128
-- modification time, and an unsigned LEB128 for the file length; the
-- table is terminated by a null byte. In DWARF 5, this is a sequence
-- of File_Names_Count entries which are encoded as described by the
-- File_Name_Entry_Format field. We store its offset for later decoding.
if Header.Version <= 4 then
Tell (C.Lines, Header.File_Names);
-- Read the file names
loop
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;
else
Header.File_Name_Entry_Format_Count := Read (C.Lines);
Read_Entry_Format_Array (C.Lines,
Header.File_Name_Entry_Format,
Header.File_Name_Entry_Format_Count);
Header.File_Names_Count := Read_LEB128 (C.Lines);
Tell (C.Lines, Header.File_Names);
for J in 1 .. Header.File_Names_Count loop
for K in 1 .. Integer (Header.File_Name_Entry_Format_Count) loop
Skip_Form (C.Lines,
Header.File_Name_Entry_Format (K).Form,
Header.Is64,
Header.Address_Size);
end loop;
end loop;
end if;
-- Check we're where we think we are. This sanity check ensures we think
-- the header ends where the header says it does. It we aren't, then we
-- have probably gotten out of sync somewhere.
Tell (C.Lines, Off);
if Header.Unit_Length /= 0
and then Off /= Last_Byte_Of_Header + 1
then
raise Dwarf_Error with "parse error reading DWARF information";
end if;
end Parse_Header;
---------------------------
-- Read_And_Execute_Insn --
---------------------------
procedure Read_And_Execute_Insn
(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;
Header : Line_Info_Header renames C.Header;
begin
Done := False;
Registers.Is_Row := False;
if Registers.End_Sequence then
Initialize_State_Machine (C);
end if;
-- If we have reached the next header, read it. Beware of possibly empty
-- blocks.
-- When testing for the end of section, beware of possible zero padding
-- at the end. Bail out as soon as there's not even room for at least a
-- DW_LNE_end_sequence, 3 bytes from Off to Off+2. This resolves to
-- Off+2 > Last_Offset_Within_Section, that is Off+2 > Section_Length-1,
-- or Off+3 > Section_Length.
Tell (C.Lines, Off);
while Off = C.Next_Header loop
Initialize_State_Machine (C);
Parse_Header (C);
Tell (C.Lines, Off);
exit when Off + 3 > 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 Header.Unit_Length = 0 or else Off + 3 > Length (C.Lines) then
Done := True;
return;
end if;
-- Read and interpret an instruction
Opcode := Read (C.Lines);
-- Extended opcodes
if Opcode = DW_LNS_extended_op 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 < Header.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 (Header.Minimum_Insn_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 - Header.Opcode_Base) / Header.Line_Range) *
Header.Minimum_Insn_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 - Header.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 / Header.Line_Range) *
int32 (Header.Minimum_Insn_Length);
Line_Increment :=
int32 (Header.Line_Base) +
int32 (Opcode mod Header.Line_Range);
Registers.Address :=
Registers.Address + uint64 (Address_Increment);
Registers.Line := uint32 (int32 (Registers.Line) + Line_Increment);
Registers.Basic_Block := 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_Insn;
----------------------
-- Set_Load_Address --
----------------------
procedure Set_Load_Address (C : in out Dwarf_Context; Addr : Address) is
begin
C.Load_Address := Addr;
end Set_Load_Address;
------------------
-- To_File_Name --
------------------
function To_File_Name
(C : in out Dwarf_Context;
File : uint32) return String
is
Buf : Buffer;
Off : Offset;
Dir_Idx : uint32;
pragma Unreferenced (Dir_Idx);
Mod_Time : uint32;
pragma Unreferenced (Mod_Time);
Length : uint32;
pragma Unreferenced (Length);
File_Entry_Format : Entry_Format_Array
renames C.Header.File_Name_Entry_Format;
begin
Seek (C.Lines, C.Header.File_Names);
-- Find the entry. Note that, up to DWARF 4, the index is 1-based
-- whereas, in DWARF 5, it is 0-based.
if C.Header.Version <= 4 then
for J in 1 .. File loop
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);
end loop;
-- DWARF 5
else
for J in 0 .. File loop
for K in 1 .. Integer (C.Header.File_Name_Entry_Format_Count) loop
if File_Entry_Format (K).C_Type = DW_LNCT_path then
case File_Entry_Format (K).Form is
when DW_FORM_string =>
Read_C_String (C.Lines, Buf);
when DW_FORM_line_strp =>
Read_Section_Offset (C.Lines, Off, C.Header.Is64);
if J = File then
Seek (C.Line_Str, Off);
Read_C_String (C.Line_Str, Buf);
end if;
when others =>
raise Dwarf_Error with "DWARF form not implemented";
end case;
else
Skip_Form (C.Lines,
File_Entry_Format (K).Form,
C.Header.Is64,
C.Header.Address_Size);
end if;
end loop;
end loop;
end if;
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;
-----------------------------
-- Read_Entry_Format_Array --
-----------------------------
procedure Read_Entry_Format_Array
(S : in out Mapped_Stream;
A : out Entry_Format_Array;
Len : uint8)
is
C_Type, Form : uint32;
N : Integer;
begin
N := A'First;
for J in 1 .. Len loop
C_Type := Read_LEB128 (S);
Form := Read_LEB128 (S);
case C_Type is
when DW_LNCT_path .. DW_LNCT_MD5 =>
if N not in A'Range then
raise Dwarf_Error with "duplicate DWARF content type";
end if;
A (N) := (C_Type, Form);
N := N + 1;
when DW_LNCT_lo_user .. DW_LNCT_hi_user =>
null;
when others =>
raise Dwarf_Error with "DWARF content type not implemented";
end case;
end loop;
end Read_Entry_Format_Array;
--------------------
-- Aranges_Lookup --
--------------------
procedure Aranges_Lookup
(C : in out Dwarf_Context;
Addr : Address;
Info_Offset : out Offset;
Success : out Boolean)
is
begin
Info_Offset := 0;
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 : Address;
Len : Storage_Count;
begin
Read_Aranges_Entry (C, Start, Len);
exit when Start = 0 and Len = 0;
if Addr >= Start
and then Addr < 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
-- 7.5.5 Classes and Forms
case Form is
when DW_FORM_addr =>
Skip := Offset (Ptr_Sz);
when DW_FORM_block1 =>
Skip := Offset (uint8'(Read (S)));
when DW_FORM_block2 =>
Skip := Offset (uint16'(Read (S)));
when DW_FORM_block4 =>
Skip := Offset (uint32'(Read (S)));
when DW_FORM_block | DW_FORM_exprloc =>
Skip := Offset (uint32'(Read_LEB128 (S)));
when DW_FORM_addrx1
| DW_FORM_data1
| DW_FORM_flag
| DW_FORM_ref1
| DW_FORM_strx1
=>
Skip := 1;
when DW_FORM_addrx2
| DW_FORM_data2
| DW_FORM_ref2
| DW_FORM_strx2
=>
Skip := 2;
when DW_FORM_addrx3 | DW_FORM_strx3 =>
Skip := 3;
when DW_FORM_addrx4
| DW_FORM_data4
| DW_FORM_ref4
| DW_FORM_ref_sup4
| DW_FORM_strx4
=>
Skip := 4;
when DW_FORM_data8
| DW_FORM_ref8
| DW_FORM_ref_sup8
| DW_FORM_ref_sig8
=>
Skip := 8;
when DW_FORM_data16 =>
Skip := 16;
when DW_FORM_sdata =>
declare
Val : constant int32 := Read_LEB128 (S);
pragma Unreferenced (Val);
begin
return;
end;
when DW_FORM_addrx
| DW_FORM_loclistx
| DW_FORM_ref_udata
| DW_FORM_rnglistx
| DW_FORM_strx
| DW_FORM_udata
=>
declare
Val : constant uint32 := Read_LEB128 (S);
pragma Unreferenced (Val);
begin
return;
end;
when DW_FORM_flag_present | DW_FORM_implicit_const =>
return;
when DW_FORM_ref_addr
| DW_FORM_sec_offset
| DW_FORM_strp
| DW_FORM_line_strp
| DW_FORM_strp_sup
=>
Skip := (if Is64 then 8 else 4);
when DW_FORM_string =>
while uint8'(Read (S)) /= 0 loop
null;
end loop;
return;
when DW_FORM_indirect =>
raise Dwarf_Error with "DW_FORM_indirect not implemented";
when others =>
raise Dwarf_Error with "DWARF form not implemented";
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
Abbrev : uint32;
Tag : uint32;
Has_Child : uint8;
pragma Unreferenced (Tag, Has_Child);
begin
Seek (C.Abbrev, Abbrev_Offset);
-- 7.5.3 Abbreviations Tables
loop
Abbrev := Read_LEB128 (C.Abbrev);
exit when Abbrev = Abbrev_Num;
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);
Cst : int32;
pragma Unreferenced (Cst);
begin
-- DW_FORM_implicit_const takes its value from the table
if Form = DW_FORM_implicit_const then
Cst := Read_LEB128 (C.Abbrev);
end if;
exit when Name = 0 and then Form = 0;
end;
end loop;
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);
Unit_Type : uint8;
pragma Unreferenced (Unit_Type);
begin
Line_Offset := 0;
Success := False;
Seek (C.Info, Info_Offset);
-- 7.5.1.1 Compilation Unit Header
Read_Initial_Length (C.Info, Unit_Length, Is64);
Version := Read (C.Info);
if Version >= 5 then
Unit_Type := Read (C.Info);
Addr_Sz := Read (C.Info);
if Addr_Sz /= (Address'Size / SSU) then
return;
end if;
Read_Section_Offset (C.Info, Abbrev_Offset, Is64);
elsif Version >= 2 then
Read_Section_Offset (C.Info, Abbrev_Offset, Is64);
Addr_Sz := Read (C.Info);
if Addr_Sz /= (Address'Size / SSU) then
return;
end if;
else
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);
-- Then the tag
if Read_LEB128 (C.Abbrev) /= uint32'(DW_TAG_Compile_Unit) then
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;
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;
Info_Offset := 0;
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 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 := 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 := 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;
Val : uint64;
Xcode_Low : constant uint64 := uint64 (C.Low);
Xcode_High : constant uint64 := uint64 (C.High);
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 of length 0 or located outside of the
-- execution code section outer boundaries.
Sz := uint32 (Size (S));
Val := Value (S);
if Sz > 0
and then Val >= Xcode_Low
and then Val <= Xcode_High
then
Addr := uint32 (Val - Xcode_Low);
-- Try to filter symbols at the same address. This is a best
-- effort as they might not be consecutive.
if 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;
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 : 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 = Null_Address and Ar_Len = 0;
Len := uint32 (Ar_Len);
Start := uint32 (Ar_Start - 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;
Off : Offset;
Mod_Time : uint32;
pragma Unreferenced (Mod_Time);
Length : uint32;
pragma Unreferenced (Length);
Directory_Entry_Format : Entry_Format_Array
renames C.Header.Directory_Entry_Format;
File_Entry_Format : Entry_Format_Array
renames C.Header.File_Name_Entry_Format;
begin
Seek (C.Lines, C.Header.File_Names);
Dir_Idx := 0;
-- Find the entry. Note that, up to DWARF 4, the index is 1-based
-- whereas, in DWARF 5, it is 0-based.
if C.Header.Version <= 4 then
for J in 1 .. Match.File loop
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);
end loop;
if Dir_Idx = 0 then
-- No directory
Dir_Name := null;
else
Seek (C.Lines, C.Header.Directories);
for J in 1 .. Dir_Idx loop
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;
end loop;
end if;
-- DWARF 5
else
for J in 0 .. Match.File loop
for K in 1 .. Integer (C.Header.File_Name_Entry_Format_Count)
loop
if File_Entry_Format (K).C_Type = DW_LNCT_path then
case File_Entry_Format (K).Form is
when DW_FORM_string =>
File_Name := Read_C_String (C.Lines);
when DW_FORM_line_strp =>
Read_Section_Offset (C.Lines, Off, C.Header.Is64);
if J = Match.File then
Seek (C.Line_Str, Off);
File_Name := Read_C_String (C.Line_Str);
end if;
when others =>
raise Dwarf_Error with "DWARF form not implemented";
end case;
elsif File_Entry_Format (K).C_Type = DW_LNCT_directory_index
then
case File_Entry_Format (K).Form is
when DW_FORM_data1 =>
Dir_Idx := uint32 (uint8'(Read (C.Lines)));
when DW_FORM_data2 =>
Dir_Idx := uint32 (uint16'(Read (C.Lines)));
when DW_FORM_udata =>
Dir_Idx := Read_LEB128 (C.Lines);
when others =>
raise Dwarf_Error with
"invalid DWARF form for DW_LNCT_directory_index";
end case;
else
Skip_Form (C.Lines,
File_Entry_Format (K).Form,
C.Header.Is64,
C.Header.Address_Size);
end if;
end loop;
end loop;
Seek (C.Lines, C.Header.Directories);
for J in 0 .. Dir_Idx loop
for K in 1 .. Integer (C.Header.Directory_Entry_Format_Count)
loop
if Directory_Entry_Format (K).C_Type = DW_LNCT_path then
case Directory_Entry_Format (K).Form is
when DW_FORM_string =>
Dir_Name := Read_C_String (C.Lines);
when DW_FORM_line_strp =>
Read_Section_Offset (C.Lines, Off, C.Header.Is64);
if J = Dir_Idx then
Seek (C.Line_Str, Off);
Dir_Name := Read_C_String (C.Line_Str);
end if;
when others =>
raise Dwarf_Error with "DWARF form not implemented";
end case;
else
Skip_Form (C.Lines,
Directory_Entry_Format (K).Form,
C.Header.Is64,
C.Header.Address_Size);
end if;
end loop;
end loop;
end if;
Line_Num := Natural (Match.Line);
end Set_Result;
Addr_Int : constant uint64 := uint64 (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;
-- Look up the symbol in the cache
if C.Cache /= null then
declare
Addr_Off : constant uint32 := uint32 (Addr - C.Low);
First, Last, Mid : Natural;
begin
First := C.Cache'First;
Last := C.Cache'Last;
Mid := First;
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
return;
end if;
end;
-- Search for the symbol in the binary
else
S := First_Symbol (C.Obj.all);
while S /= Null_Symbol loop
if Spans (S, 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_Header := 0;
Initialize_State_Machine (C);
Parse_Header (C);
Previous_Row.Line := 0;
-- Advance to the first entry
loop
Read_And_Execute_Insn (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_Header loop
Read_And_Execute_Insn (C, Done);
if C.Registers.Is_Row then
if not Previous_Row.End_Sequence
and then Addr_Int >= Previous_Row.Address
and then Addr_Int < C.Registers.Address
then
Set_Result (Previous_Row);
return;
elsif Addr_Int = 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 : STE.Tracebacks_Array;
Suppress_Hex : Boolean;
Symbol_Found : out Boolean;
Res : in out System.Bounded_Strings.Bounded_String)
is
use Ada.Characters.Handling;
C : Dwarf_Context := Cin;
Addr_In_Traceback : 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_In_Traceback := STE.PC_For (Traceback (J));
Symbolic_Address
(C,
Addr_In_Traceback - Get_Load_Displacement (C),
Dir_Name,
File_Name,
Subprg_Name,
Line_Num);
-- If we're not requested to suppress hex addresses, emit it now.
if not Suppress_Hex then
Append_Address (Res, Addr_In_Traceback);
Append (Res, ' ');
end if;
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;
else
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 Subprg_Name.Len > 0 then
Off := Strip_Leading_Char (C.Obj.all, Subprg_Name);
Append (Res, String (Subprg_Name.Ptr (Off .. Subprg_Name.Len)));
else
Append (Res, "???");
end if;
Append (Res, " at ???");
end if;
Append (Res, ASCII.LF);
end loop;
end Symbolic_Traceback;
end System.Dwarf_Lines;