{
    Copyright (c) 2009-2010 by Dmitry Boyarintsev

    Contains the binary mach-o writer

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

 ****************************************************************************
}
unit ogmacho;

{$i fpcdefs.inc}

interface

uses
  cclasses,
  globals, globtype, verbose,
  owbase, ogbase,
  aasmbase, assemble,
  macho, machoutils,
  systems,
  { assembler }
  cpuinfo,cpubase,aasmtai,aasmdata; {for system constants}

type

    { TMachoRawWriter }

    TMachoRawWriter=class(TRawWriter)
      private
        fwriter : tobjectwriter;
      public
        constructor Create(awriter: tobjectwriter);
        procedure WriteRaw(const data; datasize: Integer); override;
      end;

    { TmachoObjSection }

    TMachoSectionType=(mst_Normal, mst_ObjC, mst_Stabs, mst_Dwarf);

    TmachoObjSection=class(tObjSection)
      public
        nmsegment : string;  {mach-o segment name}
        nmsection : string;  {mach-o section name}

        inSegIdx  : Integer; {section index inside segment. one-based number}
        RelocOfs  : aword;   {file offset to the first relocation symbol}
        IndIndex  : Integer; {index in indirect table (see DysymTableCommand) for lazy and non-lazy symbol pointers}

        machoSec  : TMachoSectionType;
        function GetRelocCount: Integer;
        function FileSize: Integer;
        constructor create(AList:TFPHashObjectList; const Aname:string; Aalign:longint; Aoptions:TObjSectionOptions);override;
      end;

    { TmachoObjData }

    TmachoObjData=class(TObjData)
      public
        debugcount: Integer;
        constructor create(const n:string); override;
        procedure CreateDebugSections; override;
        function sectionname(atype:TAsmSectiontype; const aname:string; aorder:TAsmSectionOrder):string;override;
        function sectiontype2align(atype:TAsmSectiontype):longint;override;
        function sectiontype2options(atype:TAsmSectiontype):TObjSectionOptions;override;
        procedure writereloc(data:aint; len:aword; p:TObjSymbol; reltype:TObjRelocationType);override;
      public
      end;

    { TMachoObjectOutput }

    TMachoSymbolLocation=(loc_Notused, loc_Local, loc_External, loc_Undef);

    TMachoObjectOutput=class(TObjOutput)
      private
        machoData   : TMachoObjData;
        mfile       : TMachOWriter;
        cputarget   : cpu_type_t;

        stabsec     : TmachoObjSection;
        strsec      : TmachoObjSection;

        sectionscnt : integer;
        memofs      : aword;
        fileofs     : aword;

        symstrofs   : aword;
        symlen      : aword;
        symCount    : aint;

        iLocal      : Integer;
        iExtern     : Integer;
        iUndef      : Integer;
        iIndir      : Integer;

        symList     : TFPObjectList;
        IndirIndex  : tdynamicarray;

        relcount : integer;
      protected
        procedure TrailZeros;
        function current_cpu_type: cpu_type_t;inline;

        {sections}
        procedure FixSectionRelocs(s: TMachoObjSection);
        procedure section_count_sections(p:TObject;arg:pointer);
        procedure section_set_datamempos(p:TObject;arg:pointer);
        procedure section_set_relocpos(p:TObject;arg:pointer);

        procedure section_write_data(p:TObject;arg:pointer);
        procedure section_write_relocdata(p:TObject;arg:pointer);
        procedure section_prepare_indirect(s: TObjSection);

        {symbols}
        procedure symbol_write_nlist(sym:TObjSymbol; symstr: tdynamicarray);
        function dysymbol_location(sym: TObjSymbol): TMachoSymbolLocation;

        function symWriteName(s: TObjSymbol): string;
        procedure InitSymbolIndexes(var sCount: aint; var symStrLen: aword);

        {mach-o file related}
        procedure writeSectionsHeader(s: TMachoObjSection);
        procedure writeSymTabCommand;
        procedure writeSymbols(symstr: tdynamicarray);
        procedure writeDySymTabCommand(IndOffset: aword; IndCount: Integer);
        procedure writeDysymbols;

        function writedata(data:TObjData):boolean;override;
      public
        constructor Create(AWriter:TObjectWriter);override;
      end;

    { TMachoAssembler }

    TMachoAssembler=class(TInternalAssembler)
      public
        constructor create(info: pasminfo; smart:boolean);override;
      end;


implementation

uses
  owar;

  { TmachoObjData }

  constructor TmachoObjData.create(const n: string);
    begin
      inherited create(n);
      CObjSection:=TmachoObjSection;
    end;


  { TmachoObjData.CreateDebugSections. }
  { note: mach-o file has specific symbol table command (not sections) to keep symbols and symbol string }
  procedure TmachoObjData.CreateDebugSections;
    begin
      inherited CreateDebugSections;
      if target_dbg.id=dbg_stabs then
        begin
          stabssec:=createsection(sec_stab);
          stabstrsec:=createsection(sec_stabstr);
        end;
    end;


  function TmachoObjData.sectionname(atype: TAsmSectiontype; const aname: string; aorder: TAsmSectionOrder): string;
    const
      DwarfSect : array [sec_debug_frame..sec_debug_ranges] of string
        = ('sec_debug_frame','__debug_info','__debug_line','__debug_abbrev','__debug_aranges','__debug_ranges');
    begin
      case atype of
        sec_user: Result:=aname;
        sec_bss:  Result:=MakeSectionName(seg_DATA, '__common');
        sec_stab: Result:='.stabs';
        sec_stabstr: Result:='.stabsstr';
        sec_fpc:  Result:=MakeSectionName(seg_TEXT, '.fpc');
        sec_stub: Result:=MakeSectionName(seg_IMPORT, '__jump_table');
        sec_code:
          if (aname='fpc_geteipasebx') or
             (aname='fpc_geteipasecx') then
            Result:=MakeSectionName(seg_TEXT, '__textcoal_nt')
          else
            Result:=MakeSectionName(seg_TEXT, '__text');
        sec_rodata_norel: Result:=MakeSectionName(seg_TEXT, '__const'); {.const}
        sec_rodata:       Result:=MakeSectionName(seg_DATA, '__const');
        sec_data:         Result:=MakeSectionName(seg_DATA, '__data');
        sec_data_nonlazy: Result:=MakeSectionName(seg_DATA, '__nl_symbol_ptr');
        sec_data_lazy:    Result:=MakeSectionName(seg_DATA, '__la_symbol_ptr');
        sec_init_func:    Result:=MakeSectionName(seg_DATA, '__mod_init_func');
        sec_term_func:    Result:=MakeSectionName(seg_DATA, '__mod_term_func');


        sec_objc_class:           Result:='__OBJC __class';
        sec_objc_meta_class:      Result:='__OBJC __meta_class';
        sec_objc_cat_cls_meth:    Result:='__OBJC __cat_cls_meth';
        sec_objc_cat_inst_meth:   Result:='__OBJC __cat_inst_meth';
        sec_objc_protocol:      Result:='__OBJC __protocol';
        sec_objc_string_object: Result:='__OBJC __cstring';
        sec_objc_cls_meth:        Result:='__OBJC __cls_meth';
        sec_objc_inst_meth:       Result:='__OBJC __inst_meth';
        sec_objc_cls_refs:        Result:='__OBJC __cls_refs';
        sec_objc_message_refs:    Result:='__OBJC __message_refs';
        sec_objc_symbols:         Result:='__OBJC __symbols';
        sec_objc_category:      Result:='__OBJC __categories';
        sec_objc_class_vars:    Result:='__OBJC __cls_vars';
        sec_objc_instance_vars: Result:='__OBJC __inst_vars';
        sec_objc_module_info:     Result := '__OBJC __module_info';
        sec_objc_class_names:     Result:='__TEXT __cstring';
        sec_objc_meth_var_types: Result:='__OBJC __var_types';
        sec_objc_meth_var_names:  Result:='__TEXT __cstring';
        sec_objc_selector_strs: Result:='__TEXT __cstring';
        sec_objc_protocol_ext:    Result:='__OBJC __protocol_ext';
        sec_objc_class_ext:       Result:='__OBJC __class_ext';
        sec_objc_property:        Result:='__OBJC __property';
        sec_objc_image_info:      Result:='__OBJC __image_info';
        sec_objc_cstring_object:  Result:='__OBJC __cstring_object';
        sec_objc_sel_fixup:       Result:='__OBJC __sel_fixup';
        { Objective-C non-fragile ABI }
        sec_objc_data:        Result:='__OBJC __data';
        sec_objc_const:       Result:='__OBJC __const';
        sec_objc_sup_refs:      Result:='__OBJC __supc_refs';
        sec_objc_classlist:     Result:='__OBJC __classlist';
        sec_objc_nlclasslist:   Result:='__OBJC __nlclasslist';
        sec_objc_catlist:       Result:='__OBJC __catlist';
        sec_objc_nlcatlist:     Result:='__OBJC __nlcatlist';
        sec_objc_protolist:     Result:='__OBJC __protolist';

        sec_debug_frame,
        sec_debug_info,
        sec_debug_line,
        sec_debug_abbrev,
        sec_debug_aranges,
        sec_debug_ranges:
          Result:=MakeSectionName(seg_DWARF, DwarfSect[atype])

      else
        Result:=MakeSectionName(seg_DATA, '__data');
      end;
    end;


  procedure TmachoObjData.writereloc(data: aint; len: aword; p: TObjSymbol; reltype: TObjRelocationType);
    var
      symaddr : longint;
    begin
      {stabs relocation}
      case TMachoObjSection(CurrObjSec).machoSec of

        mst_Stabs:
          begin
            if Assigned(p) then
              begin
                data:=p.address;
                CurrObjSec.addsymreloc(CurrObjSec.Size,p,reltype);
              end;
            CurrObjSec.write(data, len);
          end;

        mst_Dwarf:
          begin
            if Assigned(p) then
              begin
                CurrObjSec.addsectionReloc(CurrObjSec.Size,p.objsection,reltype);
                data:=p.address;
              end;
            CurrObjSec.write(data, len);
          end;

      else
        if assigned(p) then
          begin
            { real address of the symbol }
            symaddr:=p.address;
            { Local ObjSymbols can be resolved already or need a section reloc }
            if (p.bind=AB_LOCAL) and
               (reltype in [RELOC_RELATIVE,RELOC_ABSOLUTE{$ifdef x86_64},RELOC_ABSOLUTE32{$endif x86_64}]) then
              begin
                { For a reltype relocation in the same section the value can be calculated }
                if (p.objsection=CurrObjSec) and
                   (reltype=RELOC_RELATIVE) then
                  inc(data,symaddr-len-CurrObjSec.Size)
                else
                  begin
                    if (p.typ=AT_NONE) then
                      begin
                        {undefined symbol, using section}
                        CurrObjSec.addsectionreloc(CurrObjSec.Size,p.objsection,reltype);
                        data:=symaddr-len-CurrObjSec.Size;
                      end
                    else
                      begin
                        CurrObjSec.addsymreloc(CurrObjSec.Size,p,reltype);
                        if Assigned(p.objsection) and
                           (p.objsection.Name='__TEXT __textcoal_nt') then
                          data:=symaddr-len-CurrObjSec.Size
                        else
                          data:=p.objsection.Size;
                      end;
                  end;
              end
            else if (p.bind=AB_GLOBAL) and
                    not Assigned(p.indsymbol) and
                    (reltype<>RELOC_PIC_PAIR) then
              begin
                CurrObjSec.addsectionreloc(CurrObjSec.Size,p.objsection,reltype);
                data:=p.address;
              end
            else
              CurrObjSec.addsymreloc(CurrObjSec.Size,p,reltype);
          end; {if assigned(p) }

        CurrObjSec.write(data, len);
      end;
    end;


  function TmachoObjData.sectiontype2align(atype: TAsmSectiontype): longint;
    begin
      case atype of
        sec_bss:
          Result:=4;
        sec_stabstr, sec_stab:
          Result:=1;
        sec_stub, sec_data_lazy, sec_data_nonlazy:
          Result:=4;
      else
        Result:=inherited sectiontype2align(atype);
      end;
    end;


  function TmachoObjData.sectiontype2options(atype: TAsmSectiontype): TObjSectionOptions;
    begin
      case atype of
        sec_objc_meth_var_names,
        sec_objc_class_names: Result:=[oso_data, oso_load];
      else
        Result:=inherited sectiontype2options(atype);
      end
    end;


  { TMachoAssembler }

  constructor TMachoAssembler.create(info: pasminfo; smart: boolean);
    begin
      inherited;
      CObjOutput:=TMachoObjectOutput;
      CInternalAr:=tarobjectwriter;
    end;


  { TMachoObjectOutput }

  procedure TMachoObjectOutput.FixSectionRelocs(s: TMachoObjSection);
    var
      i   : integer;
      ro  : TObjRelocation;
      dw  : aword;
    begin
      {todo: is it I386 only core}
      if not Assigned(s.Data) then
        Exit;

      for i:=0 to s.ObjRelocations.Count-1 do
        begin
          ro:=TObjRelocation(s.ObjRelocations[i]);

          if (Assigned(ro.objsection)) and
             (ro.objsection.Name='__TEXT __textcoal_nt') then
            Continue;

          if Assigned(ro.objsection) then
            begin
              s.Data.seek(ro.DataOffset);
              s.Data.read(dw, sizeof(aword));

              dw:=dw+ro.objsection.MemPos;

              s.Data.seek(ro.DataOffset);
              s.Data.write(dw, sizeof(aword));
            end
          else
            begin
              if ro.symbol.Name='fpc_geteipasebx' then
                Continue;
              if Assigned(ro.symbol.indsymbol) or
                 (ro.typ=RELOC_PIC_PAIR) then
                begin
                  s.Data.seek(ro.DataOffset);
                  s.Data.read(dw, sizeof(aword));
                  dw:=ro.symbol.address-dw;
                  s.Data.seek(ro.DataOffset);
                  s.Data.write(dw, sizeof(aword));
                end
              else if (ro.symbol.bind=AB_LOCAL) then
                begin
                  dw:=ro.symbol.address;
                  s.Data.seek(ro.DataOffset);
                  s.Data.write(dw, sizeof(aword));
                end;
            end;

        end;
      s.Data.seek(s.Data.Size);
    end;


  procedure TMachoObjectOutput.section_count_sections(p: TObject; arg: pointer);
    var
      s : TMachoObjSection;
    begin
      s:=TMachoObjSection(p);
      if s.machoSec=mst_Stabs then
        Exit;
      inc(sectionscnt);
      s.inSegIdx:=sectionscnt;
    end;


  procedure TMachoObjectOutput.section_set_datamempos(p: TObject; arg: pointer);
    var
      s : TMachoObjSection;
    begin
      s:=TMachoObjSection(p);
      if s.machoSec=mst_Stabs then
        Exit;

      s.setDataPos(fileofs);
      s.setMemPos(memofs);
      memofs:=Align(memofs+s.Size, s.SecAlign);

      fileofs:=AlignAddr(cputarget, fileofs);
    end;


  procedure TMachoObjectOutput.section_set_relocpos(p:TObject;arg:pointer);
    var
      s   : TMachoObjSection;
      sz  : Integer;
    begin
      s:=TMachoObjSection(p);
      if s.machoSec=mst_Stabs then
        Exit;

      sz:=s.GetRelocCount * sizeof(relocation_info);
      if sz > 0 then
        begin
          s.relocofs:=fileofs;
          inc(fileofs, sz);
          fileofs:=AlignAddr(cputarget, fileofs);
        end;
    end;


  procedure TMachoObjectOutput.section_write_data(p: TObject; arg: pointer);
    var
      s : TMachoObjSection;
    begin
      s:=TMachoObjSection(p);
      if s.machoSec=mst_Stabs then
        Exit;

      Writer.writezeros(s.DataAlignBytes);

      FixSectionRelocs(s);

      if s.Datapos<>FWriter.ObjSize then
        InternalError(200903101);
      if Assigned(s.data) then
        Writer.writearray(s.data);
      TrailZeros;
    end;


  procedure TMachoObjectOutput.section_write_relocdata(p: TObject; arg: pointer);
    var
      s       : TMachoObjSection;
      symsec  : TMachoObjSection;
      i       : Integer;
      dw      : aword;

      r       : relocation_info;
      sr      : scattered_relocation_info;
      ro      : TObjRelocation;
      symnum      : Integer;
      relpc       : Boolean;
      relextern   : Boolean;
      reltype     : Integer;

    begin
      s:=TMachoObjSection(p);

      {stabs relocation should not present in relocation table}
      if s.machoSec=mst_Stabs then
        Exit;
      {no relocation for the section}
      if s.relocofs=0 then
        Exit;
      {check file alignment}
      if s.relocofs<>FWriter.ObjSize then
        InternalError(200903102); {file misalignment}

      relcount:=s.ObjRelocations.Count;
      {the reversed order, is only to be alike Apple linker}
      for i:=s.ObjRelocations.Count-1 downto 0 do
      begin
        ro:=TObjRelocation(s.ObjRelocations[i]);

        {in-section relocation}
        if ro.symbol=nil then
          begin
            relextern:=false;
            relpc:=false;
            symnum:=TmachoObjSection(ro.objsection).inSegIdx;
            case ro.typ of
              RELOC_ABSOLUTE:
                begin
                  RelocInfo(ro.DataOffset, symnum, GENERIC_RELOC_VANILLA, ril_long, relpc, relextern, r);
                  mfile.WriteRelocation(r);
                end;
            else
              relpc:=ro.typ=RELOC_RELATIVE;
              RelocInfo(ro.DataOffset, symnum, GENERIC_RELOC_VANILLA, ril_long, relpc, relextern, r);
              mfile.WriteRelocation(r);
            end;

          end
        else
          begin
            symsec:=TMachoObjSection(ro.symbol.objsection);

            if Assigned(symsec) and
               (symsec.Name='__TEXT __textcoal_nt') then
              begin
                relextern:=true;
                symnum:=ro.symbol.symidx;
              end
            else if ro.symbol.bind=AB_EXTERNAL then
              begin
                relextern:=true;
                symnum:=ro.symbol.symidx;
              end
            else if Assigned(ro.symbol.objsection) and
                    (ro.symbol.bind=AB_LOCAL) and
                    (ro.symbol.typ in [AT_DATA,AT_METADATA]) then
              begin
                relextern:=false;
                symnum:=TMachoObjSection(ro.symbol.objsection).inSegIdx;
              end
            else if (ro.symbol.bind=AB_LOCAL) or
                    (ro.symbol.typ=AT_NONE) then
             begin
               relextern:=false;
                symnum:=s.inSegIdx
              end
            else
              begin
                relextern:=true;
                symnum:=ro.symbol.symidx;
              end;

            relpc:=false;
            relpc:=(ro.typ=RELOC_RELATIVE);
            if (ro.typ=RELOC_PIC_PAIR) then
              begin
                if ro.symbol.bind=AB_LOCAL then
                  reltype:=GENERIC_RELOC_LOCAL_SECTDIFF
                else
                  reltype:=GENERIC_RELOC_SECTDIFF;
                ScatterRelocInfo(ro.symbol.address, ro.DataOffset, reltype, ril_long, false, sr);
                mfile.WriteScatterReloc(sr);

                { the section data is already fixed to:   ro.SymbolOffset - Label.Offset }
                s.Data.seek(ro.DataOffset);
                s.Data.read(dw, sizeof(aword));
                dw:=ro.symbol.address-dw;
                ScatterRelocInfo(dw, 0, GENERIC_RELOC_PAIR, ril_long, false, sr);
                mfile.WriteScatterReloc(sr);
              end
            else
              begin
                RelocInfo(ro.DataOffset, symnum, GENERIC_RELOC_VANILLA, ril_long, relpc, relextern, r);
                mfile.WriteRelocation(r);
              end
          end;
        if Assigned(s.Data) then
          s.Data.seek(s.Data.size);
      end;
      TrailZeros;
    end;


    procedure TMachoObjectOutput.section_prepare_indirect(s: TObjSection);
      var
        t       : TObjSymbol;
        i       : Integer;
        anysym  : Boolean;
      begin
        if TmachoObjSection(s).machoSec=mst_Stabs then
          Exit;

        anysym:=false;
        for i:=0 to machoData.ObjSymbolList.Count-1 do
          begin
            t:=TObjSymbol(machoData.ObjSymbolList[i]);
            if (t.objsection=s) and Assigned(t.indsymbol) then
              begin
                if not anysym then
                  begin
                    {remember the index of the first indirect symbol. Will be used later at section header writting}
                    TmachoObjSection(s).indIndex:=IndirIndex.size div SizeOf(Integer);
                    anysym:=true;
                  end;
                IndirIndex.write(t.symidx, sizeof(Integer));
              end;
          end;

      end;


    procedure TMachoObjectOutput.symbol_write_nlist(sym:TObjSymbol; symstr: tdynamicarray);
      var
        n       : nlist_64;
        sec     : TmachoObjSection;
      begin
        sec:=TMachoObjSection(sym.objsection);
        FillChar(n, sizeof(n), 0);
        n.n_un.n_strx:=symstr.size;
        symstr.writestr(sym.Name+#0);

        if assigned(sec) and
           (sec.machoSec=mst_ObjC) and
           (sec.nmsection='__module_info') then
          begin
            n.n_type:=N_ABS or N_EXT;
            mfile.WriteNList(n);
            Exit;
          end;

        if (sym.typ=AT_NONE) then
          begin
            n.n_value:=0;
            if sym.bind<>AB_EXTERNAL then
              n.n_desc:=n.n_desc or REFERENCE_FLAG_UNDEFINED_LAZY;
            n.n_type:=n.n_type or N_EXT;
          end
        else if sym.bind=AB_LAZY then
          begin
            n.n_value:=0;
            n.n_type:=N_ABS or N_EXT;
            n.n_sect:=NO_SECT;
          end
        else
          begin
            n.n_value:=sym.address;

            if Assigned(sec) then
              begin
                n.n_sect:=sec.inSegIdx;
                n.n_type:=n.n_type or N_SECT;

                if (sym.typ=AT_FUNCTION) and
                   (sym.bind=AB_LOCAL) then
                  begin
                    n.n_type:=N_PEXT or N_EXT or N_SECT;
                    n.n_desc:=n.n_desc or N_WEAK_DEF;
                  end;
              end;
          end;

        if (sym.bind=AB_GLOBAL) and
           (n.n_type and N_PEXT=0) then
          n.n_type:=n.n_type or N_EXT;

        if (sym.typ=AT_FUNCTION) and
           (sym.bind=AB_GLOBAL) then
          n.n_desc:=n.n_desc or N_NO_DEAD_STRIP;

        if Assigned(sec) then
          begin
            if (sec.nmsection='__nl_symbol_ptr') then
              n.n_desc:=n.n_desc or REFERENCE_FLAG_UNDEFINED_NON_LAZY;
            if (sec.nmsegment=seg_Data) and (sec.nmsection='__const') then
              n.n_desc:=n.n_desc or N_NO_DEAD_STRIP;
          end;

        mfile.WriteNList(n);
      end;


    function TMachoObjectOutput.dysymbol_location(sym: TObjSymbol): TMachoSymbolLocation;
      begin
        if Assigned(sym.objsection) and
           (TMachoObjSection(sym.objsection).machoSec=mst_Stabs) then
          Result:=loc_Local
        else
          case sym.typ of
            AT_NONE:  Result:=loc_Undef;
            AT_LABEL: Result:=loc_Notused;
          else
            Result:=loc_External;
          end;
      end;


  procedure TMachoObjectOutput.writeSectionsHeader(s: TMachoObjSection);
    var
      sc      : TMachoSection;
    begin
      section_prepare_indirect(s);

      fillChar(sc, sizeof(sc), 0);
      sc.segname:=s.nmsegment;
      sc.sectname:=s.nmsection;
      sc.size:=s.Size;
      if s.FileSize>0 then
        sc.offset:=s.DataPos
      else
        sc.offset:=0;
      sc.addr:=s.MemPos;
      sc.nreloc:=s.GetRelocCount;
      sc.reloff:=s.relocofs;
      sc.flags:=GetSectionFlags(s.nmsegment, s.nmsection);
      sc.align:=MachoAlign(s.SecAlign);
      sc.indirectIndex:=s.indIndex;

      if (sc.flags and SECTION_TYPE)=S_SYMBOL_STUBS then
        sc.stubSize:=GetStubSize(cputarget, false);
      mfile.WriteSection(sc);
    end;


  procedure TMachoObjectOutput.writeSymTabCommand;
    begin
      mfile.WriteLoadCommand(LC_SYMTAB, sizeof(symtab_command));
      mfile.WriteUint32(fileofs); {symoff}
      mfile.WriteUint32(symCount); {nsyms}
      inc(fileofs, symCount*sizeNList(cputarget));
      fileofs:=AlignAddr(cputarget, fileofs);

      symstrofs:=fileofs;
      mfile.WriteUint32(fileofs); {stroff}
      mfile.WriteUint32(symlen); {strsize}

      inc(fileofs, symlen);
      fileofs:=AlignAddr(cputarget, fileofs);
    end;


    function TMachoObjectOutput.symWriteName(s: TObjSymbol): string;
      begin
        if not Assigned(s.indsymbol) then
          Result:=s.Name
        else
          Result:=s.indsymbol.Name;
      end;


{    function getSymWriteNameLength(s: TObjSymbol): Integer; inline;
      begin
        Result:=length(symWriteName(s))+1;
      end;}


    procedure TMachoObjectOutput.InitSymbolIndexes(var sCount: aint; var symStrLen: aword);
      var
        i         : integer;
        s         : TObjSymbol;
        stabcount : Integer;
      begin
        sCount:=0;
        symStrLen:=0;

        iIndir:=0;
        for i:=0 to machoData.ObjSymbolList.Count-1 do
          begin
            s:=TObjSymbol(machoData.ObjSymbolList[i]);
            if (s.typ=AT_LABEL) then
              Continue;

            if Assigned(s.indsymbol) then
              inc(iIndir);
          end;

        iLocal:=0;
        iExtern:=0;
        iUndef:=0;

        for i:=0 to machoData.ObjSymbolList.Count-1 do
          begin
            s:=TObjSymbol(machoData.ObjSymbolList[i]);
            if (s.typ=AT_LABEL) or
               Assigned(s.indsymbol) then
                 Continue;
            if (s.bind=AB_LOCAL) and
               (s.Name <> 'fpc_geteipasebx') then
              Continue;

            case dysymbol_location(s) of
              loc_Local:
                begin
                  symList.Insert(iLocal, s);
                  inc(iLocal); inc(iExtern); inc(iUndef);
                end;
              loc_External:
                begin
                  symList.Insert(iExtern, s);
                  inc(iExtern); inc(iUndef);
                end;
              loc_Undef:
                begin
                  symList.Insert(iUndef, s);
                  inc(iUndef);
                end;
            end;
            inc(symStrLen, length(s.Name)+1 );
          end;

        if Assigned(stabsec) then
          {skipping hdrsym! (added by ogbase) }
          stabcount:=stabsec.Size div sizeof(TObjStabEntry) - 1
        else
          stabcount:=0;

        for i:=0 to symList.Count-1 do
          TObjSymbol(symList[i]).symidx:=i+stabcount;
        sCount:=symList.Count+stabcount;

        for i:=0 to machoData.ObjSymbolList.Count-1 do
          with TObjSymbol(machoData.ObjSymbolList[i]) do
            if Assigned(indsymbol) then
              symidx:=indsymbol.symidx;

        if Assigned(strsec) then
          // 1 byte of zero name (that stands in the end of table, not at zero pos)
          inc(symlen, strsec.Size + 1)
        else
          inc(symlen); {the first zero byte}

        dec(iUndef, iExtern); { iUndef is count of undefined symbols (for dysymtable command) }
        dec(iExtern, iLocal); { iExtern is count of external symbols (for dysymtable command) }
        inc(iLocal, stabcount);
      end;


    procedure TMachoObjectOutput.writeSymbols(symstr: tdynamicarray);
      var
        i       : integer;
        s       : TObjSymbol;
        b       : byte;
        stab    : TObjStabEntry;
        ro      : TObjRelocation;
        sym     : TObjSymbol;
        addr    : aword;
        text    : TmachoObjSection;
        funofs  : AWord;
      begin
        if Assigned(stabsec) then
          begin
            for i:=0 to stabsec.ObjRelocations.Count - 1 do
              begin
                ro:=TObjRelocation(stabsec.ObjRelocations[i]);
                sym:=ro.symbol;
                addr:=sym.address;
                if Assigned(sym.objsection) then
                  begin
                    stabsec.Data.seek(ro.DataOffset-3);
                    b:=TmachoObjSection(sym.objsection).inSegIdx;
                    stabsec.Data.write(b, sizeof(b));
                  end;
                stabsec.Data.seek(ro.DataOffset);
                stabsec.Data.write(addr, sizeof(addr));
              end;

            stabsec.Data.seek(sizeof(TObjStabEntry));
            funofs:=0;
            text:=TmachoObjSection(machoData.ObjSectionList.Find(MakeSectionName(seg_TEXT, '__text')));
            for i:=1 to stabsec.Data.size div SizeOf(TObjStabEntry) - 1 do
              begin
                stabsec.Data.read(stab, sizeof(stab));
                case stab.ntype of
                  N_FUN:
                    begin
                      if stab.strpos=0 then
                        funofs:=0
                      else
                        funofs:=stab.nvalue;
                    end;
                  N_SLINE,N_RBRAC,N_LBRAC:
                    begin
                      if Assigned(text) then
                        begin
                          { SLINE are expected to be in  __TEXT __text only }
                          stab.nother:=text.inSegIdx;
                          inc(stab.nvalue, funofs);
                        end;
                    end;
                  N_OSO:
                    begin
                      { null-terminated string is the first in the list         }
                      { apple-gdb doesn't recognize it as zero-string for N_OSO }
                      { another zero-string should be added to the list         }
                      if stab.strpos=0 then
                        stab.strpos:=symstr.Size;
                    end;
                end;
                FWriter.write(stab, sizeof(stab));
              end;
          end;

        symstr.Seek(symStr.size);
        b:=0;
        symstr.Write(b,1);

        for i:=0 to symList.Count-1 do
          begin
            s:=TObjSymbol(symList[i]);
            symbol_write_nlist(s, symstr);
          end;
      end;


  procedure TMachoObjectOutput.writeDySymTabCommand(IndOffset: aword; IndCount: Integer);
    begin
      mfile.WriteLoadCommand(LC_DYSYMTAB, sizeof(dysymtab_command));

      mfile.WriteUint32(0); {ilocalsym}
      mfile.WriteUint32(iLocal); {nlocalsym}

      mfile.WriteUint32(iLocal); {iextdefsym}
      mfile.WriteUint32(iExtern); {nextdefsym}

      mfile.WriteUint32(iLocal + iExtern); {iundefsym}
      mfile.WriteUint32(iUndef); {nundefsym}

      mfile.WriteUint32(0); {tocoff}
      mfile.WriteUint32(0); {ntoc}
      mfile.WriteUint32(0); {modtaboff}
      mfile.WriteUint32(0); {nmodtab}
      mfile.WriteUint32(0); {extrefsymoff}
      mfile.WriteUint32(0); {nextrefsyms}
      mfile.WriteUint32(IndOffset);  {indirectsymoff}
      mfile.WriteUint32(IndCount);   {nindirectsyms}
      mfile.WriteUint32(0); {extreloff}
      mfile.WriteUint32(0); {nextrel}
      mfile.WriteUint32(0); {locreloff}
      mfile.WriteUint32(0); {nlocrel}
    end;


  procedure TMachoObjectOutput.writeDysymbols;
    var
      i   : integer;
      idx : LongWord;
    begin
      IndirIndex.seek(0);
      for i:=0 to (IndirIndex.size div sizeof(Integer))-1 do
        begin
          IndirIndex.read(idx, sizeof(idx));
          mfile.WriteUint32(idx);
        end;
    end;


  function AddSectionToSegment(var segment: TMachoSegment; section : TMachoObjSection): boolean;
    begin
      { sections must be attached one-by-one to the segment }
      if segment.fileoff=0 then
        segment.fileoff:=section.DataPos;

      if (segment.fileoff+segment.filesize)<(section.FileSize+section.DataPos) then
        segment.filesize:=section.FileSize+section.DataPos;

      inc(segment.nsects);
      inc(segment.vmsize, section.size);
      Result:=true;
   end;


  procedure TMachoObjectOutput.TrailZeros;
    var
      sz : LongWord;
    begin
      sz:=AlignAddr(cputarget, FWriter.Size);
      if sz - FWriter.Size>0 then
        FWriter.WriteZeros(sz-FWriter.Size);
    end;


  function TMachoObjectOutput.current_cpu_type: cpu_type_t;
    begin
{$if defined(powerpc)}
      result:=CPU_TYPE_POWERPC;
{$elseif defined(powerpc64)}
      result:=CPU_TYPE_POWERPC64;
{$elseif defined(i386)}
      result:=CPU_TYPE_I386;
{$elseif defined(x86_64)}
      result:=CPU_TYPE_X86_64;
{$elseif defined(arm)}
      result:=CPU_TYPE_ARM;
{$elseif defined(aarch64)}
      result:=CPU_TYPE_ARM64;
{$else}
      result:=CPU_TYPE_ANY;
{$endif}
    end;

  function TMachoObjectOutput.writedata(data: TObjData): boolean;
    var
      header  : TMachHeader;
      seg     : TMachoSegment;
      secobj  : TMachoObjSection;
      i       : Integer;

      symstr  : tdynamicarray;
      segSize : integer; {size of a segment command - platform dependant}
      sctSize : integer; {size of a single section header - platform dependant}

      indOfs: aword;   {indirect symbol offset}

    begin
      symList:=TFPObjectList.Create(false);
      IndirIndex:=tdynamicarray.Create(1024);

      result:=false;
      machoData:=TMachoObjData(data);

      cputarget:=current_cpu_type;
      segSize:=sizeSegment(cputarget);
      sctSize:=sizeSection(cputarget);

      sectionscnt:=0;
      stabsec:=TMachoObjSection(machoData.ObjSectionList.Find('.stabs'));
      strsec:=TMachoObjSection(machoData.ObjSectionList.Find('.stabsstr'));

      {count number of sections}
      machoData.ObjSectionList.ForEachCall(@section_count_sections, nil);

      {sections data is written after machheader,load-commands.   }
      {   basic loadcommands for MH_OBJECT are                    }
      {   single LC_SEGMENT, containing all sections headers      }
      {   symbol linking information at LC_SYMTAB and LC_DYSYMTAB }
      header.cputype:=cputarget;
      header.cpusubtype:=CPU_SUBTYPE_i386_ALL;
      header.filetype:=MH_OBJECT;
      header.ncmds:=3;
      header.sizeofcmds:=segSize+sctSize*sectionscnt+sizeof(symtab_command)+sizeof(dysymtab_command);
      header.flags:=0;

      {setting sections data and memory pos}
      fileofs:=sizeMachHeader(cputarget)+header.sizeofcmds;
      fileofs:=AlignAddr(cputarget, fileofs);
      memofs:=0;

      machoData.ObjSectionList.ForEachCall(@section_set_datamempos, nil);
      fileofs:=AlignAddr(cputarget, fileofs);

      {setting sections relocation offsets}
      machoData.ObjSectionList.ForEachCall(@section_set_relocpos, nil);
      fileofs:=AlignAddr(cputarget, fileofs);

      {creating actual mach-o file writer}
      mfile:=AllocMachoWriter(cputarget, TMachoRawWriter.Create(writer), true);
      {writing macho-o header}
      mfile.WriteHeader(header);

      {starting the first segment command}
      InitSegment(seg);

      {initialze symbols. some sections (non_lazy, lazy pointers) are effected}
      InitSymbolIndexes(symCount, symlen);

      for i:=0 to machoData.ObjSectionList.Count-1 do
        begin
          secobj:=TmachoObjSection(machoData.ObjSectionList[i]);
          if secobj.machoSec=mst_Stabs then
            Continue;
          AddSectionToSegment(seg, secobj);
        end;

      {writting segment command}
      {for MH_OBJECT, all sections are stored in the single segment}
      mfile.WriteSegmentCmd(seg, segSize+(seg.nsects)*sctSize);

      {section headers are written inside segment command}
      for i:=0 to machoData.ObjSectionlist.Count - 1 do
        begin
          secobj:=TmachoObjSection(machoData.ObjSectionList[i]);
          if secobj.machoSec=mst_Stabs then
            Continue;
          writeSectionsHeader(secobj);
        end;
      TrailZeros;

      if IndirIndex.size div sizeof(Integer)<>iIndir then
        InternalError(2009121001);
      if iIndir>0 then
        indOfs:=fileOfs
      else
        indOfs:=0;
      inc(fileofs, IndirIndex.size);

      {write symtab command}
      {initilize symbos order. local first, extern second, undef last}
      writeSymTabCommand;
      TrailZeros;

      {write dysymtab command}
      writeDySymTabCommand(indofs, iIndir);
      TrailZeros;

      {writting sections data, to precalculated offsets}
      {if precalculated offsets, doesn't match actual written offsets, internal error is risen}
      machoData.ObjSectionList.ForEachCall(@section_write_data, nil);

      {writting relocation offsets}
      machoData.ObjSectionList.ForEachCall(@section_write_relocdata, nil);

      {writting dyn symbol tables (indirect symbols arrays)}
      writeDysymbols;

      {writting symbol table}
      if Assigned(strsec) then
        symstr:=strsec.Data
      else
        symstr:=tdynamicarray.create(1024);

      writeSymbols(symstr);
      TrailZeros;

      {writting symbol table strings}
      FWriter.writearray(symstr);
      // terminating null name
      TrailZeros;

      if not Assigned(strsec) then
        symstr.Free;

      TrailZeros;

      mfile.Free;
      symList.Free;
      IndirIndex.Free;
    end;


  constructor TMachoObjectOutput.Create(AWriter: TObjectWriter);
    begin
      inherited Create(AWriter);
      CObjData:=TMachoObjData;
    end;


  { TMachoRawWriter }

  constructor TMachoRawWriter.Create(awriter: tobjectwriter);
    begin
      inherited Create;
      fwriter:=awriter;
    end;


  procedure TMachoRawWriter.WriteRaw(const data; datasize: Integer);
    begin
      fwriter.Write(data, datasize);
    end;


  { TmachoObjSection }

  function TmachoObjSection.GetRelocCount: Integer;
    var
      i: integer;
      r: TObjRelocation;
    begin
      Result:=ObjRelocations.Count;
      for i:=0 to ObjRelocations.Count-1 do
        begin
          r:=TObjRelocation(ObjRelocations[i]);
          if (r.typ=RELOC_PIC_PAIR) then
            inc(Result);
        end;
    end;


  function TmachoObjSection.FileSize: Integer;
    begin
      if Assigned(data) then
        Result:=data.size
      else
        Result:=0;
    end;


  constructor TmachoObjSection.create(AList: TFPHashObjectList;
    const Aname: string; Aalign: longint; Aoptions: TObjSectionOptions);
    begin
      if Aname = '__TEXT __textcoal_nt' then
        Aalign:=4;

      inherited create(AList, Aname, Aalign, Aoptions);
      GetSegmentSectionName(aName, nmsegment, nmsection);
      if (aname='.stabs') or
         (aname='.stabsstr') then
        machoSec:=mst_Stabs
      else if nmsegment=seg_DWARF then
        machoSec:=mst_Dwarf
      else if nmsegment=seg_OBJC then
        machoSec:=mst_ObjC
      else
        machoSec:=mst_Normal;
    end;


  const
    as_i386_darwin_info : tasminfo =
      (
        id     : as_i386_macho;
        idtxt  : 'MACHO';
        asmbin : '';
        asmcmd : '';
        supported_targets : [system_i386_darwin,system_i386_iphonesim];
        flags : [af_outputbinary,af_smartlink_sections,af_supports_dwarf{, af_stabs_use_function_absolute_addresses}];
        labelprefix : '.L';
        comment : '#';
        dollarsign: '$';
      );

initialization
{$ifdef i386}
  RegisterAssembler(as_i386_darwin_info,TMachoAssembler);
{$endif i386}

end.

