fpc/compiler/ogmacho.pas
2020-04-25 12:59:25 +00:00

1244 lines
38 KiB
ObjectPascal

{
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;
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;
{ 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;
loc_Notused:
;
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';
labelmaxlen : -1;
comment : '#';
dollarsign: '$';
);
initialization
{$ifdef i386}
RegisterAssembler(as_i386_darwin_info,TMachoAssembler);
{$endif i386}
end.