unit FpDbgDwarfVerbosePrinter;

{$mode objfpc}{$H+}
{$IFDEF INLINE_OFF}{$INLINE OFF}{$ENDIF}

interface

uses
  Classes, Math, SysUtils, FpDbgDwarf, FpDbgLoader, FpDbgDwarfConst, FpdMemoryTools, FpDbgUtil,
  FpImgReaderBase, FpDbgDwarfDataClasses, {$ifdef FORCE_LAZLOGGER_DUMMY} LazLoggerDummy {$else} LazLoggerBase {$endif}, maps;

type

  { TDwarfAbbrevDecoder }

  TDwarfAbbrevDecoder = class(TObject)
  private
    FCU: TDwarfCompilationUnit;
    function  ReadTargetAddressFromDwarfSection(var AData: Pointer; AIncPointer: Boolean = False): TFpDbgMemLocation;
    function  ReadDwarfSectionOffsetOrLenFromDwarfSection(var AData: Pointer; AIncPointer: Boolean = False): TFpDbgMemLocation;
    procedure InternalDecode(AData: Pointer; AMaxData: Pointer; const AIndent: String = '');
  protected
    procedure DecodeLocation(AData: PByte; ASize: QWord; const AIndent: String = '');
    procedure DecodeLocationList({%H-}AReference: QWord; const {%H-}AIndent: String = '');
    function MakeAddressString(AData: Pointer): string;
  public
    constructor Create(ACompilationUnit: TDwarfCompilationUnit);
    procedure Decode;
  end;

  { TDwarfStatementDecoder }

  TDwarfStatementDecoder = class(TObject)
  private
    FCU: TDwarfCompilationUnit;
    procedure InternalDecode(AData: Pointer; {%H-}AMaxData: Pointer; const {%H-}AIndent: String = '');
  protected
  public
    constructor Create(ACompilationUnit: TDwarfCompilationUnit);
    procedure Decode(AData: Pointer); // Adata := FCU.FLineInfo.Header
  end;

  { TVerboseDwarfCallframeDecoder }

  TVerboseDwarfCallframeDecoder = class(TObject)
  private
    FLoader: TDbgImageLoader;
    procedure InternalDecode(AData: Pointer; ASize, AStart: QWord);
  protected
  public
    constructor Create(ALoader: TDbgImageLoader);
    procedure Decode;
  end;

implementation
var
  FPDBG_DWARF_VERBOSE, FPDBG_DWARF_WARNINGS: PLazLoggerLogGroup;

{ TDwarfAbbrevDecoder }

constructor TDwarfAbbrevDecoder.Create(ACompilationUnit: TDwarfCompilationUnit);
begin
  inherited Create;
  FCU := ACompilationUnit;
end;

procedure TDwarfAbbrevDecoder.Decode;
var
  Iter: TMapIterator;
  Info: TDwarfAddressInfo;
  Scope: TDwarfScopeInfo;
begin
  // force all abbrevs to be loaded
  InternalDecode(FCU.InfoData, FCU.InfoData + FCU.InfoDataLength);

  DebugLn(FPDBG_DWARF_VERBOSE, ['addresses: ']);
  Iter := TMapIterator.Create(FCU.AddressMap);
  while not Iter.EOM do
  begin
    Iter.GetData(Info);
    DbgOut(FPDBG_DWARF_VERBOSE, ['  ']);
    Scope.Init(Info.ScopeList);
    Scope.Index := Info.ScopeIndex;
    Scope := Scope.Parent;
    while Scope.IsValid do
    begin
      DbgOut(FPDBG_DWARF_VERBOSE, ['.']);
      Scope := Scope.Parent;
    end;
    DebugLn(FPDBG_DWARF_VERBOSE, [Info.Name, ': $', IntToHex(Info.StartPC, FCU.AddressSize * 2), '..$', IntToHex(Info.EndPC, FCU.AddressSize * 2)]);
    Iter.Next;
  end;
  Iter.Free;

end;

procedure TDwarfAbbrevDecoder.DecodeLocation(AData: PByte; ASize: QWord; const AIndent: String);
var
  MaxData: PByte;
  v: Int64;
begin
  MaxData := AData + ASize - 1;
  while AData <= MaxData do
  begin
    DbgOut(FPDBG_DWARF_VERBOSE, [AIndent]);
    case AData^ of
      DW_OP_addr: begin
        DbgOut(FPDBG_DWARF_VERBOSE, ['DW_OP_addr ', MakeAddressString(@AData[1])]);
        Inc(AData, 4);
      end;
      DW_OP_deref: begin
        DbgOut(FPDBG_DWARF_VERBOSE, ['DW_OP_deref']);
      end;
      DW_OP_const1u: begin
        DbgOut(FPDBG_DWARF_VERBOSE, ['DW_OP_const1u ', AData[1]]);
        Inc(AData, 1);
      end;
      DW_OP_const1s: begin
        DbgOut(FPDBG_DWARF_VERBOSE, ['DW_OP_const1s ', PShortInt(@AData[1])^]);
        Inc(AData, 1);
      end;
      DW_OP_const2u: begin
        DbgOut(FPDBG_DWARF_VERBOSE, ['DW_OP_const2u ', PWord(@AData[1])^]);
        Inc(AData, 2);
      end;
      DW_OP_const2s: begin
        DbgOut(FPDBG_DWARF_VERBOSE, ['DW_OP_const2s ', PSmallInt(@AData[1])^]);
        Inc(AData, 2);
      end;
      DW_OP_const4u: begin
        DbgOut(FPDBG_DWARF_VERBOSE, ['DW_OP_const4u ', PLongWord(@AData[1])^]);
        Inc(AData, 4);
      end;
      DW_OP_const4s: begin
        DbgOut(FPDBG_DWARF_VERBOSE, ['DW_OP_const4s ', PLongInt(@AData[1])^]);
        Inc(AData, 4);
      end;
      DW_OP_const8u: begin
        DbgOut(FPDBG_DWARF_VERBOSE, ['DW_OP_const8u ', PQWord(@AData[1])^]);
        Inc(AData, 8);
      end;
      DW_OP_const8s: begin
        DbgOut(FPDBG_DWARF_VERBOSE, ['DW_OP_const8s ', PInt64(@AData[1])^]);
        Inc(AData, 8);
      end;
      DW_OP_constu: begin
        Inc(AData);
        DbgOut(FPDBG_DWARF_VERBOSE, ['DW_OP_constu ', ULEB128toOrdinal(AData)]);;
        Dec(AData);
      end;
      DW_OP_consts: begin
        Inc(AData);
        DbgOut(FPDBG_DWARF_VERBOSE, ['DW_OP_consts ', SLEB128toOrdinal(AData)]);;
        Dec(AData);
      end;
      DW_OP_dup: begin
        DbgOut(FPDBG_DWARF_VERBOSE, ['DW_OP_dup']);
      end;
      DW_OP_drop: begin
        DbgOut(FPDBG_DWARF_VERBOSE, ['DW_OP_drop']);
      end;
      DW_OP_over: begin
        DbgOut(FPDBG_DWARF_VERBOSE, ['DW_OP_over']);
      end;
      DW_OP_pick: begin
        DbgOut(FPDBG_DWARF_VERBOSE, ['DW_OP_pick ', AData[1]]);
        Inc(AData, 1);
      end;
      DW_OP_swap: begin
        DbgOut(FPDBG_DWARF_VERBOSE, ['DW_OP_swap']);
      end;
      DW_OP_rot: begin
        DbgOut(FPDBG_DWARF_VERBOSE, ['DW_OP_rot']);
      end;
      DW_OP_xderef: begin
        DbgOut(FPDBG_DWARF_VERBOSE, ['DW_OP_xderef']);
      end;
      DW_OP_abs: begin
        DbgOut(FPDBG_DWARF_VERBOSE, ['DW_OP_abs']);
      end;
      DW_OP_and: begin
        DbgOut(FPDBG_DWARF_VERBOSE, ['DW_OP_and']);
      end;
      DW_OP_div: begin
        DbgOut(FPDBG_DWARF_VERBOSE, ['DW_OP_div']);
      end;
      DW_OP_minus: begin
        DbgOut(FPDBG_DWARF_VERBOSE, ['DW_OP_minus']);
      end;
      DW_OP_mod: begin
        DbgOut(FPDBG_DWARF_VERBOSE, ['DW_OP_mod']);
      end;
      DW_OP_mul: begin
        DbgOut(FPDBG_DWARF_VERBOSE, ['DW_OP_mul']);
      end;
      DW_OP_neg: begin
        DbgOut(FPDBG_DWARF_VERBOSE, ['DW_OP_neg']);
      end;
      DW_OP_not: begin
        DbgOut(FPDBG_DWARF_VERBOSE, ['DW_OP_not']);
      end;
      DW_OP_or: begin
        DbgOut(FPDBG_DWARF_VERBOSE, ['DW_OP_or']);
      end;
      DW_OP_plus: begin
        DbgOut(FPDBG_DWARF_VERBOSE, ['DW_OP_plus']);
      end;
      DW_OP_plus_uconst: begin
        Inc(AData);
        DbgOut(FPDBG_DWARF_VERBOSE, ['DW_OP_plus_uconst ', ULEB128toOrdinal(AData)]);;
        Dec(AData);
      end;
      DW_OP_shl: begin
        DbgOut(FPDBG_DWARF_VERBOSE, ['DW_OP_shl']);
      end;
      DW_OP_shr: begin
        DbgOut(FPDBG_DWARF_VERBOSE, ['DW_OP_shr']);
      end;
      DW_OP_shra: begin
        DbgOut(FPDBG_DWARF_VERBOSE, ['DW_OP_shra']);
      end;
      DW_OP_xor: begin
        DbgOut(FPDBG_DWARF_VERBOSE, ['DW_OP_xor']);
      end;
      DW_OP_skip: begin
        DbgOut(FPDBG_DWARF_VERBOSE, ['DW_OP_skip ', PSmallInt(@AData[1])^]);
        Inc(AData, 2);
      end;
      DW_OP_bra: begin
        DbgOut(FPDBG_DWARF_VERBOSE, ['DW_OP_bra ', PSmallInt(@AData[1])^]);
        Inc(AData, 2);
      end;
      DW_OP_eq: begin
        DbgOut(FPDBG_DWARF_VERBOSE, ['DW_OP_eq']);
      end;
      DW_OP_ge: begin
        DbgOut(FPDBG_DWARF_VERBOSE, ['DW_OP_ge']);
      end;
      DW_OP_gt: begin
        DbgOut(FPDBG_DWARF_VERBOSE, ['DW_OP_gt']);
      end;
      DW_OP_le: begin
        DbgOut(FPDBG_DWARF_VERBOSE, ['DW_OP_le']);
      end;
      DW_OP_lt: begin
        DbgOut(FPDBG_DWARF_VERBOSE, ['DW_OP_lt']);
      end;
      DW_OP_ne: begin
        DbgOut(FPDBG_DWARF_VERBOSE, ['DW_OP_ne']);
      end;
      DW_OP_lit0..DW_OP_lit31: begin
        DbgOut(FPDBG_DWARF_VERBOSE, ['DW_OP_lit', AData^ - DW_OP_lit0]);
      end;
      DW_OP_reg0..DW_OP_reg31: begin
        DbgOut(FPDBG_DWARF_VERBOSE, ['DW_OP_reg', AData^ - DW_OP_reg0]);
      end;
      DW_OP_breg0..DW_OP_breg31: begin
        DbgOut(FPDBG_DWARF_VERBOSE, ['DW_OP_breg', AData^ - DW_OP_breg0]);
        Inc(AData);
        v := SLEB128toOrdinal(AData);
        Dec(AData);
        if v >= 0
        then DbgOut(FPDBG_DWARF_VERBOSE, ['+']);
        DbgOut(FPDBG_DWARF_VERBOSE, [v]);
      end;
      DW_OP_regx: begin
        Inc(AData);
        DbgOut(FPDBG_DWARF_VERBOSE, ['DW_OP_regx ', ULEB128toOrdinal(AData)]);
        Dec(AData);
      end;
      DW_OP_fbreg: begin
        Inc(AData);
        DbgOut(FPDBG_DWARF_VERBOSE, ['DW_OP_fbreg ', SLEB128toOrdinal(AData)]);
        Dec(AData);
      end;
      DW_OP_bregx: begin
        Inc(AData);
        DbgOut(FPDBG_DWARF_VERBOSE, ['DW_OP_bregx ', ULEB128toOrdinal(AData)]);
        v := SLEB128toOrdinal(AData);
        Dec(AData);
        if v >= 0
        then DbgOut(FPDBG_DWARF_VERBOSE, ['+']);
        DbgOut(FPDBG_DWARF_VERBOSE, [v]);
      end;
      DW_OP_piece: begin
        Inc(AData);
        DbgOut(FPDBG_DWARF_VERBOSE, ['DW_OP_piece ', ULEB128toOrdinal(AData)]);
        Dec(AData);
      end;
      DW_OP_deref_size: begin
        DbgOut(FPDBG_DWARF_VERBOSE, ['DW_OP_deref_size ', AData[1]]);
        Inc(AData);
      end;
      DW_OP_xderef_size: begin
        DbgOut(FPDBG_DWARF_VERBOSE, ['DW_OP_xderef_size', AData[1]]);
        Inc(AData);
      end;
      DW_OP_nop: begin
        DbgOut(FPDBG_DWARF_VERBOSE, ['DW_OP_nop']);
      end;
      DW_OP_push_object_address: begin
        DbgOut(FPDBG_DWARF_VERBOSE, ['DW_OP_push_object_address']);
      end;
      DW_OP_call2: begin
        DbgOut(FPDBG_DWARF_VERBOSE, ['DW_OP_call2 ', PWord(@AData[1])^]);
        Inc(AData, 2);
      end;
      DW_OP_call4: begin
        DbgOut(FPDBG_DWARF_VERBOSE, ['DW_OP_call4 ', PLongWord(@AData[1])^]);
        Inc(AData, 4);
      end;
      DW_OP_call_ref: begin
        DbgOut(FPDBG_DWARF_VERBOSE, ['DW_OP_call_ref ', MakeAddressString(@AData[1])]);
        Inc(AData, 4);
      end;
      DW_OP_form_tls_address: begin
        DbgOut(FPDBG_DWARF_VERBOSE, ['DW_OP_form_tls_address']);
      end;
      DW_OP_call_frame_cfa: begin
        DbgOut(FPDBG_DWARF_VERBOSE, ['DW_OP_call_frame_cfa']);
      end;
      DW_OP_bit_piece: begin
        Inc(AData);
        DbgOut(FPDBG_DWARF_VERBOSE, ['DW_OP_bit_piece ', ULEB128toOrdinal(AData), ' ', ULEB128toOrdinal(AData)]);
        Dec(AData);
      end;
      DW_OP_lo_user..DW_OP_hi_user: begin
        DbgOut(FPDBG_DWARF_VERBOSE, ['DW_OP_user=', AData^]);
      end;
    else
      DbgOut(FPDBG_DWARF_VERBOSE, ['Unknown DW_OP_', AData^]);
    end;
    Inc(AData);
    DebugLn(FPDBG_DWARF_VERBOSE, ['']);
  end;
end;

procedure TDwarfAbbrevDecoder.DecodeLocationList(AReference: QWord; const AIndent: String);
begin

end;

function TDwarfAbbrevDecoder.ReadTargetAddressFromDwarfSection(
  var AData: Pointer; AIncPointer: Boolean): TFpDbgMemLocation;
begin
  // do not need mem reader, address is in dwarf. Should be in correct format
  if FCU.AddressSize = 4
  then Result := TargetLoc(PLongWord(AData)^)
  else Result := TargetLoc(PQWord(AData)^);
  if AIncPointer then inc(AData, FCU.AddressSize);
end;

function TDwarfAbbrevDecoder.ReadDwarfSectionOffsetOrLenFromDwarfSection(var AData: Pointer;
  AIncPointer: Boolean): TFpDbgMemLocation;
begin
  // do not need mem reader, address is in dwarf. Should be in correct format
  if FCU.AddressSize = 4 // TODO Dwarf3 depends on FIsDwarf64
  then Result := TargetLoc(PLongWord(AData)^)
  else Result := TargetLoc(PQWord(AData)^);
  if AIncPointer then inc(AData, FCU.AddressSize);
end;

procedure TDwarfAbbrevDecoder.InternalDecode(AData: Pointer; AMaxData: Pointer; const AIndent: String);
  procedure Dump(var p: PByte; count: QWord);
  var
    n: integer;
  begin
    for n := 1 to Min(80, count) do
    begin
      DbgOut(FPDBG_DWARF_VERBOSE, [IntToHex(p^, 2), ' ']);
      Inc(p);
    end;
    if Count > 80
    then begin
      Inc(p, Count - 80);
      DbgOut(FPDBG_DWARF_VERBOSE, ['...']);
    end;
  end;
  procedure DumpStr(var p: PChar);
  begin
    while p^ <> #0 do
    begin
      case p^ of
        #32..#127: DbgOut(FPDBG_DWARF_VERBOSE, [p^]);
      else
        DbgOut(FPDBG_DWARF_VERBOSE, ['<', IntToHex(Ord(p^), 2), '>']);
      end;
      Inc(p);
    end;
    Inc(p);
  end;

var
  Attribute: Cardinal;
  Abbrev, Form: Cardinal;
  Def: TDwarfAbbrev;
  idx: Integer;
  Value: QWord;
  ValueSize: QWord;
  ValuePtr, p: Pointer;
  Indent: String;
  Level: Integer;
  ADefs: PDwarfAbbrevEntry;
begin
  Indent := AIndent;
  Level := 0;
  ADefs := FCU.AbbrevList.EntryPointer[0];
  while (AData <= AMaxData) and (Level >= 0) do
  begin
p := AData;
    Abbrev := ULEB128toOrdinal(AData);
    if Abbrev = 0
    then begin
      Dec(Level);
      SetLength(Indent, Length(Indent) - 2);
      if Level >= 0
      then  DebugLn(FPDBG_DWARF_VERBOSE, [Indent, ' \--']);
      Continue;
    end;
    DbgOut(FPDBG_DWARF_VERBOSE, [Indent, 'abbrev: ', Abbrev]);
    if not FCU.GetDefinition(p, Def)
    then begin
      DebugLn(FPDBG_DWARF_WARNINGS, ['Error: Abbrev not found: ', Abbrev]);
      Exit;
    end;
    DbgOut(FPDBG_DWARF_VERBOSE, [', tag: ', Def.tag, '=', DwarfTagToString(Def.tag)]);
    if (dafHasChildren in Def.flags)
    then begin
      DebugLn(FPDBG_DWARF_VERBOSE, ['']);
      DebugLn(FPDBG_DWARF_VERBOSE, [', has children']);
      Inc(Level);
    end
    else DebugLn(FPDBG_DWARF_VERBOSE, ['']);

    for idx := Def.Index to Def.Index + Def.Count - 1 do
    begin
      Form := ADefs[idx].Form;
      Attribute := ADefs[idx].Attribute;
      DbgOut(FPDBG_DWARF_VERBOSE, [Indent, ' attrib: ', Attribute, '=', DwarfAttributeToString(Attribute)]);
      DbgOut(FPDBG_DWARF_VERBOSE, [', form: ', Form, '=', DwarfAttributeFormToString(Form)]);

      ValueSize := 0;
      ValuePtr := nil;
      Value := 0;
      repeat
        DbgOut(FPDBG_DWARF_VERBOSE, [', value: ']);
        case Form of
          DW_FORM_addr     : begin
            Value := LocToAddrOrNil(ReadTargetAddressFromDwarfSection(AData));
            ValuePtr := {%H-}Pointer(PtrUInt(Value));
            ValueSize := FCU.AddressSize;
            DbgOut(FPDBG_DWARF_VERBOSE, ['$'+IntToHex(Value, FCU.AddressSize * 2)]);
            Inc(AData, FCU.AddressSize);
          end;
          DW_FORM_block    : begin
            ValueSize := ULEB128toOrdinal(AData);
            ValuePtr := AData;
            DbgOut(FPDBG_DWARF_VERBOSE, ['Size=', ValueSize, ', Data=']);
            Dump(AData, ValueSize);
          end;
          DW_FORM_block1   : begin
            ValueSize := PByte(AData)^;
            Inc(AData, 1);
            ValuePtr := AData;
            DbgOut(FPDBG_DWARF_VERBOSE, ['Size=', ValueSize, ', Data=']);
            Dump(AData, ValueSize);
          end;
          DW_FORM_block2   : begin
            ValueSize := PWord(AData)^;
            Inc(AData, 2);
            ValuePtr := AData;
            DbgOut(FPDBG_DWARF_VERBOSE, ['Size=', ValueSize, ', Data=']);
            Dump(AData, ValueSize);
          end;
          DW_FORM_block4   : begin
            ValueSize := PLongWord(AData)^;
            Inc(AData, 4);
            ValuePtr := AData;
            DbgOut(FPDBG_DWARF_VERBOSE, ['Size=', ValueSize, ', Data=']);
            Dump(AData, ValueSize);
          end;
          DW_FORM_data1    : begin
            Value := PByte(AData)^;
            ValueSize := 1;
            DbgOut(FPDBG_DWARF_VERBOSE, ['$'+IntToHex(Value, 2)]);
            Inc(AData, 1);
          end;
          DW_FORM_data2    : begin
            Value := PWord(AData)^;
            ValueSize := 2;
            DbgOut(FPDBG_DWARF_VERBOSE, ['$'+IntToHex(Value, 4)]);
            Inc(AData, 2);
          end;
          DW_FORM_data4    : begin
            Value := PLongWord(AData)^;
            ValueSize := 4;
            DbgOut(FPDBG_DWARF_VERBOSE, ['$'+IntToHex(Value, 8)]);
            Inc(AData, 4);
          end;
          DW_FORM_data8    : begin
            Value := PQWord(AData)^;
            ValueSize := 8;
            DbgOut(FPDBG_DWARF_VERBOSE, ['$'+IntToHex(Value, 16)]);
            Inc(AData, 8);
          end;
          DW_FORM_sdata    : begin
            p := AData;
            Value := ULEB128toOrdinal(AData);
            ValueSize := {%H-}PtrUInt(AData) - {%H-}PtrUInt(p);
            DbgOut(FPDBG_DWARF_VERBOSE, ['$'+IntToHex(Value, ValueSize * 2)]);
          end;
          DW_FORM_udata    : begin
            p := AData;
            Value := ULEB128toOrdinal(AData);
            ValueSize := {%H-}PtrUInt(AData) - {%H-}PtrUInt(p);
            DbgOut(FPDBG_DWARF_VERBOSE, ['$'+IntToHex(Value, ValueSize * 2)]);
          end;
          DW_FORM_flag     : begin
            Value := PByte(AData)^;
            ValueSize := 1;
            DbgOut(FPDBG_DWARF_VERBOSE, ['$'+IntToHex(Value, 2)]);
            Inc(AData, 1);
          end;
          DW_FORM_ref1     : begin
            Value := PByte(AData)^;
            ValueSize := 1;
            DbgOut(FPDBG_DWARF_VERBOSE, ['$'+IntToHex(Value, 2)]);
            Inc(AData, 1);
          end;
          DW_FORM_ref2     : begin
            Value := PWord(AData)^;
            ValueSize := 2;
            DbgOut(FPDBG_DWARF_VERBOSE, ['$'+IntToHex(Value, 4)]);
            Inc(AData, 2);
          end;
          DW_FORM_ref4     : begin
            Value := PLongWord(AData)^;
            ValueSize := 4;
            DbgOut(FPDBG_DWARF_VERBOSE, ['$'+IntToHex(Value, 8)]);
            Inc(AData, 4);
          end;
          DW_FORM_ref8     : begin
            Value := PQWord(AData)^;
            ValueSize := 8;
            DbgOut(FPDBG_DWARF_VERBOSE, ['$'+IntToHex(Value, 16)]);
            Inc(AData, 8);
          end;
          DW_FORM_ref_udata: begin
            p := AData;
            Value := ULEB128toOrdinal(AData);
            ValueSize := {%H-}PtrUInt(AData) - {%H-}PtrUInt(p);
            DbgOut(FPDBG_DWARF_VERBOSE, ['$'+IntToHex(Value, ValueSize * 2)]);
          end;
          DW_FORM_ref_addr : begin
            Value := LocToAddrOrNil(ReadDwarfSectionOffsetOrLenFromDwarfSection(AData));
            ValuePtr := {%H-}Pointer(PtrUInt(Value));
            ValueSize := FCU.AddressSize;
            DbgOut(FPDBG_DWARF_VERBOSE, ['$'+IntToHex(Value, FCU.AddressSize * 2)]);
            Inc(AData, FCU.AddressSize);
          end;
          DW_FORM_string   : begin
            ValuePtr := AData;
            DumpStr(AData);
            ValueSize := {%H-}PtrUInt(AData) - {%H-}PtrUInt(ValuePtr);
          end;
          DW_FORM_strp     : begin
            Value := LocToAddrOrNil(ReadDwarfSectionOffsetOrLenFromDwarfSection(AData));
            ValueSize := FCU.AddressSize;
            DbgOut(FPDBG_DWARF_VERBOSE, ['$'+IntToHex(Value, FCU.AddressSize * 2)]);
            Inc(AData, FCU.AddressSize);
          end;
          DW_FORM_indirect : begin
            Form := ULEB128toOrdinal(AData);
            DbgOut(FPDBG_DWARF_VERBOSE, ['indirect form: ', Form, '=', DwarfAttributeFormToString(Form)]);
            Continue;
          end;
        else
          DebugLn(FPDBG_DWARF_WARNINGS, ['Error: Unknown Form: ', Form]);
          Exit;
        end;
        Break;
      until False;

      case Attribute of
        DW_AT_accessibility: begin
          DebugLn(FPDBG_DWARF_VERBOSE, ['=', DwarfAccessibilityToString(Value)]);
        end;
        DW_AT_data_member_location: begin
          DebugLn(FPDBG_DWARF_VERBOSE, ['-->']);
          DecodeLocation(ValuePtr, ValueSize, Indent + '  ');
        end;
        DW_AT_encoding: begin
          DebugLn(FPDBG_DWARF_VERBOSE, ['=', DwarfBaseTypeEncodingToString(Value)]);
        end;
        DW_AT_language: begin
          DebugLn(FPDBG_DWARF_VERBOSE, ['=', DwarfLanguageToString(Value)]);
        end;
        DW_AT_identifier_case: begin
          DebugLn(FPDBG_DWARF_VERBOSE, ['=', DwarfIdentifierCaseToString(Value)]);
        end;
        DW_AT_location: begin
          if ValuePtr = nil
          then begin
            DebugLn(FPDBG_DWARF_VERBOSE, ['-->']);
            DecodeLocationList(Value, AIndent + '  ');
          end
          else begin
            DebugLn(FPDBG_DWARF_VERBOSE, ['-->']);
            DecodeLocation(ValuePtr, ValueSize, Indent + '  ');
          end;
        end;
        DW_AT_type: begin
          DebugLn(FPDBG_DWARF_VERBOSE, ['-->']);
          try
            p := FCU.DebugFile^.Sections[dsInfo].RawData + Value - FCU.Owner.ImageBase - FCU.DebugFile^.Sections[dsInfo].VirtualAddress;
            InternalDecode(p, p, Indent + '  ');
          except
            on E: Exception do DebugLn(FPDBG_DWARF_WARNINGS, [AIndent, '  ', E.Message]);
          end;
        end;
      else
        DebugLn(FPDBG_DWARF_VERBOSE, ['']);
      end;
    end;

    if (dafHasChildren in Def.flags)
    then begin
      DebugLn(FPDBG_DWARF_VERBOSE, [Indent, ' /--']);
      Indent := Indent + ' |';
    end;
  end;
end;


function TDwarfAbbrevDecoder.MakeAddressString(AData: Pointer): string;
begin
  if FCU.AddressSize = 4
  then Result := '$'+IntToHex(PLongWord(AData)^, 8)
  else Result := '$'+IntToHex(PQWord(AData)^, 16);
end;

{ TDwarfStatementDecoder }

constructor TDwarfStatementDecoder.Create(ACompilationUnit: TDwarfCompilationUnit);
begin
  inherited Create;
  FCU := ACompilationUnit;
end;

procedure TDwarfStatementDecoder.Decode(AData: Pointer);
begin
  if AData = nil
  then begin
    DebugLn(FPDBG_DWARF_WARNINGS, ['No lineinfo']);
    Exit;
  end;
  InternalDecode(AData, FCU.DebugFile^.Sections[dsInfo].RawData + FCU.DebugFile^.Sections[dsInfo].Size);
end;

procedure TDwarfStatementDecoder.InternalDecode(AData: Pointer; AMaxData: Pointer; const AIndent: String);
var
  Info: PDwarfLNPInfoHeader;

  Address: QWord;
  Line: Int64;
  FileNr: Cardinal;
  Column: Cardinal;
  IsStmt: Boolean;
  BasicBlock: Boolean;
  PrologueEnd: Boolean;
  EpilogueBegin: Boolean;
  Isa: QWord;

  procedure AddRow(ALast: Boolean = False);
  begin
    DbgOut(FPDBG_DWARF_VERBOSE, ['> ']);
    DbgOut(FPDBG_DWARF_VERBOSE, ['Address=$', IntToHex(Address, FCU.AddressSize * 2)]);
    DbgOut(FPDBG_DWARF_VERBOSE, [', Line=',Line]);
    DbgOut(FPDBG_DWARF_VERBOSE, [', FileNr=',FileNr]);
    DbgOut(FPDBG_DWARF_VERBOSE, [', Column=',Column]);
    DbgOut(FPDBG_DWARF_VERBOSE, [', IsStmt=',IsStmt]);
    DbgOut(FPDBG_DWARF_VERBOSE, [', BasicBlock=',BasicBlock]);
    DbgOut(FPDBG_DWARF_VERBOSE, [', PrologueEnd=',PrologueEnd]);
    DbgOut(FPDBG_DWARF_VERBOSE, [', EpilogueBegin=',EpilogueBegin]);
    DbgOut(FPDBG_DWARF_VERBOSE, [', Isa=',Isa]);
    DebugLn(FPDBG_DWARF_VERBOSE, ['']);
    if ALast
    then DebugLn(FPDBG_DWARF_VERBOSE, ['> ---------']);
  end;

  procedure DoAdjust(AOpcode: Byte);
  begin
    Dec(AOpcode, Info^.OpcodeBase);
    if Info^.LineRange = 0
    then begin
      Inc(Address, AOpcode * Info^.MinimumInstructionLength);
    end
    else begin
      Inc(Address, (AOpcode div Info^.LineRange) * Info^.MinimumInstructionLength);
      Inc(Line, Info^.LineBase + (AOpcode mod Info^.LineRange));
    end;
  end;

  procedure DoReset;
  begin
    Address := 0;
    Line := 1;
    FileNr := 1;
    Column := 0;
    IsStmt := Info^.DefaultIsStmt <> 0;
    BasicBlock := False;
    PrologueEnd := False;
    EpilogueBegin := False;
    Isa := 0;
  end;


var
  LNP32: PDwarfLNPHeader32 absolute AData;
  LNP64: PDwarfLNPHeader64 absolute AData;
  UnitLength: QWord;
  Version: Word;
  HeaderLength: QWord;
  n: integer;
  ptr: Pointer;
  p: Pointer;
  pb: PByte absolute p;
  pc: PChar absolute p;
  DataEnd: Pointer;
  DataStart: Pointer;
  UValue: QWord;
  SValue: Int64;
begin
  DebugLn(FPDBG_DWARF_VERBOSE, ['FileName: ', FCU.FileName]);

  if LNP64^.Signature = DWARF_HEADER64_SIGNATURE
  then begin
    UnitLength := LNP64^.UnitLength;
    DataEnd := Pointer(@LNP64^.Version) + UnitLength;
    Version := LNP64^.Version;
    HeaderLength := LNP64^.HeaderLength;
    Info := @LNP64^.Info;
  end
  else begin
    UnitLength := LNP32^.UnitLength;
    DataEnd := Pointer(@LNP32^.Version) + UnitLength;
    Version := LNP32^.Version;
    HeaderLength := LNP32^.HeaderLength;
    Info := @LNP32^.Info;
  end;
  DataStart := PByte(Info) + HeaderLength;

  DebugLn(FPDBG_DWARF_VERBOSE, ['UnitLength: ', UnitLength]);
  DebugLn(FPDBG_DWARF_VERBOSE, ['Version: ', Version]);
  DebugLn(FPDBG_DWARF_VERBOSE, ['HeaderLength: ', HeaderLength]);

  DebugLn(FPDBG_DWARF_VERBOSE, ['MinimumInstructionLength: ', Info^.MinimumInstructionLength]);
  DebugLn(FPDBG_DWARF_VERBOSE, ['DefaultIsStmt: ', Info^.DefaultIsStmt]);
  DebugLn(FPDBG_DWARF_VERBOSE, ['LineBase: ', Info^.LineBase]);
  DebugLn(FPDBG_DWARF_VERBOSE, ['LineRange: ', Info^.LineRange]);
  DebugLn(FPDBG_DWARF_VERBOSE, ['OpcodeBase: ', Info^.OpcodeBase]);
  p := @Info^.StandardOpcodeLengths;
  DebugLn(FPDBG_DWARF_VERBOSE, ['StandardOpcodeLengths:']);
  for n := 1 to Info^.OpcodeBase - 1 do
  begin
    DebugLn(FPDBG_DWARF_VERBOSE, ['  [', n, '] ', pb^]);
    Inc(pb);
  end;

  DebugLn(FPDBG_DWARF_VERBOSE, ['IncludeDirectories:']);
  while pc^ <> #0 do
  begin
    DbgOut(FPDBG_DWARF_VERBOSE, ['  ']);
    repeat
      DbgOut(FPDBG_DWARF_VERBOSE, [pc^]);
      Inc(pc);
    until pc^ = #0;
    DebugLn(FPDBG_DWARF_VERBOSE, ['']);
    Inc(pc);
  end;
  Inc(pc);
  DebugLn(FPDBG_DWARF_VERBOSE, ['FileNames:']);
  while pc^ <> #0 do
  begin
    DbgOut(FPDBG_DWARF_VERBOSE, ['  ']);
    repeat
      DbgOut(FPDBG_DWARF_VERBOSE, [pc^]);
      Inc(pc);
    until pc^ = #0;
    Inc(pc);
    DbgOut(FPDBG_DWARF_VERBOSE, [', diridx=', ULEB128toOrdinal(p)]);
    DbgOut(FPDBG_DWARF_VERBOSE, [', last modified=', ULEB128toOrdinal(p)]);
    DbgOut(FPDBG_DWARF_VERBOSE, [', length=', ULEB128toOrdinal(p)]);
    DebugLn(FPDBG_DWARF_VERBOSE, ['']);
  end;

  DebugLn(FPDBG_DWARF_VERBOSE, ['Program:']);

  p := DataStart;
  DoReset;

  while p < DataEnd do
  begin
    DbgOut(FPDBG_DWARF_VERBOSE, ['  ']);
    if (pb^ > 0) and (pb^ < Info^.OpcodeBase)
    then begin
      // Standard opcode
      case pb^ of
        DW_LNS_copy: begin
          Inc(p);
          DebugLn(FPDBG_DWARF_VERBOSE, ['DW_LNS_copy']);
          AddRow;
          BasicBlock := False;
          PrologueEnd := False;
          EpilogueBegin := False;
        end;
        DW_LNS_advance_pc: begin
          Inc(p);
          UValue := ULEB128toOrdinal(p);
          Inc(Address, UValue);
          DebugLn(FPDBG_DWARF_VERBOSE, ['DW_LNS_advance_pc ', UValue]);
        end;
        DW_LNS_advance_line: begin
          Inc(p);
          SValue := SLEB128toOrdinal(p);
          Inc(Line, SValue);
          DebugLn(FPDBG_DWARF_VERBOSE, ['DW_LNS_advance_line ', SValue]);
        end;
        DW_LNS_set_file: begin
          Inc(p);
          UValue := ULEB128toOrdinal(p);
          DebugLn(FPDBG_DWARF_VERBOSE, ['DW_LNS_set_file ', UVAlue]);
          FileNr := UValue;
        end;
        DW_LNS_set_column: begin
          Inc(p);
          UValue := ULEB128toOrdinal(p);
          DebugLn(FPDBG_DWARF_VERBOSE, ['DW_LNS_set_column ', UValue]);
          Column := UValue;
        end;
        DW_LNS_negate_stmt: begin
          Inc(p);
          DebugLn(FPDBG_DWARF_VERBOSE, ['DW_LNS_negate_stmt']);
          IsStmt := not IsStmt;
        end;
        DW_LNS_set_basic_block: begin
          Inc(p);
          DebugLn(FPDBG_DWARF_VERBOSE, ['DW_LNS_set_basic_block']);
          BasicBlock := True;
        end;
        DW_LNS_const_add_pc: begin
          Inc(p);
          DebugLn(FPDBG_DWARF_VERBOSE, ['DW_LNS_const_add_pc']);
          DoAdjust(255);
        end;
        DW_LNS_fixed_advance_pc: begin
          Inc(p);
          Inc(Address, PWord(p)^);
          DebugLn(FPDBG_DWARF_VERBOSE, ['DW_LNS_fixed_advance_pc ', PWord(p)^]);
          Inc(p, 2);
        end;
        DW_LNS_set_prologue_end: begin
          Inc(p);
          DebugLn(FPDBG_DWARF_VERBOSE, ['DW_LNS_set_prologue_end']);
          PrologueEnd := True;
        end;
        DW_LNS_set_epilogue_begin: begin
          Inc(p);
          DebugLn(FPDBG_DWARF_VERBOSE, ['DW_LNS_set_epilogue_begin']);
          EpilogueBegin := True;
        end;
        DW_LNS_set_isa: begin
          Inc(p);
          UValue := ULEB128toOrdinal(p);
          Isa := UValue;
          DebugLn(FPDBG_DWARF_VERBOSE, ['DW_LNS_set_isa ', UValue]);
        end;
      else
        DbgOut(FPDBG_DWARF_VERBOSE, ['unknown opcode: ', pb^]);
        Inc(p, PByte(@Info^.StandardOpcodeLengths)[pb^-1]);
      end;
      Continue;
    end;

    if pb^ = 0
    then begin
      // Extended opcode
      Inc(p);
      UValue := ULEB128toOrdinal(p); // instruction length

      case pb^ of
        DW_LNE_end_sequence: begin
          DebugLn(FPDBG_DWARF_VERBOSE, ['DW_LNE_end_sequence']);
          AddRow(True);
          DoReset;
          //Inc(p, UValue);
          //Break;
        end;
        DW_LNE_set_address: begin
          if LNP64^.Signature = DWARF_HEADER64_SIGNATURE
          then Address := PQWord(pb+1)^
          else Address := PLongWord(pb+1)^;
          DebugLn(FPDBG_DWARF_VERBOSE, ['DW_LNE_set_address $', IntToHex(Address, FCU.AddressSize * 2)]);
        end;
        DW_LNE_define_file: begin
          ptr := p;
          Inc(ptr);
          DbgOut(FPDBG_DWARF_VERBOSE, ['DW_LNE_define_file name=']);
          repeat
            DbgOut(FPDBG_DWARF_VERBOSE, [PChar(ptr)^]);
            Inc(ptr);
          until PChar(ptr)^ = #0;
          Inc(ptr);
          DbgOut(FPDBG_DWARF_VERBOSE, [', diridx=', ULEB128toOrdinal(ptr)]);
          DbgOut(FPDBG_DWARF_VERBOSE, [', last modified=', ULEB128toOrdinal(ptr)]);
          DbgOut(FPDBG_DWARF_VERBOSE, [', length=', ULEB128toOrdinal(ptr)]);
          DebugLn(FPDBG_DWARF_VERBOSE, ['']);
        end;
      else
        DbgOut(FPDBG_DWARF_VERBOSE, ['unknown extended opcode: ', pb^]);
      end;
      Inc(p, UValue);
    end
    else begin
      DebugLn(FPDBG_DWARF_VERBOSE, ['Special opcode: ', pb^]);
      // Special opcode
      DoAdjust(pb^);
      AddRow;
      BasicBlock := False;
      PrologueEnd := False;
      EpilogueBegin := False;
      Inc(p);
    end;
  end;
end;

{ TVerboseDwarfCallframeDecoder }

constructor TVerboseDwarfCallframeDecoder.Create(ALoader: TDbgImageLoader);
begin
  inherited Create;
  FLoader := Aloader;
end;

procedure TVerboseDwarfCallframeDecoder.Decode;
var
  Section: PDbgImageSection;
begin
  Section := FLoader.Section[DWARF_SECTION_NAME[dsFrame]];
  if Section <> nil
  then InternalDecode(Section^.RawData, Section^.Size, Section^.VirtualAddress);
end;

procedure TVerboseDwarfCallframeDecoder.InternalDecode(AData: Pointer; ASize: QWord; AStart: QWord);
var
  Is64bit: boolean;

  procedure DecodeInstructions(p: Pointer; MaxAddr: Pointer);
  var
    pb: PByte absolute p;
    pw: PWord absolute p;
    pc: PCardinal absolute p;
    pq: PQWord absolute p;
    q: QWord;
  begin
    repeat
      DbgOut(FPDBG_DWARF_VERBOSE, [' ']);
      Inc(pb);
      case pb[-1] of
        DW_CFA_nop: begin
          DebugLn(FPDBG_DWARF_VERBOSE, ['DW_CFA_nop']);
        end;
        DW_CFA_set_loc: begin
          // address
          DbgOut(FPDBG_DWARF_VERBOSE, ['DW_CFA_set_loc $']);
          if Is64Bit
          then begin
            DebugLn(FPDBG_DWARF_VERBOSE, [IntToHex(pq^, 16)]);
            Inc(pq);
          end
          else begin
            DebugLn(FPDBG_DWARF_VERBOSE, [IntToHex(pc^, 8)]);
            Inc(pc);
          end;
        end;
        DW_CFA_advance_loc1: begin
          // 1-byte delta
          DebugLn(FPDBG_DWARF_VERBOSE, ['DW_CFA_advance_loc1 ', pb^, ' * caf']);
          Inc(pb);
        end;
        DW_CFA_advance_loc2: begin
          // 2-byte delta
          DebugLn(FPDBG_DWARF_VERBOSE, ['DW_CFA_advance_loc2 ', pw^, ' * caf']);
          Inc(pw);
        end;
        DW_CFA_advance_loc4: begin
          // 4-byte delta
          DebugLn(FPDBG_DWARF_VERBOSE, ['DW_CFA_advance_loc4 ', pc^, ' * caf']);
          Inc(pw);
        end;
        DW_CFA_offset_extended: begin
          // ULEB128 register, ULEB128 offset
          DebugLn(FPDBG_DWARF_VERBOSE, ['DW_CFA_offset_extended R', ULEB128toOrdinal(p), ' + ', ULEB128toOrdinal(p), ' * daf']);
        end;
        DW_CFA_restore_extended: begin
          // ULEB128 register
          DebugLn(FPDBG_DWARF_VERBOSE, ['DW_CFA_restore_extended R', ULEB128toOrdinal(p)]);
        end;
        DW_CFA_undefined: begin
          // ULEB128 register
          DebugLn(FPDBG_DWARF_VERBOSE, ['DW_CFA_undefined R', ULEB128toOrdinal(p)]);
        end;
        DW_CFA_same_value: begin
          // ULEB128 register
          DebugLn(FPDBG_DWARF_VERBOSE, ['DW_CFA_same_value R', ULEB128toOrdinal(p)]);
        end;
        DW_CFA_register: begin
          // ULEB128 register, ULEB128 register
          DebugLn(FPDBG_DWARF_VERBOSE, ['DW_CFA_register R', ULEB128toOrdinal(p), ' R', ULEB128toOrdinal(p)]);
        end;
        DW_CFA_remember_state: begin
          DebugLn(FPDBG_DWARF_VERBOSE, ['DW_CFA_remember_state']);
        end;
        DW_CFA_restore_state: begin
          DebugLn(FPDBG_DWARF_VERBOSE, ['DW_CFA_restore_state']);
        end;
        DW_CFA_def_cfa: begin
          // ULEB128 register, ULEB128 offset
          DebugLn(FPDBG_DWARF_VERBOSE, ['DW_CFA_def_cfa R', ULEB128toOrdinal(p), ' + ', ULEB128toOrdinal(p)]);
        end;
        DW_CFA_def_cfa_register: begin
          // ULEB128 register
          DebugLn(FPDBG_DWARF_VERBOSE, ['DW_CFA_def_cfa_register R', ULEB128toOrdinal(p)]);
        end;
        DW_CFA_def_cfa_offset: begin
          // ULEB128 offset
          DebugLn(FPDBG_DWARF_VERBOSE, ['DW_CFA_def_cfa_offset ', ULEB128toOrdinal(p)]);
        end;
        // --- DWARF3 ---
        DW_CFA_def_cfa_expression: begin
          // BLOCK
          q := ULEB128toOrdinal(p);
          DebugLn(FPDBG_DWARF_VERBOSE, ['DW_CFA_def_cfa_expression, lenght=',q]);
          Inc(p, q);
        end;
        DW_CFA_expression: begin
          // ULEB128 register, BLOCK
          DbgOut(FPDBG_DWARF_VERBOSE, ['DW_CFA_expression R', ULEB128toOrdinal(p), ' lenght=',q]);
          q := ULEB128toOrdinal(p);
          DebugLn(FPDBG_DWARF_VERBOSE, [q]);
          Inc(p, q);
        end;
        DW_CFA_offset_extended_sf: begin
          // ULEB128 register, SLEB128 offset
          DebugLn(FPDBG_DWARF_VERBOSE, ['DW_CFA_offset_extended_sf R', ULEB128toOrdinal(p), ' + ', SLEB128toOrdinal(p), ' * daf']);
        end;
        DW_CFA_def_cfa_sf: begin
          // ULEB128 register, SLEB128 offset
          DebugLn(FPDBG_DWARF_VERBOSE, ['DW_CFA_def_cfa_sf R', ULEB128toOrdinal(p), ' + ', SLEB128toOrdinal(p), ' * daf']);
        end;
        DW_CFA_def_cfa_offset_sf: begin
          // SLEB128 offset
          DebugLn(FPDBG_DWARF_VERBOSE, ['DW_CFA_def_cfa_offset_sf ', SLEB128toOrdinal(p), ' * daf' ]);
        end;
        DW_CFA_val_offset: begin
          // ULEB128         , ULEB128
          DebugLn(FPDBG_DWARF_VERBOSE, ['DW_CFA_val_offset R', ULEB128toOrdinal(p), ' + ', ULEB128toOrdinal(p), ' * daf']);
        end;
        DW_CFA_val_offset_sf: begin
          // ULEB128         , SLEB128
          DebugLn(FPDBG_DWARF_VERBOSE, ['DW_CFA_val_offset_sf R', ULEB128toOrdinal(p), ' + ', SLEB128toOrdinal(p), ' * daf']);
        end;
        DW_CFA_val_expression: begin
          // ULEB128         , BLOCK
          DbgOut(FPDBG_DWARF_VERBOSE, ['DW_CFA_val_expression R', ULEB128toOrdinal(p), ' lenght=',q]);
          q := ULEB128toOrdinal(p);
          DebugLn(FPDBG_DWARF_VERBOSE, [q]);
          Inc(p, q);
        end;
        // ---  ---
        DW_CFA_lo_user..DW_CFA_hi_user: begin
          DebugLn(FPDBG_DWARF_VERBOSE, ['DW_CFA_user=', pb^]);
        end;
        // ---  ---
        DW_CFA_advance_loc..DW_CFA_offset-1: begin
          // delta
          DebugLn(FPDBG_DWARF_VERBOSE, ['DW_CFA_advance_loc ', pb[-1] and $3F, ' * caf']);
        end;
        DW_CFA_offset..DW_CFA_restore-1: begin
          // register  ULEB128 offset
          DebugLn(FPDBG_DWARF_VERBOSE, ['DW_CFA_offset R', pb[-1] and $3F, ' + ', ULEB128toOrdinal(p),' * caf']);
        end;
        DW_CFA_restore..$FF: begin
         // register
          DebugLn(FPDBG_DWARF_VERBOSE, ['DW_CFA_restore R', pb[-1] and $3F]);
        end;
      else
        DebugLn(FPDBG_DWARF_VERBOSE, ['Undefined $', IntToHex(pb[-1], 2)]);
      end;
    until p >= MaxAddr;
  end;

var
  p, next: Pointer;
  pb: PByte absolute p;
  pi: PInteger absolute p;
  pc: PCardinal absolute p;
  pq: PQWord absolute p;

  len: QWord;
  version: Byte;
  IsCie: Boolean;

  s: String;
begin
  p := AData;
  while p < Adata + ASize do
  begin
    DebugLn(FPDBG_DWARF_VERBOSE, ['[', {%H-}PtrUInt(p) - {%H-}PtrUInt(AData), ']']);

    Is64bit := pi^ = -1;
    if Is64bit
    then begin
      Inc(pi);
      len := pq^;
      Inc(pq);
      IsCie := Int64(pq^) = -1;
    end
    else begin
      len := pc^;
      Inc(pc);
      IsCie := pi^ = -1;
    end;
    next := p + len;

    if IsCie
    then DebugLn(FPDBG_DWARF_VERBOSE, ['=== CIE ==='])
    else DebugLn(FPDBG_DWARF_VERBOSE, ['--- FDE ---']);

    DebugLn(FPDBG_DWARF_VERBOSE, ['Length: ', len]);

    if IsCie
    then begin
      Inc(pi);
      version := pb^;
      DebugLn(FPDBG_DWARF_VERBOSE, ['Version: ', version]);
      Inc(pb);
      S := Pchar(p);
      DebugLn(FPDBG_DWARF_VERBOSE, ['Augmentation: ', S]);
      Inc(p, Length(s) + 1);
      DebugLn(FPDBG_DWARF_VERBOSE, ['Code alignment factor (caf): ', ULEB128toOrdinal(p)]);
      DebugLn(FPDBG_DWARF_VERBOSE, ['Data alignment factor (daf): ', SLEB128toOrdinal(p)]);
      DbgOut(FPDBG_DWARF_VERBOSE, ['Return addr: R']);
      if version <= 2
      then begin
        DebugLn(FPDBG_DWARF_VERBOSE, [pb^]);
        Inc(pb);
      end
      else DebugLn(FPDBG_DWARF_VERBOSE, [ULEB128toOrdinal(p)]);
    end
    else begin
      if pc^ > ASize
      then DebugLn(FPDBG_DWARF_VERBOSE, ['CIE: $', IntToHex(pc^, 8), ' (=address ?) -> offset: ', pc^ - AStart - FLoader.ImageBase])
      else DebugLn(FPDBG_DWARF_VERBOSE, ['CIE: ', pc^]);
      Inc(pc);
      DebugLn(FPDBG_DWARF_VERBOSE, ['InitialLocation: $', IntToHex(pc^, 8)]);
      Inc(pc);
      DebugLn(FPDBG_DWARF_VERBOSE, ['Address range: ', pc^]);
      Inc(pc);
    end;
    DebugLn(FPDBG_DWARF_VERBOSE, ['Instructions:']);
    DecodeInstructions(p, next);

    p := next;
  end;
end;

initialization
  FPDBG_DWARF_VERBOSE       := DebugLogger.FindOrRegisterLogGroup('FPDBG_DWARF_VERBOSE' {$IFDEF FPDBG_DWARF_VERBOSE} , True {$ENDIF} );
  FPDBG_DWARF_WARNINGS      := DebugLogger.FindOrRegisterLogGroup('FPDBG_DWARF_WARNINGS' {$IFDEF FPDBG_DWARF_WARNINGS} , True {$ENDIF} );
end.

