Browse Source

+ write also the function name in stack backtraces when dwarf debugging info is used, resolves #17574

git-svn-id: trunk@33228 -
florian 9 years ago
parent
commit
5109ad9c55
1 changed files with 287 additions and 7 deletions
  1. 287 7
      rtl/inc/lnfodwrf.pp

+ 287 - 7
rtl/inc/lnfodwrf.pp

@@ -55,7 +55,7 @@ uses
 
 
 {$MACRO ON}
 {$MACRO ON}
 
 
-//{$DEFINE DEBUG_DWARF_PARSER}
+{ $DEFINE DEBUG_DWARF_PARSER}
 {$ifdef DEBUG_DWARF_PARSER}
 {$ifdef DEBUG_DWARF_PARSER}
   {$define DEBUG_WRITELN := WriteLn}
   {$define DEBUG_WRITELN := WriteLn}
   {$define DEBUG_COMMENT :=  }
   {$define DEBUG_COMMENT :=  }
@@ -78,8 +78,14 @@ var
   EBuf: Array [0..EBUF_SIZE-1] of Byte;
   EBuf: Array [0..EBUF_SIZE-1] of Byte;
   EBufCnt, EBufPos: Integer;
   EBufCnt, EBufPos: Integer;
   { the offset and size of the DWARF debug_line section in the file }
   { the offset and size of the DWARF debug_line section in the file }
-  DwarfOffset : longint;
-  DwarfSize : longint;
+  Dwarf_Debug_Line_Section_Offset,
+  Dwarf_Debug_Line_Section_Size,
+  { the offset and size of the DWARF debug_info section in the file }
+  Dwarf_Debug_Info_Section_Offset,
+  Dwarf_Debug_Info_Section_Size,
+{ the offset and size of the DWARF debug_abbrev section in the file }
+  Dwarf_Debug_Abbrev_Section_Offset,
+  Dwarf_Debug_Abbrev_Section_Size : longint;
 
 
 { DWARF 2 default opcodes}
 { DWARF 2 default opcodes}
 const
 const
@@ -101,6 +107,28 @@ const
   DW_LNS_SET_EPILOGUE_BEGIN = 11;
   DW_LNS_SET_EPILOGUE_BEGIN = 11;
   DW_LNS_SET_ISA = 12;
   DW_LNS_SET_ISA = 12;
 
 
+  DW_FORM_addr = $1;
+  DW_FORM_block2 = $3;
+  DW_FORM_block4 = $4;
+  DW_FORM_data2 = $5;
+  DW_FORM_data4 = $6;
+  DW_FORM_data8 = $7;
+  DW_FORM_string = $8;
+  DW_FORM_block = $9;
+  DW_FORM_block1 = $a;
+  DW_FORM_data1 = $b;
+  DW_FORM_flag = $c;
+  DW_FORM_sdata = $d;
+  DW_FORM_strp = $e;
+  DW_FORM_udata = $f;
+  DW_FORM_ref_addr = $10;
+  DW_FORM_ref1 = $11;
+  DW_FORM_ref2 = $12;
+  DW_FORM_ref4 = $13;
+  DW_FORM_ref8 = $14;
+  DW_FORM_ref_udata = $15;
+  DW_FORM_indirect = $16;
+
 type
 type
   { state record for the line info state machine }
   { state record for the line info state machine }
   TMachineState = record
   TMachineState = record
@@ -142,6 +170,21 @@ type
     opcode_base : Byte;
     opcode_base : Byte;
   end;
   end;
 
 
+  TDebugInfoProgramHeader64 = packed record
+    magic : DWord;
+    unit_length : QWord;
+    version : Word;
+    debug_abbrev_offset : QWord;
+    address_size : Byte;
+  end;
+
+  TDebugInfoProgramHeader32= packed record
+    unit_length : DWord;
+    version : Word;
+    debug_abbrev_offset : DWord;
+    address_size : Byte;
+  end;
+
 {---------------------------------------------------------------------------
 {---------------------------------------------------------------------------
  I/O utility functions
  I/O utility functions
 ---------------------------------------------------------------------------}
 ---------------------------------------------------------------------------}
@@ -204,10 +247,15 @@ begin
 
 
   // Find debug data section
   // Find debug data section
   e.processaddress:=ptruint(baseaddr)-e.processaddress;
   e.processaddress:=ptruint(baseaddr)-e.processaddress;
-  if FindExeSection(e,'.debug_line',dwarfoffset,dwarfsize) then
+  if FindExeSection(e,'.debug_line',Dwarf_Debug_Line_Section_offset,dwarf_Debug_Line_Section_size) and
+    FindExeSection(e,'.debug_info',Dwarf_Debug_Info_Section_offset,dwarf_Debug_Info_Section_size) and
+    FindExeSection(e,'.debug_abbrev',Dwarf_Debug_Abbrev_Section_offset,dwarf_Debug_Abbrev_Section_size) then
   begin
   begin
     lastopendwarf:=true;
     lastopendwarf:=true;
     OpenDwarf:=true;
     OpenDwarf:=true;
+    DEBUG_WRITELN('.debug_line starts at offset $',hexstr(Dwarf_Debug_Line_Section_offset,8),' with a size of ',Dwarf_Debug_Line_Section_Size,' Bytes');
+    DEBUG_WRITELN('.debug_info starts at offset $',hexstr(Dwarf_Debug_Info_Section_offset,8),' with a size of ',Dwarf_Debug_Info_Section_Size,' Bytes');
+    DEBUG_WRITELN('.debug_abbrev starts at offset $',hexstr(Dwarf_Debug_Abbrev_Section_offset,8),' with a size of ',Dwarf_Debug_Abbrev_Section_Size,' Bytes');
   end
   end
   else
   else
     CloseExeFile(e);
     CloseExeFile(e);
@@ -767,6 +815,226 @@ begin
   end;
   end;
 end;
 end;
 
 
+
+var
+  Abbrev_Offsets : array of QWord;
+  Abbrev_Tags : array of QWord;
+  Abbrev_Children : array of Byte;
+  Abbrev_Attrs : array of array of record attr,form : QWord; end;
+
+procedure ReadAbbrevTable;
+  var
+   i : PtrInt;
+   tag,
+   nr,
+   attr,
+   form,
+   PrevHigh : Int64;
+  begin
+    DEBUG_WRITELN('Starting to read abbrev. section at $',hexstr(Dwarf_Debug_Abbrev_Section_Offset+Pos,16));
+    repeat
+      nr:=ReadULEB128;
+      if nr=0 then
+        break;
+
+      if nr>high(Abbrev_Offsets) then
+        begin
+          SetLength(Abbrev_Offsets,nr+1024);
+          SetLength(Abbrev_Tags,nr+1024);
+          SetLength(Abbrev_Attrs,nr+1024);
+          SetLength(Abbrev_Children,nr+1024);
+        end;
+
+      Abbrev_Offsets[nr]:=Pos;
+
+      { skip tag }
+      tag:=ReadULEB128;
+      Abbrev_Tags[nr]:=tag;
+      DEBUG_WRITELN('Abbrev ',nr,' at offset ',Pos,' has tag $',hexstr(tag,4));
+      { children }
+      Abbrev_Children[nr]:=ReadNext;
+      i:=0;
+      repeat
+        attr:=ReadULEB128;
+        form:=ReadULEB128;
+        if attr<>0 then
+          begin
+            SetLength(Abbrev_Attrs[nr],i+1);
+            Abbrev_Attrs[nr][i].attr:=attr;
+            Abbrev_Attrs[nr][i].form:=form;
+          end;
+        inc(i);
+      until attr=0;
+      DEBUG_WRITELN('Abbrev ',nr,' has ',Length(Abbrev_Attrs[nr]),' attributes');
+    until false;
+  end;
+
+
+
+function ParseCompilationUnitForFunctionName(const addr : PtrUInt; const file_offset : QWord;
+  var func : String; var found : Boolean) : QWord;
+var
+  state : TMachineState;
+  { we need both headers on the stack, although we only use the 64 bit one internally }
+  header64 : TDebugInfoProgramHeader64;
+  header32 : TDebugInfoProgramHeader32;
+  isdwarf64 : boolean;
+  abbrev,
+  high_pc,
+  low_pc : QWord;
+  temp_length : DWord;
+  unit_length : QWord;
+  i : PtrUInt;
+  name : String;
+  level : Integer;
+
+procedure SkipAttr(form : QWord);
+  var
+    dummy : array[0..7] of byte;
+  begin
+    case form of
+      DW_FORM_addr:
+        ReadNext(dummy,header64.address_size);
+      DW_FORM_block2:
+        ReadNext(dummy,2);
+      DW_FORM_block4:
+        ReadNext(dummy,4);
+      DW_FORM_data2:
+        ReadNext(dummy,2);
+      DW_FORM_data4:
+        ReadNext(dummy,4);
+      DW_FORM_data8:
+        ReadNext(dummy,8);
+      DW_FORM_string:
+        ReadString;
+      DW_FORM_block:
+        ReadULEB128;
+      DW_FORM_block1,
+      DW_FORM_data1,
+      DW_FORM_flag:
+        ReadNext(dummy,1);
+      DW_FORM_sdata:
+        ReadLEB128;
+      DW_FORM_ref_addr,
+      DW_FORM_strp:
+        if isdwarf64 then
+          ReadNext(dummy,8)
+        else
+          ReadNext(dummy,4);
+      DW_FORM_udata:
+        ReadULEB128;
+      DW_FORM_ref1:
+        ReadNext(dummy,1);
+      DW_FORM_ref2:
+        ReadNext(dummy,2);
+      DW_FORM_ref4:
+        ReadNext(dummy,4);
+      DW_FORM_ref8:
+        ReadNext(dummy,8);
+      DW_FORM_ref_udata:
+        ReadULEB128;
+      DW_FORM_indirect:
+        SkipAttr(ReadULEB128);
+      else
+        begin
+          writeln(stderr,'Internal error: unknown dwarf form: $',hexstr(form,2));
+          ReadNext;
+          exit;
+        end;
+    end;
+  end;
+
+begin
+  found := false;
+
+  ReadNext(temp_length, sizeof(temp_length));
+  if (temp_length <> $ffffffff) then begin
+    unit_length := temp_length + sizeof(temp_length)
+  end else begin
+    ReadNext(unit_length, sizeof(unit_length));
+    inc(unit_length, 12);
+  end;
+
+  ParseCompilationUnitForFunctionName := file_offset + unit_length;
+
+  Init(file_offset, unit_length);
+
+  DEBUG_WRITELN('Unit length: ', unit_length);
+  if (temp_length <> $ffffffff) then begin
+    DEBUG_WRITELN('32 bit DWARF detected');
+    ReadNext(header32, sizeof(header32));
+    header64.magic := $ffffffff;
+    header64.unit_length := header32.unit_length;
+    header64.version := header32.version;
+    header64.debug_abbrev_offset := header32.debug_abbrev_offset;
+    header64.address_size := header32.address_size;
+    isdwarf64:=false;
+  end else begin
+    DEBUG_WRITELN('64 bit DWARF detected');
+    ReadNext(header64, sizeof(header64));
+    isdwarf64:=true;
+  end;
+
+  abbrev:=ReadULEB128;
+  level:=0;
+  while (abbrev <> 0) and (not found) do
+    begin
+      DEBUG_WRITELN('Next abbrev: ',abbrev);
+      if Abbrev_Children[abbrev]<>0 then
+        inc(level);
+      if Abbrev_Tags[abbrev]=$2e then
+        begin
+          low_pc:=1;
+          high_pc:=0;
+          name:='';
+          for i:=0 to high(Abbrev_Attrs[abbrev]) do
+            begin
+              { DW_AT_low_pc }
+              if (Abbrev_Attrs[abbrev][i].attr=$11) and
+               (Abbrev_Attrs[abbrev][i].form=DW_FORM_addr) then
+                begin
+                  low_pc:=0;
+                  ReadNext(low_pc,header64.address_size);
+                end
+              { DW_AT_high_pc }
+              else if (Abbrev_Attrs[abbrev][i].attr=$12) and
+               (Abbrev_Attrs[abbrev][i].form=DW_FORM_addr) then
+                begin
+                  high_pc:=0;
+                  ReadNext(high_pc,header64.address_size);
+                end
+              { DW_AT_name }
+              else if (Abbrev_Attrs[abbrev][i].attr=$3) and
+                { avoid that we accidently read an DW_FORM_strp entry accidently }
+                (Abbrev_Attrs[abbrev][i].form=DW_FORM_string) then
+                begin
+                  name:=ReadString;
+                end
+              else
+                SkipAttr(Abbrev_Attrs[abbrev][i].form);
+            end;
+          DEBUG_WRITELN('Got DW_TAG_subprogram with low pc = $',hexStr(low_pc,header64.address_size*2),', high pc = $',hexStr(high_pc,header64.address_size*2),', name = ',name);
+          if (addr>low_pc) and (addr<high_pc) then
+            begin
+              found:=true;
+              func:=name;
+            end;
+        end
+      else
+        begin
+          for i:=0 to high(Abbrev_Attrs[abbrev]) do
+            SkipAttr(Abbrev_Attrs[abbrev][i].form);
+        end;
+      abbrev:=ReadULEB128;
+      while (level>0) and (abbrev=0) do
+        begin
+          dec(level);
+          abbrev:=ReadULEB128;
+        end;
+    end;
+end;
+
+
 function GetLineInfo(addr : ptruint; var func, source : string; var line : longint) : boolean;
 function GetLineInfo(addr : ptruint; var func, source : string; var line : longint) : boolean;
 var
 var
   current_offset : QWord;
   current_offset : QWord;
@@ -777,7 +1045,6 @@ var
 begin
 begin
   func := '';
   func := '';
   source := '';
   source := '';
-  found := false;
   GetLineInfo:=false;
   GetLineInfo:=false;
 
 
   if not OpenDwarf(pointer(addr)) then
   if not OpenDwarf(pointer(addr)) then
@@ -785,15 +1052,28 @@ begin
 
 
   addr := addr - e.processaddress;
   addr := addr - e.processaddress;
 
 
-  current_offset := DwarfOffset;
-  end_offset := DwarfOffset + DwarfSize;
+  current_offset := Dwarf_Debug_Line_Section_Offset;
+  end_offset := Dwarf_Debug_Line_Section_Offset + Dwarf_Debug_Line_Section_Size;
 
 
+  found := false;
   while (current_offset < end_offset) and (not found) do begin
   while (current_offset < end_offset) and (not found) do begin
     Init(current_offset, end_offset - current_offset);
     Init(current_offset, end_offset - current_offset);
     current_offset := ParseCompilationUnit(addr, current_offset,
     current_offset := ParseCompilationUnit(addr, current_offset,
       source, line, found);
       source, line, found);
   end;
   end;
 
 
+  Init(Dwarf_Debug_Abbrev_Section_Offset, Dwarf_Debug_Abbrev_Section_Size);
+  ReadAbbrevTable;
+
+  current_offset := Dwarf_Debug_Info_Section_Offset;
+  end_offset := Dwarf_Debug_Info_Section_Offset + Dwarf_Debug_Info_Section_Size;
+
+  found := false;
+  while (current_offset < end_offset) and (not found) do begin
+    Init(current_offset, end_offset - current_offset);
+    current_offset := ParseCompilationUnitForFunctionName(addr, current_offset, func, found);
+  end;
+
   if not AllowReuseOfLineInfoData then
   if not AllowReuseOfLineInfoData then
     CloseDwarf;
     CloseDwarf;