unit FpImgReaderMachoFile;

{$T-} // TODO: Fix compilation with -Sy.
{$mode objfpc}{$H+}

interface

//todo: powerpc, x86_64

uses
  Classes, SysUtils, macho, FpImgReaderBase,
  FpDbgCommon;

type
  TMachOsection = class(TObject)
    is32   : Boolean;
    sec32  : section;
    sec64  : section_64;
  end;

  { TMachOFile }

  TMachOFile = class(TObject)
  private
    cmdbuf    : array of byte;
    FTargetInfo   : TTargetDescriptor;
  public
    header    : mach_header;
    commands  : array of pload_command;
    sections  : TFPList;
    UUID      : TGuid;
    constructor Create;
    destructor Destroy; override;
    function  LoadFromFile(ALoader: TDbgFileLoader): Boolean;
    property TargetInfo: TTargetDescriptor read FTargetInfo;
  end;


implementation


{ TMachOFile }

constructor TMachOFile.Create;
begin
  sections := TFPList.Create;
end;

destructor TMachOFile.Destroy;
var
  i : integer;
begin
  for i := 0 to sections.Count - 1 do TMachOsection(sections[i]).Free;
  sections.Free;
  inherited Destroy;
end;

function TMachOFile.LoadFromFile(ALoader: TDbgFileLoader): Boolean;
var
  i   : Integer;
  j   : Integer;
  ofs : Integer;
  sc32: psection;
  sc64: psection_64;
  idcm: puuid_command;
  s   : TMachOsection;
  hs  : integer;
  i64 : boolean;
begin
  Result :=  ALoader.Read(0, sizeof(header), @header) = sizeof(header);
  if not Result then Exit;
  i64 := (header.magic = MH_CIGAM_64) or (header.magic = MH_MAGIC_64);
  Result := (header.magic = MH_MAGIC) or (header.magic = MH_CIGAM) or i64;

  if i64 then
  begin
    hs := sizeof(mach_header_64);
    FTargetInfo.bitness := b64;
  end
  else
  begin
    hs := SizeOf(mach_header);
    FTargetInfo.bitness := b32;
  end;
  case header.cputype of
    CPU_TYPE_I386       : FTargetInfo.MachineType := mt386;
    CPU_TYPE_ARM        : FTargetInfo.MachineType := mtARM;
    CPU_TYPE_SPARC      : FTargetInfo.MachineType := mtSPARC;
    //CPU_TYPE_ALPHA      : FTargetInfo.MachineType := mtALPHA;
    CPU_TYPE_POWERPC    : FTargetInfo.MachineType := mtPPC;
    CPU_TYPE_POWERPC64  : FTargetInfo.MachineType := mtPPC;
    CPU_TYPE_X86_64     : FTargetInfo.MachineType := mtX86_64;
    CPU_TYPE_ARM64      : FTargetInfo.MachineType := mtARM;
  else
    FTargetInfo.machineType := mtNone;
  end;

  SetLength(cmdbuf, header.sizeofcmds);
  ALoader.Read(hs, header.sizeofcmds, @cmdbuf[0]);

  SetLength(commands, header.ncmds);
  ofs := 0;
  for i := 0 to header.ncmds - 1 do begin
    commands[i] := @cmdbuf[ofs];

    if commands[i]^.cmd = LC_SEGMENT then begin
      sc32 := @cmdbuf[ofs+sizeof(segment_command)];
      for j := 0 to psegment_command(commands[i])^.nsects- 1 do begin
        s := TMachOSection.Create;
        s.is32:=true;
        s.sec32:=sc32^;
        sections.add(s);
        inc(sc32);
      end;
    end
    else if commands[i]^.cmd = LC_SEGMENT_64 then begin
      sc64 := @cmdbuf[ofs+sizeof(segment_command_64)];
      for j := 0 to psegment_command_64(commands[i])^.nsects- 1 do begin
        s := TMachOSection.Create;
        s.is32:=False;
        s.sec64:=sc64^;
        sections.add(s);
        inc(sc64);
      end;
    end
    else if commands[i]^.cmd = LC_UUID then begin
      idcm := @cmdbuf[ofs];
      UUID:=PGuid(@(idcm^.uuid))^;
    end;
    inc(ofs, commands[i]^.cmdsize);
  end;

end;


end.

