xcoffwriter.pp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413
  1. {
  2. This file is part of the Free Pascal run time library.
  3. Copyright (c) 2008 by Giulio Bernardi
  4. Resource writer for COFF files
  5. See the file COPYING.FPC, included in this distribution,
  6. for details about the copyright.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. **********************************************************************}
  11. {$IFNDEF FPC_DOTTEDUNITS}
  12. unit xcoffwriter;
  13. {$ENDIF FPC_DOTTEDUNITS}
  14. {$MODE OBJFPC} {$H+}
  15. interface
  16. {$IFDEF FPC_DOTTEDUNITS}
  17. uses
  18. System.Classes, System.SysUtils, System.Resources.StringTable.Types, System.Resources.Resource, System.Resources.Tree,
  19. System.Resources.Coff.Types, System.Resources.Coff.Writer;
  20. {$ELSE FPC_DOTTEDUNITS}
  21. uses
  22. Classes, SysUtils, strtable, resource, resourcetree, cofftypes, coffwriter;
  23. {$ENDIF FPC_DOTTEDUNITS}
  24. type
  25. { TXCoffResourceWriter }
  26. TXCoffResourceWriter = class (TCoffResourceWriter)
  27. protected
  28. fResStrTable : TResStringTable;
  29. fCurOfs : longword;
  30. fDataAlignment : integer;
  31. function NextAligned(aBound, aValue : longword) : longword;
  32. procedure Align(aBound : integer; aStream : TStream);
  33. function GetFixedCoffHeader: TCoffHeader; override;
  34. procedure WriteEmptySectionHeader(aStream: TStream); override;
  35. procedure FixSectionHeader(aStream : TStream; aResources : TResources); override;
  36. function PrescanNode(aNode: TResourceTreeNode; aNodeSize : longword): longword; override;
  37. procedure PrescanResourceTree; override;
  38. procedure WriteNodeInfos(aStream: TStream);
  39. procedure WriteNodeInfo(aStream: TStream; aNode: TResourceTreeNode);
  40. procedure WriteSubNodes(aStream : TStream; aNode : TResourceTreeNode);
  41. procedure WriteResStringTable(aStream: TStream); override;
  42. procedure WriteDataSymbol(aStream: TStream; const name: AnsiString; aStorageClass, aAuxStorageType: byte; aSecNum, aSecOffset, aSize: qword);
  43. procedure WriteSymbolTable(aStream : TStream; aResources : TResources); override;
  44. procedure WriteResHeader(aStream : TStream; aResources : TResources);
  45. procedure Write(aResources : TResources; aStream : TStream); override;
  46. public
  47. constructor Create; override;
  48. destructor Destroy; override;
  49. end;
  50. implementation
  51. {$IFDEF FPC_DOTTEDUNITS}
  52. uses System.Resources.Coff.Consts, System.Resources.Types;
  53. {$ELSE FPC_DOTTEDUNITS}
  54. uses coffconsts,fpcrestypes;
  55. {$ENDIF FPC_DOTTEDUNITS}
  56. type
  57. // Todo: 64 bit
  58. _ptrtype_ = longword;
  59. _TResHdr_ = TResHdr32;
  60. _TResInfoNode_ = TResInfoNode32;
  61. { much of the code below is (almost) identical to the code in the Elf and
  62. Mach-O writers (they already duplicated lots of stuff). Todo: consolidate.
  63. }
  64. { TXCoffResourceWriter }
  65. function TXCoffResourceWriter.NextAligned(aBound, aValue : longword) : longword;
  66. var topad : longword;
  67. begin
  68. Result:=aValue;
  69. topad:=aBound-(aValue mod aBound);
  70. if topad<>aBound then inc(Result,topad);
  71. end;
  72. procedure TXCoffResourceWriter.Align(aBound: integer; aStream: TStream);
  73. var topad,tmp : integer;
  74. qw : qword;
  75. begin
  76. qw:=0;
  77. topad:=aBound-(aStream.Position mod aBound);
  78. if topad<>aBound then
  79. while topad>0 do
  80. begin
  81. if topad>8 then tmp:=8 else tmp:=topad;
  82. aStream.WriteBuffer(qw,tmp);
  83. dec(topad,tmp);
  84. end;
  85. end;
  86. function TXCoffResourceWriter.GetFixedCoffHeader: TCoffHeader;
  87. begin
  88. Result:=inherited GetFixedCoffHeader;
  89. { we also have a BSS section }
  90. Result.NumSects:=2;
  91. end;
  92. procedure TXCoffResourceWriter.WriteEmptySectionHeader(aStream: TStream);
  93. // Todo: 64 bit
  94. var hdr : TXCoff32SectionHeader;
  95. begin
  96. FillByte(hdr,sizeof(hdr),0);
  97. { .data }
  98. aStream.WriteBuffer(hdr,sizeof(hdr));
  99. { .bss }
  100. aStream.WriteBuffer(hdr,sizeof(hdr));
  101. end;
  102. procedure TXCoffResourceWriter.FixSectionHeader(aStream: TStream; aResources: TResources);
  103. // Todo: 64 bit
  104. var hdr : TXCoff32SectionHeader;
  105. oldpos : int64;
  106. begin
  107. oldpos:=aStream.Position;
  108. aStream.Position:=sizeof(TCoffHeader);
  109. { initialized data }
  110. hdr.s_name:='.data'#0#0#0;
  111. hdr.s_paddr:=0;
  112. hdr.s_vaddr:=0;
  113. hdr.s_size:=fResDataEntryCurrentRVA;
  114. hdr.s_scnptr:=sizeof(TCoffHeader)+2*sizeof(TCoffSectionHeader);
  115. hdr.s_relptr:=fRelocations.StartAddress;
  116. hdr.s_lnnoptr:=0;
  117. hdr.s_nreloc:=fRelocations.Count;
  118. hdr.s_nlnno:=0;
  119. hdr.s_flags:=STYP_DATA;
  120. if OppositeEndianess then
  121. begin
  122. hdr.s_paddr:=SwapEndian(hdr.s_paddr);
  123. hdr.s_vaddr:=SwapEndian(hdr.s_vaddr);
  124. hdr.s_size:=SwapEndian(hdr.s_size);
  125. hdr.s_scnptr:=SwapEndian(hdr.s_scnptr);
  126. hdr.s_relptr:=SwapEndian(hdr.s_relptr);
  127. hdr.s_lnnoptr:=SwapEndian(hdr.s_lnnoptr);
  128. hdr.s_nreloc:=SwapEndian(hdr.s_nreloc);
  129. hdr.s_nlnno:=SwapEndian(hdr.s_nlnno);
  130. hdr.s_flags:=SwapEndian(hdr.s_flags);
  131. end;
  132. aStream.WriteBuffer(hdr,sizeof(hdr));
  133. { uninitialized data }
  134. hdr.s_name:='.bss'#0#0#0#0;
  135. hdr.s_paddr:=0;
  136. hdr.s_vaddr:=0;
  137. hdr.s_size:=aResources.Count*sizeof(_ptrtype_);
  138. hdr.s_scnptr:=0;
  139. hdr.s_relptr:=0;
  140. hdr.s_lnnoptr:=0;
  141. hdr.s_nreloc:=0;
  142. hdr.s_nlnno:=0;
  143. hdr.s_flags:=STYP_BSS;
  144. if OppositeEndianess then
  145. begin
  146. hdr.s_paddr:=SwapEndian(hdr.s_paddr);
  147. hdr.s_vaddr:=SwapEndian(hdr.s_vaddr);
  148. hdr.s_size:=SwapEndian(hdr.s_size);
  149. hdr.s_scnptr:=SwapEndian(hdr.s_scnptr);
  150. hdr.s_relptr:=SwapEndian(hdr.s_relptr);
  151. hdr.s_lnnoptr:=SwapEndian(hdr.s_lnnoptr);
  152. hdr.s_nreloc:=SwapEndian(hdr.s_nreloc);
  153. hdr.s_nlnno:=SwapEndian(hdr.s_nlnno);
  154. hdr.s_flags:=SwapEndian(hdr.s_flags);
  155. end;
  156. aStream.WriteBuffer(hdr,sizeof(hdr));
  157. aStream.Position:=oldpos;
  158. end;
  159. function TXCoffResourceWriter.PrescanNode(aNode: TResourceTreeNode; aNodeSize : longword): longword;
  160. var curofs : longword;
  161. i : integer;
  162. subnode : TResourceTreeNode;
  163. begin
  164. if aNode.IsLeaf then
  165. begin
  166. Result:=aNode.SubDirRVA;
  167. exit;
  168. end;
  169. if aNode.Desc.DescType=dtName then
  170. aNode.NameRVA:=fResStrTable.Add(aNode.Desc.Name);
  171. //first node subnodes begin at curofs (after all node headers)
  172. curofs:=aNode.SubDirRva+(aNode.NamedCount+aNode.IDCount)*aNodeSize;
  173. for i:=0 to aNode.NamedCount-1 do
  174. begin
  175. subnode:=aNode.NamedEntries[i];
  176. subnode.SubDirRVA:=curofs;
  177. curofs:=PrescanNode(subnode,aNodeSize);
  178. end;
  179. for i:=0 to aNode.IDCount-1 do
  180. begin
  181. subnode:=aNode.IDEntries[i];
  182. subnode.SubDirRVA:=curofs;
  183. curofs:=PrescanNode(subnode,aNodeSize);
  184. end;
  185. Result:=curofs;
  186. end;
  187. procedure TXCoffResourceWriter.PrescanResourceTree;
  188. begin
  189. fResStrTable.Clear;
  190. fRoot.SubDirRVA:=sizeof(_TResHdr_)+sizeof(_TResInfoNode_);
  191. fResStrTable.StartOfs:=PrescanNode(fRoot,sizeof(_TResInfoNode_));
  192. if fResStrTable.Used then
  193. fResDataEntryCurrentRVA:=NextAligned(fDataAlignment,fResStrTable.StartOfs+fResStrTable.Size)
  194. else
  195. fResDataEntryCurrentRVA:=fResStrTable.StartOfs;
  196. end;
  197. procedure TXCoffResourceWriter.WriteNodeInfos(aStream: TStream);
  198. begin
  199. fCurOfs:=sizeof(_TResHdr_);
  200. WriteNodeInfo(aStream,fRoot);
  201. WriteSubNodes(aStream,fRoot);
  202. end;
  203. procedure TXCoffResourceWriter.WriteNodeInfo(aStream: TStream; aNode: TResourceTreeNode);
  204. var infonode : _TResInfoNode_;
  205. begin
  206. if aNode.Desc.DescType=dtID then
  207. infonode.nameid:=aNode.Desc.ID
  208. else
  209. begin
  210. infonode.nameid:=fResStrTable.StartOfs+aNode.NameRVA;
  211. fRelocations.AddRelativeToSection(fCurOfs,fResDataSectionSymIdx);
  212. end;
  213. infonode.ncount:=aNode.NamedCount;
  214. if aNode.IsLeaf then
  215. begin
  216. infonode.idcountsize:=aNode.Data.RawData.Size;
  217. infonode.subptr:=fResDataEntryCurrentRVA;
  218. fResDataEntryCurrentRVA:=NextAligned(fDataAlignment,fResDataEntryCurrentRVA+infonode.idcountsize);
  219. end
  220. else
  221. begin
  222. infonode.idcountsize:=aNode.IDCount;
  223. infonode.subptr:=aNode.SubDirRVA;
  224. end;
  225. fRelocations.AddRelativeToSection(
  226. fCurOfs+sizeof(infonode.nameid)+sizeof(infonode.ncount)+
  227. sizeof(infonode.idcountsize),fResDataSectionSymIdx);
  228. if fOppositeEndianess then
  229. begin
  230. infonode.nameid:=SwapEndian(infonode.nameid);
  231. infonode.ncount:=SwapEndian(infonode.ncount);
  232. infonode.idcountsize:=SwapEndian(infonode.idcountsize);
  233. infonode.subptr:=SwapEndian(infonode.subptr);
  234. end;
  235. aStream.WriteBuffer(infonode,sizeof(infonode));
  236. inc(fCurOfs,sizeof(infonode));
  237. end;
  238. procedure TXCoffResourceWriter.WriteSubNodes(aStream: TStream; aNode: TResourceTreeNode);
  239. var i : integer;
  240. begin
  241. for i:=0 to aNode.NamedCount-1 do
  242. WriteNodeInfo(aStream,aNode.NamedEntries[i]);
  243. for i:=0 to aNode.IDCount-1 do
  244. WriteNodeInfo(aStream,aNode.IDEntries[i]);
  245. for i:=0 to aNode.NamedCount-1 do
  246. WriteSubNodes(aStream,aNode.NamedEntries[i]);
  247. for i:=0 to aNode.IDCount-1 do
  248. WriteSubNodes(aStream,aNode.IDEntries[i]);
  249. end;
  250. procedure TXCoffResourceWriter.WriteResStringTable(aStream: TStream);
  251. begin
  252. if fResStrTable.Used then
  253. fResStrTable.WriteToStream(aStream);
  254. Align(fDataAlignment,aStream);
  255. end;
  256. procedure TXCoffResourceWriter.WriteDataSymbol(aStream: TStream; const name: AnsiString; aStorageClass, aAuxStorageType: byte; aSecNum, aSecOffset, aSize: qword);
  257. var
  258. st : TCoffSymtableEntry;
  259. aux : TXCoffAuxSymbol32;
  260. offs : dword;
  261. begin
  262. { top 4 bytes 0, lower 4 bytes = offset in string table }
  263. st.n_name:=#0#0#0#0;
  264. offs:=fStringTable.Size;
  265. if OppositeEndianess then
  266. offs:=SwapEndian(offs);
  267. PDWord(@st.n_name[4])^:=offs;
  268. fStringTable.Add(name);
  269. st.n_value:=aSecOffset;
  270. st.n_scnum:=aSecNum;
  271. st.n_type:=0;
  272. st.n_sclass:=aStorageClass;
  273. st.n_numaux:=1;
  274. if OppositeEndianess then
  275. begin
  276. st.n_value:=SwapEndian(st.n_value);
  277. st.n_scnum:=SwapEndian(st.n_scnum);
  278. st.n_type:=SwapEndian(st.n_type);
  279. end;
  280. aStream.WriteBuffer(st,sizeof(st));
  281. inc(fNumSymtableEntries);
  282. aux.x_scnlen:=aSize;
  283. aux.x_parmhash:=0;
  284. aux.x_snhash:=0;
  285. aux.x_smtyp:=aAuxStorageType;
  286. aux.x_smclas:=XMC_RW;
  287. aux.x_stab:=0;
  288. aux.x_snstab:=0;
  289. if OppositeEndianess then
  290. begin
  291. aux.x_scnlen:=SwapEndian(aux.x_scnlen);
  292. aux.x_parmhash:=SwapEndian(aux.x_parmhash);
  293. aux.x_snhash:=SwapEndian(aux.x_snhash);
  294. aux.x_stab:=SwapEndian(aux.x_stab);
  295. aux.x_snstab:=SwapEndian(aux.x_snstab);
  296. end;
  297. aStream.WriteBuffer(aux,sizeof(aux));
  298. inc(fNumSymtableEntries);
  299. end;
  300. procedure TXCoffResourceWriter.WriteSymbolTable(aStream: TStream; aResources : TResources);
  301. const
  302. SECTION_DATA_ALIGNMENT = 8;
  303. begin
  304. fSymTablePtr:=aStream.Position;
  305. { if order is changed, also adapt fResDataSectionSymIdx and fResHandlesSectionSymIdx }
  306. { initialized data }
  307. WriteDataSymbol(aStream,XCoffRsrcSectName,IMAGE_SYM_CLASS_HIDEXT,SECTION_DATA_ALIGNMENT or XTY_SD,1,0,fResDataEntryCurrentRVA);
  308. { create global FPC_RESSYMBOL symbol at the start of XCoffRsrcSectName
  309. (for XTY_LD: "size" = symbol index of the containing csect) }
  310. WriteDataSymbol(aStream,'FPC_RESSYMBOL',IMAGE_SYM_CLASS_EXT,XTY_LD,1,0,0);
  311. { uninitialized data }
  312. WriteDataSymbol(aStream,XCoffHandlesSectName,IMAGE_SYM_CLASS_HIDEXT,SECTION_DATA_ALIGNMENT or XTY_CM,2,0,aResources.Count*sizeof(_ptrtype_));
  313. end;
  314. procedure TXCoffResourceWriter.WriteResHeader(aStream: TStream; aResources: TResources);
  315. var hdr : _TResHdr_;
  316. begin
  317. hdr.rootptr:=sizeof(hdr);
  318. hdr.count:=aResources.Count;
  319. hdr.usedhandles:=0;
  320. hdr.handles:=0;
  321. fRelocations.AddRelativeToSection(0,fResDataSectionSymIdx);
  322. fRelocations.AddRelativeToSection(sizeof(hdr.rootptr)+sizeof(hdr.count)+sizeof(hdr.usedhandles),fResHandlesSectionSymIdx);
  323. if fOppositeEndianess then
  324. begin
  325. hdr.rootptr:=SwapEndian(hdr.rootptr);
  326. hdr.count:=SwapEndian(hdr.count);
  327. // only used at run time, always 0 in object file
  328. // hdr.usedhandles:=SwapEndian(hdr.usedhandles);
  329. // pointer to first byte of fpc.reshandles; since it's in a separate
  330. // section, this is at address 0 so we don't have to change this anymore
  331. // later either (hdr.handles is not yet known at this point)
  332. // hdr.handles:=SwapEndian(hdr.handles);
  333. end;
  334. aStream.WriteBuffer(hdr,sizeof(hdr));
  335. end;
  336. procedure TXCoffResourceWriter.Write(aResources: TResources; aStream: TStream);
  337. begin
  338. WriteEmptyCoffHeader(aStream);
  339. WriteEmptySectionHeader(aStream);
  340. fRoot:=TRootResTreeNode(GetTree(aResources));
  341. PrescanResourceTree;
  342. WriteResHeader(aStream,aResources);
  343. WriteNodeInfos(aStream);
  344. WriteResStringTable(aStream);
  345. WriteRawData(aStream);
  346. WriteRelocations(aStream);
  347. WriteSymbolTable(aStream,aResources);
  348. WriteCoffStringTable(aStream);
  349. FixCoffHeader(aStream);
  350. FixSectionHeader(aStream,aResources);
  351. end;
  352. constructor TXCoffResourceWriter.Create;
  353. begin
  354. inherited;
  355. fResStrTable:=TResStringTable.Create;
  356. fResDataSectionSymIdx:=0;
  357. { skip auxilary symtable entry part of data section symbol and FPC_RESSYMBOL }
  358. fResHandlesSectionSymIdx:=4;
  359. // TODO: 64 bit
  360. fDataAlignment:=4;
  361. end;
  362. destructor TXCoffResourceWriter.Destroy;
  363. begin
  364. fResStrTable.Free;
  365. inherited Destroy;
  366. end;
  367. initialization
  368. TResources.RegisterWriter('.o',TXCoffResourceWriter);
  369. end.