| ------------------------------------------------------------------------------ |
| -- -- |
| -- 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; |