1 {
2     Copyright (c) 2009-2010 by Dmitry Boyarintsev
3 
4     Contains the binary mach-o writer
5 
6     This program is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10 
11     This program is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15 
16     You should have received a copy of the GNU General Public License
17     along with this program; if not, write to the Free Software
18     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 
20  ****************************************************************************
21 }
22 unit ogmacho;
23 
24 {$i fpcdefs.inc}
25 
26 interface
27 
28 uses
29   cclasses,
30   globals, globtype, verbose,
31   owbase, ogbase,
32   aasmbase, assemble,
33   macho, machoutils,
34   systems,
35   { assembler }
36   cpuinfo,cpubase,aasmtai,aasmdata; {for system constants}
37 
38 type
39 
40     { TMachoRawWriter }
41 
42     TMachoRawWriter=class(TRawWriter)
43       private
44         fwriter : tobjectwriter;
45       public
46         constructor Create(awriter: tobjectwriter);
47         procedure WriteRaw(const data; datasize: Integer); override;
48       end;
49 
50     { TmachoObjSection }
51 
52     TMachoSectionType=(mst_Normal, mst_ObjC, mst_Stabs, mst_Dwarf);
53 
54     TmachoObjSection=class(tObjSection)
55       public
56         nmsegment : string;  {mach-o segment name}
57         nmsection : string;  {mach-o section name}
58 
59         inSegIdx  : Integer; {section index inside segment. one-based number}
60         RelocOfs  : aword;   {file offset to the first relocation symbol}
61         IndIndex  : Integer; {index in indirect table (see DysymTableCommand) for lazy and non-lazy symbol pointers}
62 
63         machoSec  : TMachoSectionType;
GetRelocCountnull64         function GetRelocCount: Integer;
FileSizenull65         function FileSize: Integer;
66         constructor create(AList:TFPHashObjectList; const Aname:string; Aalign:longint; Aoptions:TObjSectionOptions);override;
67       end;
68 
69     { TmachoObjData }
70 
71     TmachoObjData=class(TObjData)
72       public
73         debugcount: Integer;
74         constructor create(const n:string); override;
75         procedure CreateDebugSections; override;
sectionnamenull76         function sectionname(atype:TAsmSectiontype; const aname:string; aorder:TAsmSectionOrder):string;override;
sectiontype2alignnull77         function sectiontype2align(atype:TAsmSectiontype):longint;override;
sectiontype2optionsnull78         function sectiontype2options(atype:TAsmSectiontype):TObjSectionOptions;override;
79         procedure writereloc(data:aint; len:aword; p:TObjSymbol; reltype:TObjRelocationType);override;
80       public
81       end;
82 
83     { TMachoObjectOutput }
84 
85     TMachoSymbolLocation=(loc_Notused, loc_Local, loc_External, loc_Undef);
86 
87     TMachoObjectOutput=class(TObjOutput)
88       private
89         machoData   : TMachoObjData;
90         mfile       : TMachOWriter;
91         cputarget   : cpu_type_t;
92 
93         stabsec     : TmachoObjSection;
94         strsec      : TmachoObjSection;
95 
96         sectionscnt : integer;
97         memofs      : aword;
98         fileofs     : aword;
99 
100         symstrofs   : aword;
101         symlen      : aword;
102         symCount    : aint;
103 
104         iLocal      : Integer;
105         iExtern     : Integer;
106         iUndef      : Integer;
107         iIndir      : Integer;
108 
109         symList     : TFPObjectList;
110         IndirIndex  : tdynamicarray;
111 
112         relcount : integer;
113       protected
114         procedure TrailZeros;
current_cpu_typenull115         function current_cpu_type: cpu_type_t;inline;
116 
117         {sections}
118         procedure FixSectionRelocs(s: TMachoObjSection);
119         procedure section_count_sections(p:TObject;arg:pointer);
120         procedure section_set_datamempos(p:TObject;arg:pointer);
121         procedure section_set_relocpos(p:TObject;arg:pointer);
122 
123         procedure section_write_data(p:TObject;arg:pointer);
124         procedure section_write_relocdata(p:TObject;arg:pointer);
125         procedure section_prepare_indirect(s: TObjSection);
126 
127         {symbols}
128         procedure symbol_write_nlist(sym:TObjSymbol; symstr: tdynamicarray);
dysymbol_locationnull129         function dysymbol_location(sym: TObjSymbol): TMachoSymbolLocation;
130 
symWriteNamenull131         function symWriteName(s: TObjSymbol): string;
132         procedure InitSymbolIndexes(var sCount: aint; var symStrLen: aword);
133 
134         {mach-o file related}
135         procedure writeSectionsHeader(s: TMachoObjSection);
136         procedure writeSymTabCommand;
137         procedure writeSymbols(symstr: tdynamicarray);
138         procedure writeDySymTabCommand(IndOffset: aword; IndCount: Integer);
139         procedure writeDysymbols;
140 
writedatanull141         function writedata(data:TObjData):boolean;override;
142       public
143         constructor Create(AWriter:TObjectWriter);override;
144       end;
145 
146     { TMachoAssembler }
147 
148     TMachoAssembler=class(TInternalAssembler)
149       public
150         constructor create(info: pasminfo; smart:boolean);override;
151       end;
152 
153 
154 implementation
155 
156 uses
157   owar;
158 
159   { TmachoObjData }
160 
161   constructor TmachoObjData.create(const n: string);
162     begin
163       inherited create(n);
164       CObjSection:=TmachoObjSection;
165     end;
166 
167 
168   { TmachoObjData.CreateDebugSections. }
169   { note: mach-o file has specific symbol table command (not sections) to keep symbols and symbol string }
170   procedure TmachoObjData.CreateDebugSections;
171     begin
172       inherited CreateDebugSections;
173       if target_dbg.id=dbg_stabs then
174         begin
175           stabssec:=createsection(sec_stab);
176           stabstrsec:=createsection(sec_stabstr);
177         end;
178     end;
179 
180 
TmachoObjData.sectionnamenull181   function TmachoObjData.sectionname(atype: TAsmSectiontype; const aname: string; aorder: TAsmSectionOrder): string;
182     const
183       DwarfSect : array [sec_debug_frame..sec_debug_ranges] of string
184         = ('sec_debug_frame','__debug_info','__debug_line','__debug_abbrev','__debug_aranges','__debug_ranges');
185     begin
186       case atype of
187         sec_user: Result:=aname;
188         sec_bss:  Result:=MakeSectionName(seg_DATA, '__common');
189         sec_stab: Result:='.stabs';
190         sec_stabstr: Result:='.stabsstr';
191         sec_fpc:  Result:=MakeSectionName(seg_TEXT, '.fpc');
192         sec_stub: Result:=MakeSectionName(seg_IMPORT, '__jump_table');
193         sec_code:
194           if (aname='fpc_geteipasebx') or
195              (aname='fpc_geteipasecx') then
196             Result:=MakeSectionName(seg_TEXT, '__textcoal_nt')
197           else
198             Result:=MakeSectionName(seg_TEXT, '__text');
199         sec_rodata_norel: Result:=MakeSectionName(seg_TEXT, '__const'); {.const}
200         sec_rodata:       Result:=MakeSectionName(seg_DATA, '__const');
201         sec_data:         Result:=MakeSectionName(seg_DATA, '__data');
202         sec_data_nonlazy: Result:=MakeSectionName(seg_DATA, '__nl_symbol_ptr');
203         sec_data_lazy:    Result:=MakeSectionName(seg_DATA, '__la_symbol_ptr');
204         sec_init_func:    Result:=MakeSectionName(seg_DATA, '__mod_init_func');
205         sec_term_func:    Result:=MakeSectionName(seg_DATA, '__mod_term_func');
206 
207 
208         sec_objc_class:           Result:='__OBJC __class';
209         sec_objc_meta_class:      Result:='__OBJC __meta_class';
210         sec_objc_cat_cls_meth:    Result:='__OBJC __cat_cls_meth';
211         sec_objc_cat_inst_meth:   Result:='__OBJC __cat_inst_meth';
212         sec_objc_protocol:      Result:='__OBJC __protocol';
213         sec_objc_string_object: Result:='__OBJC __cstring';
214         sec_objc_cls_meth:        Result:='__OBJC __cls_meth';
215         sec_objc_inst_meth:       Result:='__OBJC __inst_meth';
216         sec_objc_cls_refs:        Result:='__OBJC __cls_refs';
217         sec_objc_message_refs:    Result:='__OBJC __message_refs';
218         sec_objc_symbols:         Result:='__OBJC __symbols';
219         sec_objc_category:      Result:='__OBJC __categories';
220         sec_objc_class_vars:    Result:='__OBJC __cls_vars';
221         sec_objc_instance_vars: Result:='__OBJC __inst_vars';
222         sec_objc_module_info:     Result := '__OBJC __module_info';
223         sec_objc_class_names:     Result:='__TEXT __cstring';
224         sec_objc_meth_var_types: Result:='__OBJC __var_types';
225         sec_objc_meth_var_names:  Result:='__TEXT __cstring';
226         sec_objc_selector_strs: Result:='__TEXT __cstring';
227         sec_objc_protocol_ext:    Result:='__OBJC __protocol_ext';
228         sec_objc_class_ext:       Result:='__OBJC __class_ext';
229         sec_objc_property:        Result:='__OBJC __property';
230         sec_objc_image_info:      Result:='__OBJC __image_info';
231         sec_objc_cstring_object:  Result:='__OBJC __cstring_object';
232         sec_objc_sel_fixup:       Result:='__OBJC __sel_fixup';
233         { Objective-C non-fragile ABI }
234         sec_objc_data:        Result:='__OBJC __data';
235         sec_objc_const:       Result:='__OBJC __const';
236         sec_objc_sup_refs:      Result:='__OBJC __supc_refs';
237         sec_objc_classlist:     Result:='__OBJC __classlist';
238         sec_objc_nlclasslist:   Result:='__OBJC __nlclasslist';
239         sec_objc_catlist:       Result:='__OBJC __catlist';
240         sec_objc_nlcatlist:     Result:='__OBJC __nlcatlist';
241         sec_objc_protolist:     Result:='__OBJC __protolist';
242 
243         sec_debug_frame,
244         sec_debug_info,
245         sec_debug_line,
246         sec_debug_abbrev,
247         sec_debug_aranges,
248         sec_debug_ranges:
249           Result:=MakeSectionName(seg_DWARF, DwarfSect[atype])
250 
251       else
252         Result:=MakeSectionName(seg_DATA, '__data');
253       end;
254     end;
255 
256 
257   procedure TmachoObjData.writereloc(data: aint; len: aword; p: TObjSymbol; reltype: TObjRelocationType);
258     var
259       symaddr : longint;
260     begin
261       {stabs relocation}
262       case TMachoObjSection(CurrObjSec).machoSec of
263 
264         mst_Stabs:
265           begin
266             if Assigned(p) then
267               begin
268                 data:=p.address;
269                 CurrObjSec.addsymreloc(CurrObjSec.Size,p,reltype);
270               end;
271             CurrObjSec.write(data, len);
272           end;
273 
274         mst_Dwarf:
275           begin
276             if Assigned(p) then
277               begin
278                 CurrObjSec.addsectionReloc(CurrObjSec.Size,p.objsection,reltype);
279                 data:=p.address;
280               end;
281             CurrObjSec.write(data, len);
282           end;
283 
284       else
285         if assigned(p) then
286           begin
287             { real address of the symbol }
288             symaddr:=p.address;
289             { Local ObjSymbols can be resolved already or need a section reloc }
290             if (p.bind=AB_LOCAL) and
291                (reltype in [RELOC_RELATIVE,RELOC_ABSOLUTE{$ifdef x86_64},RELOC_ABSOLUTE32{$endif x86_64}]) then
292               begin
293                 { For a reltype relocation in the same section the value can be calculated }
294                 if (p.objsection=CurrObjSec) and
295                    (reltype=RELOC_RELATIVE) then
296                   inc(data,symaddr-len-CurrObjSec.Size)
297                 else
298                   begin
299                     if (p.typ=AT_NONE) then
300                       begin
301                         {undefined symbol, using section}
302                         CurrObjSec.addsectionreloc(CurrObjSec.Size,p.objsection,reltype);
303                         data:=symaddr-len-CurrObjSec.Size;
304                       end
305                     else
306                       begin
307                         CurrObjSec.addsymreloc(CurrObjSec.Size,p,reltype);
308                         if Assigned(p.objsection) and
309                            (p.objsection.Name='__TEXT __textcoal_nt') then
310                           data:=symaddr-len-CurrObjSec.Size
311                         else
312                           data:=p.objsection.Size;
313                       end;
314                   end;
315               end
316             else if (p.bind=AB_GLOBAL) and
317                     not Assigned(p.indsymbol) and
318                     (reltype<>RELOC_PIC_PAIR) then
319               begin
320                 CurrObjSec.addsectionreloc(CurrObjSec.Size,p.objsection,reltype);
321                 data:=p.address;
322               end
323             else
324               CurrObjSec.addsymreloc(CurrObjSec.Size,p,reltype);
325           end; {if assigned(p) }
326 
327         CurrObjSec.write(data, len);
328       end;
329     end;
330 
331 
TmachoObjData.sectiontype2alignnull332   function TmachoObjData.sectiontype2align(atype: TAsmSectiontype): longint;
333     begin
334       case atype of
335         sec_bss:
336           Result:=4;
337         sec_stabstr, sec_stab:
338           Result:=1;
339         sec_stub, sec_data_lazy, sec_data_nonlazy:
340           Result:=4;
341       else
342         Result:=inherited sectiontype2align(atype);
343       end;
344     end;
345 
346 
TmachoObjData.sectiontype2optionsnull347   function TmachoObjData.sectiontype2options(atype: TAsmSectiontype): TObjSectionOptions;
348     begin
349       case atype of
350         sec_objc_meth_var_names,
351         sec_objc_class_names: Result:=[oso_data, oso_load];
352       else
353         Result:=inherited sectiontype2options(atype);
354       end
355     end;
356 
357 
358   { TMachoAssembler }
359 
360   constructor TMachoAssembler.create(info: pasminfo; smart: boolean);
361     begin
362       inherited;
363       CObjOutput:=TMachoObjectOutput;
364       CInternalAr:=tarobjectwriter;
365     end;
366 
367 
368   { TMachoObjectOutput }
369 
370   procedure TMachoObjectOutput.FixSectionRelocs(s: TMachoObjSection);
371     var
372       i   : integer;
373       ro  : TObjRelocation;
374       dw  : aword;
375     begin
376       {todo: is it I386 only core}
377       if not Assigned(s.Data) then
378         Exit;
379 
380       for i:=0 to s.ObjRelocations.Count-1 do
381         begin
382           ro:=TObjRelocation(s.ObjRelocations[i]);
383 
384           if (Assigned(ro.objsection)) and
385              (ro.objsection.Name='__TEXT __textcoal_nt') then
386             Continue;
387 
388           if Assigned(ro.objsection) then
389             begin
390               s.Data.seek(ro.DataOffset);
391               s.Data.read(dw, sizeof(aword));
392 
393               dw:=dw+ro.objsection.MemPos;
394 
395               s.Data.seek(ro.DataOffset);
396               s.Data.write(dw, sizeof(aword));
397             end
398           else
399             begin
400               if ro.symbol.Name='fpc_geteipasebx' then
401                 Continue;
402               if Assigned(ro.symbol.indsymbol) or
403                  (ro.typ=RELOC_PIC_PAIR) then
404                 begin
405                   s.Data.seek(ro.DataOffset);
406                   s.Data.read(dw, sizeof(aword));
407                   dw:=ro.symbol.address-dw;
408                   s.Data.seek(ro.DataOffset);
409                   s.Data.write(dw, sizeof(aword));
410                 end
411               else if (ro.symbol.bind=AB_LOCAL) then
412                 begin
413                   dw:=ro.symbol.address;
414                   s.Data.seek(ro.DataOffset);
415                   s.Data.write(dw, sizeof(aword));
416                 end;
417             end;
418 
419         end;
420       s.Data.seek(s.Data.Size);
421     end;
422 
423 
424   procedure TMachoObjectOutput.section_count_sections(p: TObject; arg: pointer);
425     var
426       s : TMachoObjSection;
427     begin
428       s:=TMachoObjSection(p);
429       if s.machoSec=mst_Stabs then
430         Exit;
431       inc(sectionscnt);
432       s.inSegIdx:=sectionscnt;
433     end;
434 
435 
436   procedure TMachoObjectOutput.section_set_datamempos(p: TObject; arg: pointer);
437     var
438       s : TMachoObjSection;
439     begin
440       s:=TMachoObjSection(p);
441       if s.machoSec=mst_Stabs then
442         Exit;
443 
444       s.setDataPos(fileofs);
445       s.setMemPos(memofs);
446       memofs:=Align(memofs+s.Size, s.SecAlign);
447 
448       fileofs:=AlignAddr(cputarget, fileofs);
449     end;
450 
451 
452   procedure TMachoObjectOutput.section_set_relocpos(p:TObject;arg:pointer);
453     var
454       s   : TMachoObjSection;
455       sz  : Integer;
456     begin
457       s:=TMachoObjSection(p);
458       if s.machoSec=mst_Stabs then
459         Exit;
460 
461       sz:=s.GetRelocCount * sizeof(relocation_info);
462       if sz > 0 then
463         begin
464           s.relocofs:=fileofs;
465           inc(fileofs, sz);
466           fileofs:=AlignAddr(cputarget, fileofs);
467         end;
468     end;
469 
470 
471   procedure TMachoObjectOutput.section_write_data(p: TObject; arg: pointer);
472     var
473       s : TMachoObjSection;
474     begin
475       s:=TMachoObjSection(p);
476       if s.machoSec=mst_Stabs then
477         Exit;
478 
479       Writer.writezeros(s.DataAlignBytes);
480 
481       FixSectionRelocs(s);
482 
483       if s.Datapos<>FWriter.ObjSize then
484         InternalError(200903101);
485       if Assigned(s.data) then
486         Writer.writearray(s.data);
487       TrailZeros;
488     end;
489 
490 
491   procedure TMachoObjectOutput.section_write_relocdata(p: TObject; arg: pointer);
492     var
493       s       : TMachoObjSection;
494       symsec  : TMachoObjSection;
495       i       : Integer;
496       dw      : aword;
497 
498       r       : relocation_info;
499       sr      : scattered_relocation_info;
500       ro      : TObjRelocation;
501       symnum      : Integer;
502       relpc       : Boolean;
503       relextern   : Boolean;
504       reltype     : Integer;
505 
506     begin
507       s:=TMachoObjSection(p);
508 
509       {stabs relocation should not present in relocation table}
510       if s.machoSec=mst_Stabs then
511         Exit;
512       {no relocation for the section}
513       if s.relocofs=0 then
514         Exit;
515       {check file alignment}
516       if s.relocofs<>FWriter.ObjSize then
517         InternalError(200903102); {file misalignment}
518 
519       relcount:=s.ObjRelocations.Count;
520       {the reversed order, is only to be alike Apple linker}
521       for i:=s.ObjRelocations.Count-1 downto 0 do
522       begin
523         ro:=TObjRelocation(s.ObjRelocations[i]);
524 
525         {in-section relocation}
526         if ro.symbol=nil then
527           begin
528             relextern:=false;
529             relpc:=false;
530             symnum:=TmachoObjSection(ro.objsection).inSegIdx;
531             case ro.typ of
532               RELOC_ABSOLUTE:
533                 begin
534                   RelocInfo(ro.DataOffset, symnum, GENERIC_RELOC_VANILLA, ril_long, relpc, relextern, r);
535                   mfile.WriteRelocation(r);
536                 end;
537             else
538               relpc:=ro.typ=RELOC_RELATIVE;
539               RelocInfo(ro.DataOffset, symnum, GENERIC_RELOC_VANILLA, ril_long, relpc, relextern, r);
540               mfile.WriteRelocation(r);
541             end;
542 
543           end
544         else
545           begin
546             symsec:=TMachoObjSection(ro.symbol.objsection);
547 
548             if Assigned(symsec) and
549                (symsec.Name='__TEXT __textcoal_nt') then
550               begin
551                 relextern:=true;
552                 symnum:=ro.symbol.symidx;
553               end
554             else if ro.symbol.bind=AB_EXTERNAL then
555               begin
556                 relextern:=true;
557                 symnum:=ro.symbol.symidx;
558               end
559             else if Assigned(ro.symbol.objsection) and
560                     (ro.symbol.bind=AB_LOCAL) and
561                     (ro.symbol.typ in [AT_DATA,AT_METADATA]) then
562               begin
563                 relextern:=false;
564                 symnum:=TMachoObjSection(ro.symbol.objsection).inSegIdx;
565               end
566             else if (ro.symbol.bind=AB_LOCAL) or
567                     (ro.symbol.typ=AT_NONE) then
568              begin
569                relextern:=false;
570                 symnum:=s.inSegIdx
571               end
572             else
573               begin
574                 relextern:=true;
575                 symnum:=ro.symbol.symidx;
576               end;
577 
578             relpc:=false;
579             relpc:=(ro.typ=RELOC_RELATIVE);
580             if (ro.typ=RELOC_PIC_PAIR) then
581               begin
582                 if ro.symbol.bind=AB_LOCAL then
583                   reltype:=GENERIC_RELOC_LOCAL_SECTDIFF
584                 else
585                   reltype:=GENERIC_RELOC_SECTDIFF;
586                 ScatterRelocInfo(ro.symbol.address, ro.DataOffset, reltype, ril_long, false, sr);
587                 mfile.WriteScatterReloc(sr);
588 
589                 { the section data is already fixed to:   ro.SymbolOffset - Label.Offset }
590                 s.Data.seek(ro.DataOffset);
591                 s.Data.read(dw, sizeof(aword));
592                 dw:=ro.symbol.address-dw;
593                 ScatterRelocInfo(dw, 0, GENERIC_RELOC_PAIR, ril_long, false, sr);
594                 mfile.WriteScatterReloc(sr);
595               end
596             else
597               begin
598                 RelocInfo(ro.DataOffset, symnum, GENERIC_RELOC_VANILLA, ril_long, relpc, relextern, r);
599                 mfile.WriteRelocation(r);
600               end
601           end;
602         if Assigned(s.Data) then
603           s.Data.seek(s.Data.size);
604       end;
605       TrailZeros;
606     end;
607 
608 
609     procedure TMachoObjectOutput.section_prepare_indirect(s: TObjSection);
610       var
611         t       : TObjSymbol;
612         i       : Integer;
613         anysym  : Boolean;
614       begin
615         if TmachoObjSection(s).machoSec=mst_Stabs then
616           Exit;
617 
618         anysym:=false;
619         for i:=0 to machoData.ObjSymbolList.Count-1 do
620           begin
621             t:=TObjSymbol(machoData.ObjSymbolList[i]);
622             if (t.objsection=s) and Assigned(t.indsymbol) then
623               begin
624                 if not anysym then
625                   begin
626                     {remember the index of the first indirect symbol. Will be used later at section header writting}
627                     TmachoObjSection(s).indIndex:=IndirIndex.size div SizeOf(Integer);
628                     anysym:=true;
629                   end;
630                 IndirIndex.write(t.symidx, sizeof(Integer));
631               end;
632           end;
633 
634       end;
635 
636 
637     procedure TMachoObjectOutput.symbol_write_nlist(sym:TObjSymbol; symstr: tdynamicarray);
638       var
639         n       : nlist_64;
640         sec     : TmachoObjSection;
641       begin
642         sec:=TMachoObjSection(sym.objsection);
643         FillChar(n, sizeof(n), 0);
644         n.n_un.n_strx:=symstr.size;
645         symstr.writestr(sym.Name+#0);
646 
647         if assigned(sec) and
648            (sec.machoSec=mst_ObjC) and
649            (sec.nmsection='__module_info') then
650           begin
651             n.n_type:=N_ABS or N_EXT;
652             mfile.WriteNList(n);
653             Exit;
654           end;
655 
656         if (sym.typ=AT_NONE) then
657           begin
658             n.n_value:=0;
659             if sym.bind<>AB_EXTERNAL then
660               n.n_desc:=n.n_desc or REFERENCE_FLAG_UNDEFINED_LAZY;
661             n.n_type:=n.n_type or N_EXT;
662           end
663         else if sym.bind=AB_LAZY then
664           begin
665             n.n_value:=0;
666             n.n_type:=N_ABS or N_EXT;
667             n.n_sect:=NO_SECT;
668           end
669         else
670           begin
671             n.n_value:=sym.address;
672 
673             if Assigned(sec) then
674               begin
675                 n.n_sect:=sec.inSegIdx;
676                 n.n_type:=n.n_type or N_SECT;
677 
andnull678                 if (sym.typ=AT_FUNCTION) and
679                    (sym.bind=AB_LOCAL) then
680                   begin
681                     n.n_type:=N_PEXT or N_EXT or N_SECT;
682                     n.n_desc:=n.n_desc or N_WEAK_DEF;
683                   end;
684               end;
685           end;
686 
687         if (sym.bind=AB_GLOBAL) and
688            (n.n_type and N_PEXT=0) then
689           n.n_type:=n.n_type or N_EXT;
690 
andnull691         if (sym.typ=AT_FUNCTION) and
692            (sym.bind=AB_GLOBAL) then
693           n.n_desc:=n.n_desc or N_NO_DEAD_STRIP;
694 
695         if Assigned(sec) then
696           begin
697             if (sec.nmsection='__nl_symbol_ptr') then
698               n.n_desc:=n.n_desc or REFERENCE_FLAG_UNDEFINED_NON_LAZY;
699             if (sec.nmsegment=seg_Data) and (sec.nmsection='__const') then
700               n.n_desc:=n.n_desc or N_NO_DEAD_STRIP;
701           end;
702 
703         mfile.WriteNList(n);
704       end;
705 
706 
TMachoObjectOutput.dysymbol_locationnull707     function TMachoObjectOutput.dysymbol_location(sym: TObjSymbol): TMachoSymbolLocation;
708       begin
709         if Assigned(sym.objsection) and
710            (TMachoObjSection(sym.objsection).machoSec=mst_Stabs) then
711           Result:=loc_Local
712         else
713           case sym.typ of
714             AT_NONE:  Result:=loc_Undef;
715             AT_LABEL: Result:=loc_Notused;
716           else
717             Result:=loc_External;
718           end;
719       end;
720 
721 
722   procedure TMachoObjectOutput.writeSectionsHeader(s: TMachoObjSection);
723     var
724       sc      : TMachoSection;
725     begin
726       section_prepare_indirect(s);
727 
728       fillChar(sc, sizeof(sc), 0);
729       sc.segname:=s.nmsegment;
730       sc.sectname:=s.nmsection;
731       sc.size:=s.Size;
732       if s.FileSize>0 then
733         sc.offset:=s.DataPos
734       else
735         sc.offset:=0;
736       sc.addr:=s.MemPos;
737       sc.nreloc:=s.GetRelocCount;
738       sc.reloff:=s.relocofs;
739       sc.flags:=GetSectionFlags(s.nmsegment, s.nmsection);
740       sc.align:=MachoAlign(s.SecAlign);
741       sc.indirectIndex:=s.indIndex;
742 
743       if (sc.flags and SECTION_TYPE)=S_SYMBOL_STUBS then
744         sc.stubSize:=GetStubSize(cputarget, false);
745       mfile.WriteSection(sc);
746     end;
747 
748 
749   procedure TMachoObjectOutput.writeSymTabCommand;
750     begin
751       mfile.WriteLoadCommand(LC_SYMTAB, sizeof(symtab_command));
752       mfile.WriteUint32(fileofs); {symoff}
753       mfile.WriteUint32(symCount); {nsyms}
754       inc(fileofs, symCount*sizeNList(cputarget));
755       fileofs:=AlignAddr(cputarget, fileofs);
756 
757       symstrofs:=fileofs;
758       mfile.WriteUint32(fileofs); {stroff}
759       mfile.WriteUint32(symlen); {strsize}
760 
761       inc(fileofs, symlen);
762       fileofs:=AlignAddr(cputarget, fileofs);
763     end;
764 
765 
TMachoObjectOutput.symWriteNamenull766     function TMachoObjectOutput.symWriteName(s: TObjSymbol): string;
767       begin
768         if not Assigned(s.indsymbol) then
769           Result:=s.Name
770         else
771           Result:=s.indsymbol.Name;
772       end;
773 
774 
775 {    function getSymWriteNameLength(s: TObjSymbol): Integer; inline;
776       begin
777         Result:=length(symWriteName(s))+1;
778       end;}
779 
780 
781     procedure TMachoObjectOutput.InitSymbolIndexes(var sCount: aint; var symStrLen: aword);
782       var
783         i         : integer;
784         s         : TObjSymbol;
785         stabcount : Integer;
786       begin
787         sCount:=0;
788         symStrLen:=0;
789 
790         iIndir:=0;
791         for i:=0 to machoData.ObjSymbolList.Count-1 do
792           begin
793             s:=TObjSymbol(machoData.ObjSymbolList[i]);
794             if (s.typ=AT_LABEL) then
795               Continue;
796 
797             if Assigned(s.indsymbol) then
798               inc(iIndir);
799           end;
800 
801         iLocal:=0;
802         iExtern:=0;
803         iUndef:=0;
804 
805         for i:=0 to machoData.ObjSymbolList.Count-1 do
806           begin
807             s:=TObjSymbol(machoData.ObjSymbolList[i]);
808             if (s.typ=AT_LABEL) or
809                Assigned(s.indsymbol) then
810                  Continue;
811             if (s.bind=AB_LOCAL) and
812                (s.Name <> 'fpc_geteipasebx') then
813               Continue;
814 
815             case dysymbol_location(s) of
816               loc_Local:
817                 begin
818                   symList.Insert(iLocal, s);
819                   inc(iLocal); inc(iExtern); inc(iUndef);
820                 end;
821               loc_External:
822                 begin
823                   symList.Insert(iExtern, s);
824                   inc(iExtern); inc(iUndef);
825                 end;
826               loc_Undef:
827                 begin
828                   symList.Insert(iUndef, s);
829                   inc(iUndef);
830                 end;
831             end;
832             inc(symStrLen, length(s.Name)+1 );
833           end;
834 
835         if Assigned(stabsec) then
836           {skipping hdrsym! (added by ogbase) }
837           stabcount:=stabsec.Size div sizeof(TObjStabEntry) - 1
838         else
839           stabcount:=0;
840 
841         for i:=0 to symList.Count-1 do
842           TObjSymbol(symList[i]).symidx:=i+stabcount;
843         sCount:=symList.Count+stabcount;
844 
845         for i:=0 to machoData.ObjSymbolList.Count-1 do
846           with TObjSymbol(machoData.ObjSymbolList[i]) do
847             if Assigned(indsymbol) then
848               symidx:=indsymbol.symidx;
849 
850         if Assigned(strsec) then
851           // 1 byte of zero name (that stands in the end of table, not at zero pos)
852           inc(symlen, strsec.Size + 1)
853         else
854           inc(symlen); {the first zero byte}
855 
856         dec(iUndef, iExtern); { iUndef is count of undefined symbols (for dysymtable command) }
857         dec(iExtern, iLocal); { iExtern is count of external symbols (for dysymtable command) }
858         inc(iLocal, stabcount);
859       end;
860 
861 
862     procedure TMachoObjectOutput.writeSymbols(symstr: tdynamicarray);
863       var
864         i       : integer;
865         s       : TObjSymbol;
866         b       : byte;
867         stab    : TObjStabEntry;
868         ro      : TObjRelocation;
869         sym     : TObjSymbol;
870         addr    : aword;
871         text    : TmachoObjSection;
872         funofs  : AWord;
873       begin
874         if Assigned(stabsec) then
875           begin
876             for i:=0 to stabsec.ObjRelocations.Count - 1 do
877               begin
878                 ro:=TObjRelocation(stabsec.ObjRelocations[i]);
879                 sym:=ro.symbol;
880                 addr:=sym.address;
881                 if Assigned(sym.objsection) then
882                   begin
883                     stabsec.Data.seek(ro.DataOffset-3);
884                     b:=TmachoObjSection(sym.objsection).inSegIdx;
885                     stabsec.Data.write(b, sizeof(b));
886                   end;
887                 stabsec.Data.seek(ro.DataOffset);
888                 stabsec.Data.write(addr, sizeof(addr));
889               end;
890 
891             stabsec.Data.seek(sizeof(TObjStabEntry));
892             funofs:=0;
893             text:=TmachoObjSection(machoData.ObjSectionList.Find(MakeSectionName(seg_TEXT, '__text')));
894             for i:=1 to stabsec.Data.size div SizeOf(TObjStabEntry) - 1 do
895               begin
896                 stabsec.Data.read(stab, sizeof(stab));
897                 case stab.ntype of
898                   N_FUN:
899                     begin
900                       if stab.strpos=0 then
901                         funofs:=0
902                       else
903                         funofs:=stab.nvalue;
904                     end;
905                   N_SLINE,N_RBRAC,N_LBRAC:
906                     begin
907                       if Assigned(text) then
908                         begin
909                           { SLINE are expected to be in  __TEXT __text only }
910                           stab.nother:=text.inSegIdx;
911                           inc(stab.nvalue, funofs);
912                         end;
913                     end;
914                   N_OSO:
915                     begin
916                       { null-terminated string is the first in the list         }
917                       { apple-gdb doesn't recognize it as zero-string for N_OSO }
918                       { another zero-string should be added to the list         }
919                       if stab.strpos=0 then
920                         stab.strpos:=symstr.Size;
921                     end;
922                 end;
923                 FWriter.write(stab, sizeof(stab));
924               end;
925           end;
926 
927         symstr.Seek(symStr.size);
928         b:=0;
929         symstr.Write(b,1);
930 
931         for i:=0 to symList.Count-1 do
932           begin
933             s:=TObjSymbol(symList[i]);
934             symbol_write_nlist(s, symstr);
935           end;
936       end;
937 
938 
939   procedure TMachoObjectOutput.writeDySymTabCommand(IndOffset: aword; IndCount: Integer);
940     begin
941       mfile.WriteLoadCommand(LC_DYSYMTAB, sizeof(dysymtab_command));
942 
943       mfile.WriteUint32(0); {ilocalsym}
944       mfile.WriteUint32(iLocal); {nlocalsym}
945 
946       mfile.WriteUint32(iLocal); {iextdefsym}
947       mfile.WriteUint32(iExtern); {nextdefsym}
948 
949       mfile.WriteUint32(iLocal + iExtern); {iundefsym}
950       mfile.WriteUint32(iUndef); {nundefsym}
951 
952       mfile.WriteUint32(0); {tocoff}
953       mfile.WriteUint32(0); {ntoc}
954       mfile.WriteUint32(0); {modtaboff}
955       mfile.WriteUint32(0); {nmodtab}
956       mfile.WriteUint32(0); {extrefsymoff}
957       mfile.WriteUint32(0); {nextrefsyms}
958       mfile.WriteUint32(IndOffset);  {indirectsymoff}
959       mfile.WriteUint32(IndCount);   {nindirectsyms}
960       mfile.WriteUint32(0); {extreloff}
961       mfile.WriteUint32(0); {nextrel}
962       mfile.WriteUint32(0); {locreloff}
963       mfile.WriteUint32(0); {nlocrel}
964     end;
965 
966 
967   procedure TMachoObjectOutput.writeDysymbols;
968     var
969       i   : integer;
970       idx : LongWord;
971     begin
972       IndirIndex.seek(0);
973       for i:=0 to (IndirIndex.size div sizeof(Integer))-1 do
974         begin
975           IndirIndex.read(idx, sizeof(idx));
976           mfile.WriteUint32(idx);
977         end;
978     end;
979 
980 
AddSectionToSegmentnull981   function AddSectionToSegment(var segment: TMachoSegment; section : TMachoObjSection): boolean;
982     begin
983       { sections must be attached one-by-one to the segment }
984       if segment.fileoff=0 then
985         segment.fileoff:=section.DataPos;
986 
987       if (segment.fileoff+segment.filesize)<(section.FileSize+section.DataPos) then
988         segment.filesize:=section.FileSize+section.DataPos;
989 
990       inc(segment.nsects);
991       inc(segment.vmsize, section.size);
992       Result:=true;
993    end;
994 
995 
996   procedure TMachoObjectOutput.TrailZeros;
997     var
998       sz : LongWord;
999     begin
1000       sz:=AlignAddr(cputarget, FWriter.Size);
1001       if sz - FWriter.Size>0 then
1002         FWriter.WriteZeros(sz-FWriter.Size);
1003     end;
1004 
1005 
TMachoObjectOutput.current_cpu_typenull1006   function TMachoObjectOutput.current_cpu_type: cpu_type_t;
1007     begin
1008 {$if defined(powerpc)}
1009       result:=CPU_TYPE_POWERPC;
1010 {$elseif defined(powerpc64)}
1011       result:=CPU_TYPE_POWERPC64;
1012 {$elseif defined(i386)}
1013       result:=CPU_TYPE_I386;
1014 {$elseif defined(x86_64)}
1015       result:=CPU_TYPE_X86_64;
1016 {$elseif defined(arm)}
1017       result:=CPU_TYPE_ARM;
1018 {$elseif defined(aarch64)}
1019       result:=CPU_TYPE_ARM64;
1020 {$else}
1021       result:=CPU_TYPE_ANY;
1022 {$endif}
1023     end;
1024 
TMachoObjectOutput.writedatanull1025   function TMachoObjectOutput.writedata(data: TObjData): boolean;
1026     var
1027       header  : TMachHeader;
1028       seg     : TMachoSegment;
1029       secobj  : TMachoObjSection;
1030       i       : Integer;
1031 
1032       symstr  : tdynamicarray;
1033       segSize : integer; {size of a segment command - platform dependant}
1034       sctSize : integer; {size of a single section header - platform dependant}
1035 
1036       indOfs: aword;   {indirect symbol offset}
1037 
1038     begin
1039       symList:=TFPObjectList.Create(false);
1040       IndirIndex:=tdynamicarray.Create(1024);
1041 
1042       result:=false;
1043       machoData:=TMachoObjData(data);
1044 
1045       cputarget:=current_cpu_type;
1046       segSize:=sizeSegment(cputarget);
1047       sctSize:=sizeSection(cputarget);
1048 
1049       sectionscnt:=0;
1050       stabsec:=TMachoObjSection(machoData.ObjSectionList.Find('.stabs'));
1051       strsec:=TMachoObjSection(machoData.ObjSectionList.Find('.stabsstr'));
1052 
1053       {count number of sections}
1054       machoData.ObjSectionList.ForEachCall(@section_count_sections, nil);
1055 
1056       {sections data is written after machheader,load-commands.   }
1057       {   basic loadcommands for MH_OBJECT are                    }
1058       {   single LC_SEGMENT, containing all sections headers      }
1059       {   symbol linking information at LC_SYMTAB and LC_DYSYMTAB }
1060       header.cputype:=cputarget;
1061       header.cpusubtype:=CPU_SUBTYPE_i386_ALL;
1062       header.filetype:=MH_OBJECT;
1063       header.ncmds:=3;
1064       header.sizeofcmds:=segSize+sctSize*sectionscnt+sizeof(symtab_command)+sizeof(dysymtab_command);
1065       header.flags:=0;
1066 
1067       {setting sections data and memory pos}
1068       fileofs:=sizeMachHeader(cputarget)+header.sizeofcmds;
1069       fileofs:=AlignAddr(cputarget, fileofs);
1070       memofs:=0;
1071 
1072       machoData.ObjSectionList.ForEachCall(@section_set_datamempos, nil);
1073       fileofs:=AlignAddr(cputarget, fileofs);
1074 
1075       {setting sections relocation offsets}
1076       machoData.ObjSectionList.ForEachCall(@section_set_relocpos, nil);
1077       fileofs:=AlignAddr(cputarget, fileofs);
1078 
1079       {creating actual mach-o file writer}
1080       mfile:=AllocMachoWriter(cputarget, TMachoRawWriter.Create(writer), true);
1081       {writing macho-o header}
1082       mfile.WriteHeader(header);
1083 
1084       {starting the first segment command}
1085       InitSegment(seg);
1086 
1087       {initialze symbols. some sections (non_lazy, lazy pointers) are effected}
1088       InitSymbolIndexes(symCount, symlen);
1089 
1090       for i:=0 to machoData.ObjSectionList.Count-1 do
1091         begin
1092           secobj:=TmachoObjSection(machoData.ObjSectionList[i]);
1093           if secobj.machoSec=mst_Stabs then
1094             Continue;
1095           AddSectionToSegment(seg, secobj);
1096         end;
1097 
1098       {writting segment command}
1099       {for MH_OBJECT, all sections are stored in the single segment}
1100       mfile.WriteSegmentCmd(seg, segSize+(seg.nsects)*sctSize);
1101 
1102       {section headers are written inside segment command}
1103       for i:=0 to machoData.ObjSectionlist.Count - 1 do
1104         begin
1105           secobj:=TmachoObjSection(machoData.ObjSectionList[i]);
1106           if secobj.machoSec=mst_Stabs then
1107             Continue;
1108           writeSectionsHeader(secobj);
1109         end;
1110       TrailZeros;
1111 
1112       if IndirIndex.size div sizeof(Integer)<>iIndir then
1113         InternalError(2009121001);
1114       if iIndir>0 then
1115         indOfs:=fileOfs
1116       else
1117         indOfs:=0;
1118       inc(fileofs, IndirIndex.size);
1119 
1120       {write symtab command}
1121       {initilize symbos order. local first, extern second, undef last}
1122       writeSymTabCommand;
1123       TrailZeros;
1124 
1125       {write dysymtab command}
1126       writeDySymTabCommand(indofs, iIndir);
1127       TrailZeros;
1128 
1129       {writting sections data, to precalculated offsets}
1130       {if precalculated offsets, doesn't match actual written offsets, internal error is risen}
1131       machoData.ObjSectionList.ForEachCall(@section_write_data, nil);
1132 
1133       {writting relocation offsets}
1134       machoData.ObjSectionList.ForEachCall(@section_write_relocdata, nil);
1135 
1136       {writting dyn symbol tables (indirect symbols arrays)}
1137       writeDysymbols;
1138 
1139       {writting symbol table}
1140       if Assigned(strsec) then
1141         symstr:=strsec.Data
1142       else
1143         symstr:=tdynamicarray.create(1024);
1144 
1145       writeSymbols(symstr);
1146       TrailZeros;
1147 
1148       {writting symbol table strings}
1149       FWriter.writearray(symstr);
1150       // terminating null name
1151       TrailZeros;
1152 
1153       if not Assigned(strsec) then
1154         symstr.Free;
1155 
1156       TrailZeros;
1157 
1158       mfile.Free;
1159       symList.Free;
1160       IndirIndex.Free;
1161     end;
1162 
1163 
1164   constructor TMachoObjectOutput.Create(AWriter: TObjectWriter);
1165     begin
1166       inherited Create(AWriter);
1167       CObjData:=TMachoObjData;
1168     end;
1169 
1170 
1171   { TMachoRawWriter }
1172 
1173   constructor TMachoRawWriter.Create(awriter: tobjectwriter);
1174     begin
1175       inherited Create;
1176       fwriter:=awriter;
1177     end;
1178 
1179 
1180   procedure TMachoRawWriter.WriteRaw(const data; datasize: Integer);
1181     begin
1182       fwriter.Write(data, datasize);
1183     end;
1184 
1185 
1186   { TmachoObjSection }
1187 
TmachoObjSection.GetRelocCountnull1188   function TmachoObjSection.GetRelocCount: Integer;
1189     var
1190       i: integer;
1191       r: TObjRelocation;
1192     begin
1193       Result:=ObjRelocations.Count;
1194       for i:=0 to ObjRelocations.Count-1 do
1195         begin
1196           r:=TObjRelocation(ObjRelocations[i]);
1197           if (r.typ=RELOC_PIC_PAIR) then
1198             inc(Result);
1199         end;
1200     end;
1201 
1202 
TmachoObjSection.FileSizenull1203   function TmachoObjSection.FileSize: Integer;
1204     begin
1205       if Assigned(data) then
1206         Result:=data.size
1207       else
1208         Result:=0;
1209     end;
1210 
1211 
1212   constructor TmachoObjSection.create(AList: TFPHashObjectList;
1213     const Aname: string; Aalign: longint; Aoptions: TObjSectionOptions);
1214     begin
1215       if Aname = '__TEXT __textcoal_nt' then
1216         Aalign:=4;
1217 
1218       inherited create(AList, Aname, Aalign, Aoptions);
1219       GetSegmentSectionName(aName, nmsegment, nmsection);
1220       if (aname='.stabs') or
1221          (aname='.stabsstr') then
1222         machoSec:=mst_Stabs
1223       else if nmsegment=seg_DWARF then
1224         machoSec:=mst_Dwarf
1225       else if nmsegment=seg_OBJC then
1226         machoSec:=mst_ObjC
1227       else
1228         machoSec:=mst_Normal;
1229     end;
1230 
1231 
1232   const
1233     as_i386_darwin_info : tasminfo =
1234       (
1235         id     : as_i386_macho;
1236         idtxt  : 'MACHO';
1237         asmbin : '';
1238         asmcmd : '';
1239         supported_targets : [system_i386_darwin,system_i386_iphonesim];
1240         flags : [af_outputbinary,af_smartlink_sections,af_supports_dwarf{, af_stabs_use_function_absolute_addresses}];
1241         labelprefix : '.L';
1242         comment : '#';
1243         dollarsign: '$';
1244       );
1245 
1246 initialization
1247 {$ifdef i386}
1248   RegisterAssembler(as_i386_darwin_info,TMachoAssembler);
1249 {$endif i386}
1250 
1251 end.
1252 
1253