1{
2    This file is part of the Free Pascal run time library.
3    Copyright (c) 2008 by Giulio Bernardi
4
5    Resource writer for Mach-O files
6
7    See the file COPYING.FPC, included in this distribution,
8    for details about the copyright.
9
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13
14 **********************************************************************}
15
16type
17  _TMachOSymbolTable_ = class(TMachOSymbolTable)
18  protected
19    function AddSymbol(aName : string; sect : byte; addr : longword;
20      glob, undef : boolean) : integer; override;
21  protected
22  public
23    procedure WriteToStream(aStream : TStream); override;
24    procedure SetSymbolOffset(symbolnum : integer; offset: longword); override;
25  end;
26
27  _TMachOSubWriter_ = class(TAbstractMachOSubWriter)
28  private
29    procedure SwapSection(var aSection: _TSection_);
30  protected
31    procedure PrescanResourceTree; override;
32    procedure WriteResHeader(aStream : TStream; aResources : TResources); override;
33    procedure WriteNodeInfos(aStream : TStream); override;
34    procedure WriteNodeInfo(aStream : TStream; aNode : TResourceTreeNode); override;
35    procedure AllocateSpaceForLoadCommands(aStream : TStream); override;
36
37    procedure FixLoadCommands(aStream : TStream; aResources : TResources); override;
38  public
39    constructor Create(aParent : TMachOResourceWriter; const aMachineType
40      : TMachOMachineType; const aSubMachineType: TMachoSubMachineType;
41      const aOppositeEndianess : boolean); override;
42  end;
43
44{ _TMachOSymbolTable_ }
45
46function _TMachOSymbolTable_.AddSymbol(aName: string; sect: byte; addr: longword;
47  glob, undef: boolean): integer;
48var p : _PNlist_;
49begin
50  p:=GetMem(sizeof(_TNlist_));
51  p^.strx:=fStringTable.Add(aName);
52  if not undef then
53    p^._type:=N_SECT
54  else
55    p^._type:=N_UNDF;
56  if glob then
57    p^._type:=p^._type or N_EXT;
58  p^.desc:=0;
59  p^.sect:=sect;
60  p^.value:=addr;
61  Result:=fList.Count;
62  fList.Add(p);
63end;
64
65procedure _TMachOSymbolTable_.WriteToStream(aStream: TStream);
66var nlist : _TNlist_;
67    i : integer;
68    sawglobal: boolean;
69begin
70  { first write local symbols, then global ones, as ilocalsym is hardcoded to
71    be index 0. Can't reorder here because we may already have used symbol
72    numbers to generate relocations -> give an error if a global symbol
73    comes before any local symbols }
74  sawglobal:=false;
75  for i:=0 to fList.Count-1 do
76  begin
77    nlist:=_PNlist_(fList[i])^;
78    if (nlist._type and N_EXT)<>0 then
79      sawglobal:=true
80    else if sawglobal then
81      raise EMachOResourceWriterSymbolTableWrongOrderException.Create('');
82    if fOppositeEndianess then
83    begin
84      nlist.strx:=SwapEndian(nlist.strx);
85      nlist.desc:=SwapEndian(nlist.desc);
86      nlist.value:=SwapEndian(nlist.value);
87    end;
88    aStream.WriteBuffer(nlist,sizeof(nlist));
89  end;
90end;
91
92procedure _TMachOSymbolTable_.SetSymbolOffset(symbolnum: integer; offset: longword);
93var
94  p : _PNlist_;
95begin
96  p:=_PNlist_(flist[symbolnum]);
97  p^.value:=offset;
98end;
99
100{ _TMachOSubWriter_ }
101
102procedure _TMachOSubWriter_.SwapSection(var aSection: _TSection_);
103begin
104  aSection.addr:=SwapEndian(aSection.addr);
105  aSection.size:=SwapEndian(aSection.size);
106  aSection.offset:=SwapEndian(aSection.offset);
107  aSection.align:=SwapEndian(aSection.align);
108  aSection.reloff:=SwapEndian(aSection.reloff);
109  aSection.nreloc:=SwapEndian(aSection.nreloc);
110  aSection.flags:=SwapEndian(aSection.flags);
111  aSection.reserved1:=SwapEndian(aSection.reserved1);
112  aSection.reserved2:=SwapEndian(aSection.reserved2);
113end;
114
115procedure _TMachOSubWriter_.PrescanResourceTree;
116begin
117  fResStrTable.Clear;
118  fRoot.SubDirRVA:=sizeof(_TResHdr_)+sizeof(_TResInfoNode_);
119  fResStrTable.StartOfs:=PrescanNode(fRoot,sizeof(_TResInfoNode_));
120  if fResStrTable.Used then
121    fDataCurOfs:=NextAligned(fDataAlignment,fResStrTable.StartOfs+fResStrTable.Size)
122  else
123    fDataCurOfs:=fResStrTable.StartOfs;
124end;
125
126procedure _TMachOSubWriter_.WriteResHeader(aStream: TStream;
127  aResources: TResources);
128var hdr : _TResHdr_;
129begin
130  hdr.rootptr:=sizeof(hdr);
131  hdr.count:=aResources.Count;
132  hdr.usedhandles:=0;
133  hdr.handles:=0;
134  { the first pointer (rootptr at offset 0) goes to the root node, which comes
135    right after the header (the addend has been set to sizeof(hdr) -> add the
136    address of the fpc.resources section to it via a relocations}
137  fRelocations.Add(0,ffpcresourcessym);
138  { the last pointer (handles at offset sizeof(fields before it)) goes to the
139    fpc.reshandles section }
140  fRelocations.Add(sizeof(hdr.rootptr)+sizeof(hdr.count)+sizeof(hdr.usedhandles),ffpcreshandlessym);
141  if fOppositeEndianess then
142  begin
143    hdr.rootptr:=SwapEndian(hdr.rootptr);
144    hdr.count:=SwapEndian(hdr.count);
145    //handles must be fixed later
146//    hdr.usedhandles:=SwapEndian(hdr.usedhandles);
147//    hdr.handles:=SwapEndian(hdr.handles);
148  end;
149  aStream.WriteBuffer(hdr,sizeof(hdr));
150end;
151
152procedure _TMachOSubWriter_.WriteNodeInfos(aStream: TStream);
153begin
154  { offset inside the object }
155  fCurOfs:=sizeof(_TResHdr_);
156  WriteNodeInfo(aStream,fRoot);
157  WriteSubNodes(aStream,fRoot);
158end;
159
160procedure _TMachOSubWriter_.WriteNodeInfo(aStream: TStream;
161  aNode: TResourceTreeNode);
162var infonode : _TResInfoNode_;
163begin
164  if aNode.Desc.DescType=dtID then
165    infonode.nameid:=aNode.Desc.ID
166  else
167  begin
168    infonode.nameid:=fResStrTable.StartOfs+aNode.NameRVA;
169    fRelocations.Add(fCurOfs,ffpcresourcessym);
170  end;
171  infonode.ncount:=aNode.NamedCount;
172  if aNode.IsLeaf then
173  begin
174    infonode.idcountsize:=aNode.Data.RawData.Size;
175    infonode.subptr:=fDataCurOfs;
176    fDataCurOfs:=NextAligned(fDataAlignment,fDataCurOfs+infonode.idcountsize);
177  end
178  else
179  begin
180    infonode.idcountsize:=aNode.IDCount;
181    infonode.subptr:=aNode.SubDirRVA;
182  end;
183  fRelocations.Add(
184    fCurOfs+sizeof(infonode.nameid)+sizeof(infonode.ncount)+
185    sizeof(infonode.idcountsize),ffpcresourcessym);
186  if fOppositeEndianess then
187  begin
188    infonode.nameid:=SwapEndian(infonode.nameid);
189    infonode.ncount:=SwapEndian(infonode.ncount);
190    infonode.idcountsize:=SwapEndian(infonode.idcountsize);
191    infonode.subptr:=SwapEndian(infonode.subptr);
192  end;
193  aStream.WriteBuffer(infonode,sizeof(infonode));
194  inc(fCurOfs,sizeof(infonode));
195end;
196
197procedure _TMachOSubWriter_.AllocateSpaceForLoadCommands(aStream: TStream);
198var buf : pbyte;
199begin
200  fHeader.sizeofcmds:=
201    //segment+res section+bss section
202    sizeof(_TSegmentCommand_)+sizeof(_TSection_)*3+
203    //symbol table and dynamic symbol table commands
204    sizeof(TSymtabCommand)+sizeof(TDySymtabCommand)+
205    //common header of the three commands
206    sizeof(TLoadCommand)*3;
207  buf:=GetMem(fHeader.sizeofcmds);
208  FillByte(buf^,fHeader.sizeofcmds,0);
209  try
210    aStream.WriteBuffer(buf^,fHeader.sizeofcmds);
211  finally
212    FreeMem(buf);
213  end;
214end;
215
216procedure _TMachOSubWriter_.FixLoadCommands(aStream: TStream; aResources : TResources);
217var ldcommand : TLoadCommand;
218    segcommand : _TSegmentCommand_;
219    symcommand : TSymtabCommand;
220    dysymcommand : TDySymtabCommand;
221    ressection,bsssection,textsection : _TSection_;
222begin
223  ldcommand.cmd:=fSegType;
224  ldcommand.cmdsize:=sizeof(TLoadCommand)+sizeof(segcommand)+sizeof(ressection)*3;
225
226  FillByte(segcommand.name[0],16,0);
227  segcommand.vmaddr:=0;
228  segcommand.vmsize:=fDataCurOfs+sizeof(_ptrtype_)*aResources.Count;
229  segcommand.fileoff:=fSectionStart;
230  segcommand.filesize:=fDataCurOfs;
231  segcommand.maxprot:=VM_PROT_READ or VM_PROT_WRITE or VM_PROT_EXECUTE;
232  segcommand.initprot:=VM_PROT_READ or VM_PROT_WRITE or VM_PROT_EXECUTE;
233  segcommand.nsects:=3;
234  segcommand.flags:=0;
235
236  fillbyte(textsection,sizeof(textsection),0);
237  textsection.sectname:='__text';
238  textsection.segname:='__TEXT';
239  textsection.addr:=0;
240  textsection.size:=0;
241  textsection.offset:=segcommand.fileoff;
242  textsection.align:=0;
243  textsection.reloff:=0;
244  textsection.nreloc:=0;
245  textsection.flags:=S_ATTR_PURE_INSTRUCTIONS;
246  textsection.reserved1:=0;
247  textsection.reserved2:=0;
248
249  fillbyte(ressection,sizeof(ressection),0);
250  ressection.sectname:=RsrcSectName;
251  ressection.segname:=DataSegName;
252  ressection.addr:=0;
253  ressection.size:=segcommand.filesize;
254  ressection.offset:=segcommand.fileoff;
255  ressection.align:=fSectAlignment;
256  ressection.reloff:=fRelocations.StartOfs;
257  ressection.nreloc:=fRelocations.Count;
258  ressection.flags:=S_ATTR_LOC_RELOC;
259  ressection.reserved1:=0;
260  ressection.reserved2:=0;
261
262  fillbyte(bsssection,sizeof(bsssection),0);
263  bsssection.sectname:=HandlesSectName;
264  bsssection.segname:=DataSegName;
265  bsssection.addr:=fDataCurOfs;
266  bsssection.size:=sizeof(_ptrtype_)*aResources.Count;
267  bsssection.offset:=0;
268  bsssection.align:=fSectAlignment;
269  bsssection.reloff:=0;
270  bsssection.nreloc:=0;
271  bsssection.flags:=S_ZEROFILL;
272  bsssection.reserved1:=0;
273  bsssection.reserved2:=0;
274
275  if fOppositeEndianess then
276  begin
277    ldcommand.cmd:=SwapEndian(ldcommand.cmd);
278    ldcommand.cmdsize:=SwapEndian(ldcommand.cmdsize);
279
280    segcommand.vmaddr:=SwapEndian(segcommand.vmaddr);
281    segcommand.vmsize:=SwapEndian(segcommand.vmsize);
282    segcommand.fileoff:=SwapEndian(segcommand.fileoff);
283    segcommand.filesize:=SwapEndian(segcommand.filesize);
284    segcommand.maxprot:=SwapEndian(segcommand.maxprot);
285    segcommand.initprot:=SwapEndian(segcommand.initprot);
286    segcommand.nsects:=SwapEndian(segcommand.nsects);
287    segcommand.flags:=SwapEndian(segcommand.flags);
288
289    SwapSection(textsection);
290    SwapSection(ressection);
291    SwapSection(bsssection);
292  end;
293
294  aStream.WriteBuffer(ldcommand,sizeof(ldcommand));
295  aStream.WriteBuffer(segcommand,sizeof(segcommand));
296  aStream.WriteBuffer(textsection,sizeof(textsection));
297  aStream.WriteBuffer(ressection,sizeof(ressection));
298  aStream.WriteBuffer(bsssection,sizeof(bsssection));
299
300  ldcommand.cmd:=LC_SYMTAB;
301  ldcommand.cmdsize:=sizeof(TLoadCommand)+sizeof(symcommand);
302
303  symcommand.symoff:=fSymbolTable.StartOfs;
304  symcommand.nsyms:=fSymbolTable.Count;
305  symcommand.stroff:=fMachOStringTable.StartOfs;
306  symcommand.strsize:=NextAligned(4,fMachOStringTable.Size);
307
308  if fOppositeEndianess then
309  begin
310    ldcommand.cmd:=SwapEndian(ldcommand.cmd);
311    ldcommand.cmdsize:=SwapEndian(ldcommand.cmdsize);
312
313    symcommand.symoff:=SwapEndian(symcommand.symoff);
314    symcommand.nsyms:=SwapEndian(symcommand.nsyms);
315    symcommand.stroff:=SwapEndian(symcommand.stroff);
316    symcommand.strsize:=SwapEndian(symcommand.strsize);
317  end;
318
319  aStream.WriteBuffer(ldcommand,sizeof(ldcommand));
320  aStream.WriteBuffer(symcommand,sizeof(symcommand));
321
322  ldcommand.cmd:=LC_DYSYMTAB;
323  ldcommand.cmdsize:=sizeof(TLoadCommand)+sizeof(dysymcommand);
324
325  dysymcommand.ilocalsym:=0;
326  dysymcommand.nlocalsym:=fSymbolTable.LocalCount;
327  dysymcommand.iextdefsym:=dysymcommand.ilocalsym+dysymcommand.nlocalsym;
328  dysymcommand.nextdefsym:=fSymbolTable.GlobalCount;
329  dysymcommand.iundefsym:=dysymcommand.iextdefsym+dysymcommand.nextdefsym;
330  dysymcommand.nundefsym:=0;
331  dysymcommand.tocoff:=0;
332  dysymcommand.ntoc:=0;
333  dysymcommand.modtaboff:=0;
334  dysymcommand.nmodtab:=0;
335  dysymcommand.extrefsymoff:=0;
336  dysymcommand.nextrefsyms:=0;
337  dysymcommand.indirectsymoff:=0;
338  dysymcommand.nindirectsyms:=0;
339  dysymcommand.extreloff:=0;
340  dysymcommand.nextrel:=0;
341  dysymcommand.locreloff:=0;
342  dysymcommand.nlocrel:=0;
343
344  if fOppositeEndianess then
345  begin
346    ldcommand.cmd:=SwapEndian(ldcommand.cmd);
347    ldcommand.cmdsize:=SwapEndian(ldcommand.cmdsize);
348
349    dysymcommand.ilocalsym:=SwapEndian(dysymcommand.ilocalsym);
350    dysymcommand.nlocalsym:=SwapEndian(dysymcommand.nlocalsym);
351    dysymcommand.iextdefsym:=SwapEndian(dysymcommand.iextdefsym);
352    dysymcommand.nextdefsym:=SwapEndian(dysymcommand.nextdefsym);
353    dysymcommand.iundefsym:=SwapEndian(dysymcommand.iundefsym);
354    dysymcommand.nundefsym:=SwapEndian(dysymcommand.nundefsym);
355    dysymcommand.tocoff:=SwapEndian(dysymcommand.tocoff);
356    dysymcommand.ntoc:=SwapEndian(dysymcommand.ntoc);
357    dysymcommand.modtaboff:=SwapEndian(dysymcommand.modtaboff);
358    dysymcommand.nmodtab:=SwapEndian(dysymcommand.nmodtab);
359    dysymcommand.extrefsymoff:=SwapEndian(dysymcommand.extrefsymoff);
360    dysymcommand.nextrefsyms:=SwapEndian(dysymcommand.nextrefsyms);
361    dysymcommand.indirectsymoff:=SwapEndian(dysymcommand.indirectsymoff);
362    dysymcommand.nindirectsyms:=SwapEndian(dysymcommand.nindirectsyms);
363    dysymcommand.extreloff:=SwapEndian(dysymcommand.extreloff);
364    dysymcommand.nextrel:=SwapEndian(dysymcommand.nextrel);
365    dysymcommand.locreloff:=SwapEndian(dysymcommand.locreloff);
366    dysymcommand.nlocrel:=SwapEndian(dysymcommand.nlocrel);
367  end;
368
369  aStream.WriteBuffer(ldcommand,sizeof(ldcommand));
370  aStream.WriteBuffer(dysymcommand,sizeof(dysymcommand));
371end;
372
373constructor _TMachOSubWriter_.Create(aParent : TMachOResourceWriter;
374  const aMachineType : TMachOMachineType; const aSubMachineType: TMachoSubMachineType; const aOppositeEndianess : boolean);
375begin
376  inherited Create(aParent,aMachineType,aSubMachineType,aOppositeEndianess);
377  fSymbolTable:=_TMachOSymbolTable_.Create(fMachOStringTable);
378  fSymbolTable.OppositeEndianess:=fOppositeEndianess;
379  {$IF _TMachOSubWriter_=TMachO32SubWriter}
380  fDataAlignment:=4;
381  fSectAlignment:=2; //2^2
382  fSegType:=LC_SEGMENT;
383  {$ELSE}
384  fDataAlignment:=8;
385  fSectAlignment:=3; //2^3
386  fSegType:=LC_SEGMENT_64;
387  {$ENDIF}
388end;
389
390