1 ------------------------------------------------------------------------------
3 -- GNAT COMPILER COMPONENTS --
5 -- S Y S T E M . D W A R F _ L I N E S --
9 -- Copyright (C) 2009-2017, Free Software Foundation, Inc. --
11 -- GNAT is free software; you can redistribute it and/or modify it under --
12 -- terms of the GNU General Public License as published by the Free Soft- --
13 -- ware Foundation; either version 3, or (at your option) any later ver- --
14 -- sion. GNAT is distributed in the hope that it will be useful, but WITH- --
15 -- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY --
16 -- or FITNESS FOR A PARTICULAR PURPOSE. --
18 -- As a special exception under Section 7 of GPL version 3, you are granted --
19 -- additional permissions described in the GCC Runtime Library Exception, --
20 -- version 3.1, as published by the Free Software Foundation. --
22 -- You should have received a copy of the GNU General Public License and --
23 -- a copy of the GCC Runtime Library Exception along with this program; --
24 -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see --
25 -- <http://www.gnu.org/licenses/>. --
27 -- GNAT was originally developed by the GNAT team at New York University. --
28 -- Extensive contributions were provided by Ada Core Technologies Inc. --
30 ------------------------------------------------------------------------------
33 -- We must turn polling off for this unit, because otherwise we can get
34 -- elaboration circularities when polling is turned on
36 with Ada
.Characters
.Handling
;
37 with Ada
.Exceptions
.Traceback
; use Ada
.Exceptions
.Traceback
;
38 with Ada
.Unchecked_Deallocation
;
39 with Ada
.Containers
.Generic_Array_Sort
;
41 with Interfaces
; use Interfaces
;
43 with System
; use System
;
44 with System
.Storage_Elements
; use System
.Storage_Elements
;
45 with System
.Address_Image
;
46 with System
.IO
; use System
.IO
;
47 with System
.Object_Reader
; use System
.Object_Reader
;
48 with System
.Traceback_Entries
; use System
.Traceback_Entries
;
49 with System
.Mmap
; use System
.Mmap
;
50 with System
.Bounded_Strings
; use System
.Bounded_Strings
;
52 package body System
.Dwarf_Lines
is
54 SSU
: constant := System
.Storage_Unit
;
56 function String_Length
(Str
: Str_Access
) return Natural;
57 -- Return the length of the C string Str
59 ---------------------------------
60 -- DWARF Parser Implementation --
61 ---------------------------------
63 procedure Read_Initial_Length
64 (S
: in out Mapped_Stream
;
67 -- Read initial length as specified by Dwarf-4 7.2.2
69 procedure Read_Section_Offset
70 (S
: in out Mapped_Stream
;
73 -- Read a section offset, as specified by Dwarf-4 7.4
75 procedure Read_Aranges_Entry
76 (C
: in out Dwarf_Context
;
77 Start
: out Integer_Address
;
78 Len
: out Storage_Count
);
79 -- Read a single .debug_aranges pair
81 procedure Read_Aranges_Header
82 (C
: in out Dwarf_Context
;
83 Info_Offset
: out Offset
;
84 Success
: out Boolean);
85 -- Read .debug_aranges header
87 procedure Aranges_Lookup
88 (C
: in out Dwarf_Context
;
90 Info_Offset
: out Offset
;
91 Success
: out Boolean);
92 -- Search for Addr in .debug_aranges and return offset Info_Offset in
96 (S
: in out Mapped_Stream
;
100 -- Advance offset in S for Form.
102 procedure Seek_Abbrev
103 (C
: in out Dwarf_Context
;
104 Abbrev_Offset
: Offset
;
105 Abbrev_Num
: uint32
);
106 -- Seek to abbrev Abbrev_Num (starting from Abbrev_Offset)
108 procedure Debug_Info_Lookup
109 (C
: in out Dwarf_Context
;
110 Info_Offset
: Offset
;
111 Line_Offset
: out Offset
;
112 Success
: out Boolean);
113 -- Search for stmt_list tag in Info_Offset and set Line_Offset to the
114 -- offset in .debug_lines. Only look at the first DIE, which should be
115 -- a compilation unit.
117 procedure Initialize_Pass
(C
: in out Dwarf_Context
);
118 -- Seek to the first byte of the first prologue and prepare to make a pass
119 -- over the line number entries.
121 procedure Initialize_State_Machine
(C
: in out Dwarf_Context
);
122 -- Set all state machine registers to their specified initial values
124 procedure Parse_Prologue
(C
: in out Dwarf_Context
);
125 -- Decode a DWARF statement program prologue
127 procedure Read_And_Execute_Isn
128 (C
: in out Dwarf_Context
;
130 -- Read an execute a statement program instruction
132 function To_File_Name
133 (C
: in out Dwarf_Context
;
134 Code
: uint32
) return String;
135 -- Extract a file name from the prologue
137 type Callback
is access procedure (C
: in out Dwarf_Context
);
138 procedure For_Each_Row
(C
: in out Dwarf_Context
; F
: Callback
);
139 -- Traverse each .debug_line entry with a callback
141 procedure Dump_Row
(C
: in out Dwarf_Context
);
144 function "<" (Left
, Right
: Search_Entry
) return Boolean;
145 -- For sorting Search_Entry
147 procedure Sort_Search_Array
is new Ada
.Containers
.Generic_Array_Sort
148 (Index_Type
=> Natural,
149 Element_Type
=> Search_Entry
,
150 Array_Type
=> Search_Array
);
152 procedure Symbolic_Address
153 (C
: in out Dwarf_Context
;
155 Dir_Name
: out Str_Access
;
156 File_Name
: out Str_Access
;
157 Subprg_Name
: out String_Ptr_Len
;
158 Line_Num
: out Natural);
159 -- Symbolize one address
161 -----------------------
162 -- DWARF constants --
163 -----------------------
165 -- 6.2.5.2 Standard Opcodes
167 DW_LNS_copy
: constant := 1;
168 DW_LNS_advance_pc
: constant := 2;
169 DW_LNS_advance_line
: constant := 3;
170 DW_LNS_set_file
: constant := 4;
171 DW_LNS_set_column
: constant := 5;
172 DW_LNS_negate_stmt
: constant := 6;
173 DW_LNS_set_basic_block
: constant := 7;
174 DW_LNS_const_add_pc
: constant := 8;
175 DW_LNS_fixed_advance_pc
: constant := 9;
176 DW_LNS_set_prologue_end
: constant := 10;
177 DW_LNS_set_epilogue_begin
: constant := 11;
178 DW_LNS_set_isa
: constant := 12;
180 -- 6.2.5.3 Extended Opcodes
182 DW_LNE_end_sequence
: constant := 1;
183 DW_LNE_set_address
: constant := 2;
184 DW_LNE_define_file
: constant := 3;
186 -- From the DWARF version 4 public review draft
188 DW_LNE_set_discriminator
: constant := 4;
190 -- Attribute encodings
192 DW_TAG_Compile_Unit
: constant := 16#
11#
;
194 DW_AT_Stmt_List
: constant := 16#
10#
;
196 DW_FORM_addr
: constant := 16#
01#
;
197 DW_FORM_block2
: constant := 16#
03#
;
198 DW_FORM_block4
: constant := 16#
04#
;
199 DW_FORM_data2
: constant := 16#
05#
;
200 DW_FORM_data4
: constant := 16#
06#
;
201 DW_FORM_data8
: constant := 16#
07#
;
202 DW_FORM_string
: constant := 16#
08#
;
203 DW_FORM_block
: constant := 16#
09#
;
204 DW_FORM_block1
: constant := 16#
0a#
;
205 DW_FORM_data1
: constant := 16#
0b#
;
206 DW_FORM_flag
: constant := 16#
0c#
;
207 DW_FORM_sdata
: constant := 16#
0d#
;
208 DW_FORM_strp
: constant := 16#
0e#
;
209 DW_FORM_udata
: constant := 16#
0f#
;
210 DW_FORM_ref_addr
: constant := 16#
10#
;
211 DW_FORM_ref1
: constant := 16#
11#
;
212 DW_FORM_ref2
: constant := 16#
12#
;
213 DW_FORM_ref4
: constant := 16#
13#
;
214 DW_FORM_ref8
: constant := 16#
14#
;
215 DW_FORM_ref_udata
: constant := 16#
15#
;
216 DW_FORM_indirect
: constant := 16#
16#
;
217 DW_FORM_sec_offset
: constant := 16#
17#
;
218 DW_FORM_exprloc
: constant := 16#
18#
;
219 DW_FORM_flag_present
: constant := 16#
19#
;
220 DW_FORM_ref_sig8
: constant := 16#
20#
;
226 function "<" (Left
, Right
: Search_Entry
) return Boolean is
228 return Left
.First
< Right
.First
;
235 procedure Close
(C
: in out Dwarf_Context
) is
236 procedure Unchecked_Deallocation
is new Ada
.Unchecked_Deallocation
239 procedure Unchecked_Deallocation
is new Ada
.Unchecked_Deallocation
241 Search_Array_Access
);
251 Unchecked_Deallocation
(C
.Obj
);
253 Unchecked_Deallocation
(C
.Cache
);
260 procedure Dump
(C
: in out Dwarf_Context
) is
262 For_Each_Row
(C
, Dump_Row
'Access);
269 procedure Dump_Row
(C
: in out Dwarf_Context
) is
270 PC
: constant Integer_Address
:= Integer_Address
(C
.Registers
.Address
);
275 Put
(System
.Address_Image
(To_Address
(PC
)));
277 Put
(To_File_Name
(C
, C
.Registers
.File
));
281 Image
: constant String := uint32
'Image (C
.Registers
.Line
);
283 Put_Line
(Image
(2 .. Image
'Last));
289 procedure Dump_Cache
(C
: Dwarf_Context
) is
290 Cache
: constant Search_Array_Access
:= C
.Cache
;
292 Name
: String_Ptr_Len
;
295 Put_Line
("No cache");
298 for I
in Cache
'Range loop
299 Put
(System
.Address_Image
(C
.Low
+ Storage_Count
(Cache
(I
).First
)));
302 (System
.Address_Image
303 (C
.Low
+ Storage_Count
(Cache
(I
).First
+ Cache
(I
).Size
)));
306 (System
.Address_Image
307 (To_Address
(Integer_Address
(Cache
(I
).Line
))));
309 S
:= Read_Symbol
(C
.Obj
.all, Offset
(Cache
(I
).Sym
));
310 Name
:= Object_Reader
.Name
(C
.Obj
.all, S
);
311 Put
(String (Name
.Ptr
(1 .. Name
.Len
)));
320 procedure For_Each_Row
(C
: in out Dwarf_Context
; F
: Callback
) is
327 Read_And_Execute_Isn
(C
, Done
);
329 if C
.Registers
.Is_Row
then
337 ---------------------
338 -- Initialize_Pass --
339 ---------------------
341 procedure Initialize_Pass
(C
: in out Dwarf_Context
) is
344 C
.Next_Prologue
:= 0;
346 Initialize_State_Machine
(C
);
349 ------------------------------
350 -- Initialize_State_Machine --
351 ------------------------------
353 procedure Initialize_State_Machine
(C
: in out Dwarf_Context
) is
360 Is_Stmt
=> C
.Prologue
.Default_Is_Stmt
= 0,
361 Basic_Block
=> False,
362 End_Sequence
=> False,
363 Prologue_End
=> False,
364 Epilogue_Begin
=> False,
367 end Initialize_State_Machine
;
373 function Is_Inside
(C
: Dwarf_Context
; Addr
: Address
) return Boolean is
375 return (Addr
>= To_Address
(To_Integer
(C
.Low
) + C
.Load_Slide
)
376 and Addr
<= To_Address
(To_Integer
(C
.High
) + C
.Load_Slide
));
383 function Low
(C
: Dwarf_Context
) return Address
is
394 C
: out Dwarf_Context
;
395 Success
: out Boolean)
397 Line_Sec
, Info_Sec
, Abbrev_Sec
, Aranges_Sec
: Object_Section
;
400 -- Not a success by default
406 C
.Obj
:= Open
(File_Name
, C
.In_Exception
);
416 Get_Memory_Bounds
(C
.Obj
.all, Lo
, Hi
);
417 C
.Low
:= Address
(Lo
);
418 C
.High
:= Address
(Hi
);
420 -- Create a stream for debug sections
422 if Format
(C
.Obj
.all) = XCOFF32
then
423 Line_Sec
:= Get_Section
(C
.Obj
.all, ".dwline");
424 Abbrev_Sec
:= Get_Section
(C
.Obj
.all, ".dwabrev");
425 Info_Sec
:= Get_Section
(C
.Obj
.all, ".dwinfo");
426 Aranges_Sec
:= Get_Section
(C
.Obj
.all, ".dwarnge");
428 Line_Sec
:= Get_Section
(C
.Obj
.all, ".debug_line");
429 Abbrev_Sec
:= Get_Section
(C
.Obj
.all, ".debug_abbrev");
430 Info_Sec
:= Get_Section
(C
.Obj
.all, ".debug_info");
431 Aranges_Sec
:= Get_Section
(C
.Obj
.all, ".debug_aranges");
434 if Line_Sec
= Null_Section
435 or else Abbrev_Sec
= Null_Section
436 or else Info_Sec
= Null_Section
437 or else Aranges_Sec
= Null_Section
439 C
.Has_Debug
:= False;
443 C
.Lines
:= Create_Stream
(C
.Obj
.all, Line_Sec
);
444 C
.Abbrev
:= Create_Stream
(C
.Obj
.all, Abbrev_Sec
);
445 C
.Info
:= Create_Stream
(C
.Obj
.all, Info_Sec
);
446 C
.Aranges
:= Create_Stream
(C
.Obj
.all, Aranges_Sec
);
448 -- All operations are successful, context is valid
457 procedure Parse_Prologue
(C
: in out Dwarf_Context
) is
460 -- The most recently read character and the one preceding it
463 -- Destination for reads we don't care about
468 First_Byte_Of_Prologue
: Offset
;
469 Last_Byte_Of_Prologue
: Offset
;
471 Max_Op_Per_Insn
: uint8
;
472 pragma Unreferenced
(Max_Op_Per_Insn
);
474 Prologue
: Line_Info_Prologue
renames C
.Prologue
;
477 Tell
(C
.Lines
, First_Byte_Of_Prologue
);
478 Prologue
.Unit_Length
:= Read
(C
.Lines
);
480 C
.Next_Prologue
:= Off
+ Offset
(Prologue
.Unit_Length
);
482 Prologue
.Version
:= Read
(C
.Lines
);
483 Prologue
.Prologue_Length
:= Read
(C
.Lines
);
484 Tell
(C
.Lines
, Last_Byte_Of_Prologue
);
485 Last_Byte_Of_Prologue
:=
486 Last_Byte_Of_Prologue
+ Offset
(Prologue
.Prologue_Length
) - 1;
488 Prologue
.Min_Isn_Length
:= Read
(C
.Lines
);
490 if Prologue
.Version
>= 4 then
491 Max_Op_Per_Insn
:= Read
(C
.Lines
);
494 Prologue
.Default_Is_Stmt
:= Read
(C
.Lines
);
495 Prologue
.Line_Base
:= Read
(C
.Lines
);
496 Prologue
.Line_Range
:= Read
(C
.Lines
);
497 Prologue
.Opcode_Base
:= Read
(C
.Lines
);
499 -- Opcode_Lengths is an array of Opcode_Base bytes specifying the number
500 -- of LEB128 operands for each of the standard opcodes.
502 for J
in 1 .. uint32
(Prologue
.Opcode_Base
- 1) loop
503 Prologue
.Opcode_Lengths
(J
) := Read
(C
.Lines
);
506 -- The include directories table follows. This is a list of null
507 -- terminated strings terminated by a double null. We only store
508 -- its offset for later decoding.
510 Tell
(C
.Lines
, Prologue
.Includes_Offset
);
511 Char
:= Read
(C
.Lines
);
516 Char
:= Read
(C
.Lines
);
517 exit when Char
= 0 and Prev
= 0;
521 -- The file_names table is next. Each record is a null terminated string
522 -- for the file name, an unsigned LEB128 directory index, an unsigned
523 -- LEB128 modification time, and an LEB128 file length. The table is
524 -- terminated by a null byte.
526 Tell
(C
.Lines
, Prologue
.File_Names_Offset
);
531 Read_C_String
(C
.Lines
, Buf
);
532 exit when Buf
(0) = 0;
533 Dummy
:= Read_LEB128
(C
.Lines
); -- Skip the directory index.
534 Dummy
:= Read_LEB128
(C
.Lines
); -- Skip the modification time.
535 Dummy
:= Read_LEB128
(C
.Lines
); -- Skip the file length.
538 -- Check we're where we think we are. This sanity check ensures we think
539 -- the prologue ends where the prologue says it does. It we aren't then
540 -- we've probably gotten out of sync somewhere.
544 if Prologue
.Unit_Length
/= 0
545 and then Off
/= Last_Byte_Of_Prologue
+ 1
547 raise Dwarf_Error
with "Parse error reading DWARF information";
551 --------------------------
552 -- Read_And_Execute_Isn --
553 --------------------------
555 procedure Read_And_Execute_Isn
556 (C
: in out Dwarf_Context
;
560 Extended_Opcode
: uint8
;
561 uint32_Operand
: uint32
;
562 int32_Operand
: int32
;
563 uint16_Operand
: uint16
;
566 Extended_Length
: uint32
;
567 pragma Unreferenced
(Extended_Length
);
569 Obj
: Object_File
renames C
.Obj
.all;
570 Registers
: Line_Info_Registers
renames C
.Registers
;
571 Prologue
: Line_Info_Prologue
renames C
.Prologue
;
575 Registers
.Is_Row
:= False;
577 if Registers
.End_Sequence
then
578 Initialize_State_Machine
(C
);
581 -- If we have reached the next prologue, read it. Beware of possibly
584 -- When testing for the end of section, beware of possible zero padding
585 -- at the end. Bail out as soon as there's not even room for at least a
586 -- DW_LNE_end_sequence, 3 bytes from Off to Off+2. This resolves to
587 -- Off+2 > Last_Offset_Within_Section, that is Off+2 > Section_Length-1,
588 -- or Off+3 > Section_Length.
591 while Off
= C
.Next_Prologue
loop
592 Initialize_State_Machine
(C
);
595 exit when Off
+ 3 > Length
(C
.Lines
);
598 -- Test whether we're done
602 -- We are finished when we either reach the end of the section, or we
603 -- have reached zero padding at the end of the section.
605 if Prologue
.Unit_Length
= 0 or else Off
+ 3 > Length
(C
.Lines
) then
610 -- Read and interpret an instruction
612 Opcode
:= Read
(C
.Lines
);
617 Extended_Length
:= Read_LEB128
(C
.Lines
);
618 Extended_Opcode
:= Read
(C
.Lines
);
620 case Extended_Opcode
is
621 when DW_LNE_end_sequence
=>
623 -- Mark the end of a sequence of source locations
625 Registers
.End_Sequence
:= True;
626 Registers
.Is_Row
:= True;
628 when DW_LNE_set_address
=>
630 -- Set the program counter to a word
632 Registers
.Address
:= Read_Address
(Obj
, C
.Lines
);
634 when DW_LNE_define_file
=>
638 raise Dwarf_Error
with "DWARF operator not implemented";
640 when DW_LNE_set_discriminator
=>
644 int32_Operand
:= Read_LEB128
(C
.Lines
);
648 -- Fail on an unrecognized opcode
650 raise Dwarf_Error
with "DWARF operator not implemented";
655 elsif Opcode
< Prologue
.Opcode_Base
then
658 -- Append a row to the line info matrix
661 Registers
.Basic_Block
:= False;
662 Registers
.Is_Row
:= True;
664 -- Add an unsigned word to the program counter
666 when DW_LNS_advance_pc
=>
667 uint32_Operand
:= Read_LEB128
(C
.Lines
);
670 uint64
(uint32_Operand
* uint32
(Prologue
.Min_Isn_Length
));
672 -- Add a signed word to the current source line
674 when DW_LNS_advance_line
=>
675 int32_Operand
:= Read_LEB128
(C
.Lines
);
677 uint32
(int32
(Registers
.Line
) + int32_Operand
);
679 -- Set the current source file
681 when DW_LNS_set_file
=>
682 uint32_Operand
:= Read_LEB128
(C
.Lines
);
683 Registers
.File
:= uint32_Operand
;
685 -- Set the current source column
687 when DW_LNS_set_column
=>
688 uint32_Operand
:= Read_LEB128
(C
.Lines
);
689 Registers
.Column
:= uint32_Operand
;
691 -- Toggle the "is statement" flag. GCC doesn't seem to set this???
693 when DW_LNS_negate_stmt
=>
694 Registers
.Is_Stmt
:= not Registers
.Is_Stmt
;
696 -- Mark the beginning of a basic block
698 when DW_LNS_set_basic_block
=>
699 Registers
.Basic_Block
:= True;
701 -- Advance the program counter as by the special opcode 255
703 when DW_LNS_const_add_pc
=>
707 (((255 - Prologue
.Opcode_Base
) / Prologue
.Line_Range
) *
708 Prologue
.Min_Isn_Length
);
710 -- Advance the program counter by a constant
712 when DW_LNS_fixed_advance_pc
=>
713 uint16_Operand
:= Read
(C
.Lines
);
715 Registers
.Address
+ uint64
(uint16_Operand
);
717 -- The following are not implemented and ignored
719 when DW_LNS_set_prologue_end
=>
722 when DW_LNS_set_epilogue_begin
=>
725 when DW_LNS_set_isa
=>
728 -- Anything else is an error
731 raise Dwarf_Error
with "DWARF operator not implemented";
734 -- Decode a special opcode. This is a line and address increment encoded
735 -- in a single byte 'special opcode' as described in 6.2.5.1.
739 Address_Increment
: int32
;
740 Line_Increment
: int32
;
743 Opcode
:= Opcode
- Prologue
.Opcode_Base
;
745 -- The adjusted opcode is a uint8 encoding an address increment
746 -- and a signed line increment. The upperbound is allowed to be
747 -- greater than int8'last so we decode using int32 directly to
748 -- prevent overflows.
751 int32
(Opcode
/ Prologue
.Line_Range
) *
752 int32
(Prologue
.Min_Isn_Length
);
754 int32
(Prologue
.Line_Base
) +
755 int32
(Opcode
mod Prologue
.Line_Range
);
758 Registers
.Address
+ uint64
(Address_Increment
);
759 Registers
.Line
:= uint32
(int32
(Registers
.Line
) + Line_Increment
);
760 Registers
.Basic_Block
:= False;
761 Registers
.Prologue_End
:= False;
762 Registers
.Epilogue_Begin
:= False;
763 Registers
.Is_Row
:= True;
770 -- In case of errors during parse, just stop reading
772 Registers
.Is_Row
:= False;
774 end Read_And_Execute_Isn
;
776 ----------------------
777 -- Set_Load_Address --
778 ----------------------
780 procedure Set_Load_Address
(C
: in out Dwarf_Context
; Addr
: Address
) is
782 C
.Load_Slide
:= To_Integer
(Addr
);
783 end Set_Load_Address
;
789 function To_File_Name
790 (C
: in out Dwarf_Context
;
791 Code
: uint32
) return String
797 pragma Unreferenced
(Dir_Idx
);
800 pragma Unreferenced
(Mod_Time
);
803 pragma Unreferenced
(Length
);
806 Seek
(C
.Lines
, C
.Prologue
.File_Names_Offset
);
813 Read_C_String
(C
.Lines
, Buf
);
815 if Buf
(Buf
'First) = 0 then
819 Dir_Idx
:= Read_LEB128
(C
.Lines
);
820 Mod_Time
:= Read_LEB128
(C
.Lines
);
821 Length
:= Read_LEB128
(C
.Lines
);
825 return To_String
(Buf
);
828 -------------------------
829 -- Read_Initial_Length --
830 -------------------------
832 procedure Read_Initial_Length
833 (S
: in out Mapped_Stream
;
841 if Len32
< 16#ffff_fff0#
then
843 Len
:= Offset
(Len32
);
844 elsif Len32
< 16#ffff_ffff#
then
846 raise Constraint_Error
;
850 Len
:= Offset
(Len64
);
852 end Read_Initial_Length
;
854 -------------------------
855 -- Read_Section_Offset --
856 -------------------------
858 procedure Read_Section_Offset
859 (S
: in out Mapped_Stream
;
865 Len
:= Offset
(uint64
'(Read (S)));
867 Len := Offset (uint32'(Read
(S
)));
869 end Read_Section_Offset
;
875 procedure Aranges_Lookup
876 (C
: in out Dwarf_Context
;
878 Info_Offset
: out Offset
;
879 Success
: out Boolean)
884 while Tell
(C
.Aranges
) < Length
(C
.Aranges
) loop
885 Read_Aranges_Header
(C
, Info_Offset
, Success
);
886 exit when not Success
;
890 Start
: Integer_Address
;
893 Read_Aranges_Entry
(C
, Start
, Len
);
894 exit when Start
= 0 and Len
= 0;
895 if Addr
>= To_Address
(Start
)
896 and then Addr
< To_Address
(Start
) + Len
912 (S
: in out Mapped_Stream
;
921 Skip
:= Offset
(Ptr_Sz
);
922 when DW_FORM_block2
=>
923 Skip
:= Offset
(uint16
'(Read (S)));
924 when DW_FORM_block4 =>
925 Skip := Offset (uint32'(Read
(S
)));
926 when DW_FORM_data2 | DW_FORM_ref2
=>
928 when DW_FORM_data4 | DW_FORM_ref4
=>
930 when DW_FORM_data8 | DW_FORM_ref8 | DW_FORM_ref_sig8
=>
932 when DW_FORM_string
=>
933 while uint8
'(Read (S)) /= 0 loop
937 when DW_FORM_block | DW_FORM_exprloc =>
938 Skip := Offset (uint32'(Read_LEB128
(S
)));
939 when DW_FORM_block1 | DW_FORM_ref1
=>
940 Skip
:= Offset
(uint8
'(Read (S)));
941 when DW_FORM_data1 | DW_FORM_flag =>
943 when DW_FORM_sdata =>
945 Val : constant int32 := Read_LEB128 (S);
946 pragma Unreferenced (Val);
950 when DW_FORM_strp | DW_FORM_ref_addr | DW_FORM_sec_offset =>
951 Skip := (if Is64 then 8 else 4);
952 when DW_FORM_udata | DW_FORM_ref_udata =>
954 Val : constant uint32 := Read_LEB128 (S);
955 pragma Unreferenced (Val);
959 when DW_FORM_flag_present =>
961 when DW_FORM_indirect =>
962 raise Constraint_Error;
964 raise Constraint_Error;
966 Seek (S, Tell (S) + Skip);
973 procedure Seek_Abbrev
974 (C : in out Dwarf_Context;
975 Abbrev_Offset : Offset;
982 pragma Unreferenced (Abbrev, Tag, Has_Child);
984 Seek (C.Abbrev, Abbrev_Offset);
989 exit when Num = Abbrev_Num;
991 Abbrev := Read_LEB128 (C.Abbrev);
992 Tag := Read_LEB128 (C.Abbrev);
993 Has_Child := Read (C.Abbrev);
997 Name : constant uint32 := Read_LEB128 (C.Abbrev);
998 Form : constant uint32 := Read_LEB128 (C.Abbrev);
1000 exit when Name = 0 and Form = 0;
1008 -----------------------
1009 -- Debug_Info_Lookup --
1010 -----------------------
1012 procedure Debug_Info_Lookup
1013 (C : in out Dwarf_Context;
1014 Info_Offset : Offset;
1015 Line_Offset : out Offset;
1016 Success : out Boolean)
1018 Unit_Length : Offset;
1021 Abbrev_Offset : Offset;
1025 pragma Unreferenced (Has_Child);
1029 Seek (C.Info, Info_Offset);
1031 Read_Initial_Length (C.Info, Unit_Length, Is64);
1033 Version := Read (C.Info);
1034 if Version not in 2 .. 4 then
1038 Read_Section_Offset (C.Info, Abbrev_Offset, Is64);
1040 Addr_Sz := Read (C.Info);
1041 if Addr_Sz /= (Address'Size / SSU) then
1048 Abbrev := Read_LEB128 (C.Info);
1049 exit when Abbrev /= 0;
1052 -- Read abbrev table
1054 Seek_Abbrev (C, Abbrev_Offset, Abbrev);
1056 -- First ULEB128 is the abbrev code
1058 if Read_LEB128 (C.Abbrev) /= Abbrev then
1059 -- Ill formed abbrev table
1065 if Read_LEB128 (C.Abbrev) /= uint32'(DW_TAG_Compile_Unit
) then
1066 -- Expect compile unit
1070 -- Then the has child flag
1072 Has_Child
:= Read
(C
.Abbrev
);
1076 Name
: constant uint32
:= Read_LEB128
(C
.Abbrev
);
1077 Form
: constant uint32
:= Read_LEB128
(C
.Abbrev
);
1079 exit when Name
= 0 and Form
= 0;
1080 if Name
= DW_AT_Stmt_List
then
1082 when DW_FORM_sec_offset
=>
1083 Read_Section_Offset
(C
.Info
, Line_Offset
, Is64
);
1084 when DW_FORM_data4
=>
1085 Line_Offset
:= Offset
(uint32
'(Read (C.Info)));
1086 when DW_FORM_data8 =>
1087 Line_Offset := Offset (uint64'(Read
(C
.Info
)));
1096 Skip_Form
(C
.Info
, Form
, Is64
, Addr_Sz
);
1102 end Debug_Info_Lookup
;
1104 -------------------------
1105 -- Read_Aranges_Header --
1106 -------------------------
1108 procedure Read_Aranges_Header
1109 (C
: in out Dwarf_Context
;
1110 Info_Offset
: out Offset
;
1111 Success
: out Boolean)
1113 Unit_Length
: Offset
;
1120 Read_Initial_Length
(C
.Aranges
, Unit_Length
, Is64
);
1122 Version
:= Read
(C
.Aranges
);
1123 if Version
/= 2 then
1127 Read_Section_Offset
(C
.Aranges
, Info_Offset
, Is64
);
1129 -- Read address_size (ubyte)
1131 Sz
:= Read
(C
.Aranges
);
1132 if Sz
/= (Address
'Size / SSU
) then
1136 -- Read segment_size (ubyte)
1138 Sz
:= Read
(C
.Aranges
);
1143 -- Handle alignment on twice the address size
1145 Cur_Off
: constant Offset
:= Tell
(C
.Aranges
);
1146 Align
: constant Offset
:= 2 * Address
'Size / SSU
;
1147 Space
: constant Offset
:= Cur_Off
mod Align
;
1150 Seek
(C
.Aranges
, Cur_Off
+ Align
- Space
);
1155 end Read_Aranges_Header
;
1157 ------------------------
1158 -- Read_Aranges_Entry --
1159 ------------------------
1161 procedure Read_Aranges_Entry
1162 (C
: in out Dwarf_Context
;
1163 Start
: out Integer_Address
;
1164 Len
: out Storage_Count
)
1168 if Address
'Size = 32 then
1172 S
:= Read
(C
.Aranges
);
1173 L
:= Read
(C
.Aranges
);
1174 Start
:= Integer_Address
(S
);
1175 Len
:= Storage_Count
(L
);
1177 elsif Address
'Size = 64 then
1181 S
:= Read
(C
.Aranges
);
1182 L
:= Read
(C
.Aranges
);
1183 Start
:= Integer_Address
(S
);
1184 Len
:= Storage_Count
(L
);
1187 raise Constraint_Error
;
1189 end Read_Aranges_Entry
;
1195 procedure Enable_Cache
(C
: in out Dwarf_Context
) is
1196 Cache
: Search_Array_Access
;
1198 -- Phase 1: count number of symbols. Phase 2: fill the cache.
1202 Addr
, Prev_Addr
: uint32
;
1203 Nbr_Symbols
: Natural;
1205 for Phase
in 1 .. 2 loop
1207 S
:= First_Symbol
(C
.Obj
.all);
1208 Prev_Addr
:= uint32
'Last;
1209 while S
/= Null_Symbol
loop
1210 -- Discard symbols whose length is 0
1211 Sz
:= uint32
(Size
(S
));
1213 -- Try to filter symbols at the same address. This is a best
1214 -- effort as they might not be consecutive.
1215 Addr
:= uint32
(Value
(S
) - uint64
(C
.Low
));
1216 if Sz
> 0 and then Addr
/= Prev_Addr
then
1217 Nbr_Symbols
:= Nbr_Symbols
+ 1;
1221 C
.Cache
(Nbr_Symbols
) :=
1224 Sym
=> uint32
(Off
(S
)),
1229 S
:= Next_Symbol
(C
.Obj
.all, S
);
1233 -- Allocate the cache
1234 Cache
:= new Search_Array
(1 .. Nbr_Symbols
);
1238 pragma Assert
(Nbr_Symbols
= C
.Cache
'Last);
1242 Sort_Search_Array
(C
.Cache
.all);
1245 if not C
.Has_Debug
then
1249 Info_Offset
: Offset
;
1250 Line_Offset
: Offset
;
1252 Ar_Start
: Integer_Address
;
1253 Ar_Len
: Storage_Count
;
1254 Start
, Len
: uint32
;
1255 First
, Last
: Natural;
1258 Seek
(C
.Aranges
, 0);
1260 while Tell
(C
.Aranges
) < Length
(C
.Aranges
) loop
1261 Read_Aranges_Header
(C
, Info_Offset
, Success
);
1262 exit when not Success
;
1264 Debug_Info_Lookup
(C
, Info_Offset
, Line_Offset
, Success
);
1265 exit when not Success
;
1269 Read_Aranges_Entry
(C
, Ar_Start
, Ar_Len
);
1270 exit when Ar_Start
= 0 and Ar_Len
= 0;
1272 Len
:= uint32
(Ar_Len
);
1273 Start
:= uint32
(Ar_Start
- To_Integer
(C
.Low
));
1275 -- Search START in the array
1276 First
:= Cache
'First;
1278 Mid
:= First
; -- In case of array with one element
1279 while First
< Last
loop
1280 Mid
:= First
+ (Last
- First
) / 2;
1281 if Start
< Cache
(Mid
).First
then
1283 elsif Start
>= Cache
(Mid
).First
+ Cache
(Mid
).Size
then
1292 -- There can be overlapping symbols
1293 while Mid
> Cache
'First
1294 and then Cache
(Mid
- 1).First
<= Start
1295 and then Cache
(Mid
- 1).First
+ Cache
(Mid
- 1).Size
> Start
1299 while Mid
<= Cache
'Last loop
1300 if Start
< Cache
(Mid
).First
+ Cache
(Mid
).Size
1301 and then Start
+ Len
> Cache
(Mid
).First
1303 -- MID is within the bounds
1304 Cache
(Mid
).Line
:= uint32
(Line_Offset
);
1305 elsif Start
+ Len
<= Cache
(Mid
).First
then
1316 ----------------------
1317 -- Symbolic_Address --
1318 ----------------------
1320 procedure Symbolic_Address
1321 (C
: in out Dwarf_Context
;
1323 Dir_Name
: out Str_Access
;
1324 File_Name
: out Str_Access
;
1325 Subprg_Name
: out String_Ptr_Len
;
1326 Line_Num
: out Natural)
1328 procedure Set_Result
(Match
: Line_Info_Registers
);
1329 -- Set results using match
1331 procedure Set_Result
(Match
: Line_Info_Registers
) is
1336 pragma Unreferenced
(Mod_Time
);
1339 pragma Unreferenced
(Length
);
1342 Seek
(C
.Lines
, C
.Prologue
.File_Names_Offset
);
1349 File_Name
:= Read_C_String
(C
.Lines
);
1351 if File_Name
(File_Name
'First) = ASCII
.NUL
then
1352 -- End of file list, so incorrect entry
1356 Dir_Idx
:= Read_LEB128
(C
.Lines
);
1357 Mod_Time
:= Read_LEB128
(C
.Lines
);
1358 Length
:= Read_LEB128
(C
.Lines
);
1359 exit when J
= Match
.File
;
1367 Seek
(C
.Lines
, C
.Prologue
.Includes_Offset
);
1372 Dir_Name
:= Read_C_String
(C
.Lines
);
1374 if Dir_Name
(Dir_Name
'First) = ASCII
.NUL
then
1375 -- End of directory list, so ill-formed table
1379 exit when J
= Dir_Idx
;
1384 Line_Num
:= Natural (Match
.Line
);
1387 Addr_Int
: constant Integer_Address
:= To_Integer
(Addr
);
1388 Previous_Row
: Line_Info_Registers
;
1389 Info_Offset
: Offset
;
1390 Line_Offset
: Offset
;
1395 -- Initialize result
1398 Subprg_Name
:= (null, 0);
1401 if C
.Cache
/= null then
1402 -- Look in the cache
1404 Addr_Off
: constant uint32
:= uint32
(Addr
- C
.Low
);
1405 First
, Last
, Mid
: Natural;
1407 First
:= C
.Cache
'First;
1408 Last
:= C
.Cache
'Last;
1409 while First
<= Last
loop
1410 Mid
:= First
+ (Last
- First
) / 2;
1411 if Addr_Off
< C
.Cache
(Mid
).First
then
1413 elsif Addr_Off
>= C
.Cache
(Mid
).First
+ C
.Cache
(Mid
).Size
then
1419 if Addr_Off
>= C
.Cache
(Mid
).First
1420 and then Addr_Off
< C
.Cache
(Mid
).First
+ C
.Cache
(Mid
).Size
1422 Line_Offset
:= Offset
(C
.Cache
(Mid
).Line
);
1423 S
:= Read_Symbol
(C
.Obj
.all, Offset
(C
.Cache
(Mid
).Sym
));
1424 Subprg_Name
:= Object_Reader
.Name
(C
.Obj
.all, S
);
1432 S
:= First_Symbol
(C
.Obj
.all);
1433 while S
/= Null_Symbol
loop
1434 if Spans
(S
, uint64
(Addr_Int
)) then
1435 Subprg_Name
:= Object_Reader
.Name
(C
.Obj
.all, S
);
1439 S
:= Next_Symbol
(C
.Obj
.all, S
);
1442 -- Search address in aranges table
1444 Aranges_Lookup
(C
, Addr
, Info_Offset
, Success
);
1449 -- Search stmt_list in info table
1451 Debug_Info_Lookup
(C
, Info_Offset
, Line_Offset
, Success
);
1457 Seek
(C
.Lines
, Line_Offset
);
1458 C
.Next_Prologue
:= 0;
1459 Initialize_State_Machine
(C
);
1462 -- Advance to the first entry
1465 Read_And_Execute_Isn
(C
, Done
);
1467 if C
.Registers
.Is_Row
then
1468 Previous_Row
:= C
.Registers
;
1475 -- Read the rest of the entries
1477 while Tell
(C
.Lines
) < C
.Next_Prologue
loop
1478 Read_And_Execute_Isn
(C
, Done
);
1480 if C
.Registers
.Is_Row
then
1481 if not Previous_Row
.End_Sequence
1482 and then Addr_Int
>= Integer_Address
(Previous_Row
.Address
)
1483 and then Addr_Int
< Integer_Address
(C
.Registers
.Address
)
1485 Set_Result
(Previous_Row
);
1488 elsif Addr_Int
= Integer_Address
(C
.Registers
.Address
) then
1489 Set_Result
(C
.Registers
);
1493 Previous_Row
:= C
.Registers
;
1498 end Symbolic_Address
;
1504 function String_Length
(Str
: Str_Access
) return Natural is
1506 for I
in Str
'Range loop
1507 if Str
(I
) = ASCII
.NUL
then
1508 return I
- Str
'First;
1514 ------------------------
1515 -- Symbolic_Traceback --
1516 ------------------------
1518 procedure Symbolic_Traceback
1519 (Cin
: Dwarf_Context
;
1520 Traceback
: AET
.Tracebacks_Array
;
1521 Suppress_Hex
: Boolean;
1522 Symbol_Found
: in out Boolean;
1523 Res
: in out System
.Bounded_Strings
.Bounded_String
)
1525 use Ada
.Characters
.Handling
;
1526 C
: Dwarf_Context
:= Cin
;
1528 Addr_In_Traceback
: Address
;
1529 Addr_To_Lookup
: Address
;
1531 Dir_Name
: Str_Access
;
1532 File_Name
: Str_Access
;
1533 Subprg_Name
: String_Ptr_Len
;
1537 if not C
.Has_Debug
then
1538 Symbol_Found
:= False;
1541 Symbol_Found
:= True;
1544 for J
in Traceback
'Range loop
1545 -- If the buffer is full, no need to do any useless work
1546 exit when Is_Full
(Res
);
1548 Addr_In_Traceback
:= PC_For
(Traceback
(J
));
1550 Addr_To_Lookup
:= To_Address
1551 (To_Integer
(Addr_In_Traceback
) - C
.Load_Slide
);
1561 if File_Name
/= null then
1563 Last
: constant Natural := String_Length
(File_Name
);
1564 Is_Ada
: constant Boolean :=
1567 To_Upper
(String (File_Name
(Last
- 3 .. Last
- 1))) =
1569 -- True if this is an Ada file. This doesn't take into account
1570 -- nonstandard file-naming conventions, but that's OK; this is
1571 -- purely cosmetic. It covers at least .ads, .adb, and .ada.
1573 Line_Image
: constant String := Natural'Image (Line_Num
);
1575 if Subprg_Name
.Len
/= 0 then
1576 -- For Ada code, Symbol_Image is in all lower case; we don't
1577 -- have the case from the original source code. But the best
1578 -- guess is Mixed_Case, so convert to that.
1582 Symbol_Image
: String :=
1583 Object_Reader
.Decoded_Ada_Name
1587 for K
in Symbol_Image
'Range loop
1588 if K
= Symbol_Image
'First
1590 (Is_Letter
(Symbol_Image
(K
- 1))
1591 or else Is_Digit
(Symbol_Image
(K
- 1)))
1593 Symbol_Image
(K
) := To_Upper
(Symbol_Image
(K
));
1596 Append
(Res
, Symbol_Image
);
1599 Off
:= Strip_Leading_Char
(C
.Obj
.all, Subprg_Name
);
1603 String (Subprg_Name
.Ptr
(Off
.. Subprg_Name
.Len
)));
1608 Append
(Res
, "at ");
1609 Append
(Res
, String (File_Name
(1 .. Last
)));
1611 Append
(Res
, Line_Image
(2 .. Line_Image
'Last));
1614 if Suppress_Hex
then
1615 Append
(Res
, "...");
1617 Append_Address
(Res
, Addr_In_Traceback
);
1620 if Subprg_Name
.Len
> 0 then
1621 Off
:= Strip_Leading_Char
(C
.Obj
.all, Subprg_Name
);
1624 Append
(Res
, String (Subprg_Name
.Ptr
(Off
.. Subprg_Name
.Len
)));
1627 Append
(Res
, " at ???");
1630 Append
(Res
, ASCII
.LF
);
1632 end Symbolic_Traceback
;
1633 end System
.Dwarf_Lines
;