elfresfix.pas 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  1. { *********************************************************************** }
  2. { }
  3. { elfresfix - Free Pascal Resource to ELF object compiler - fixup tool }
  4. { Part of the Free Pascal and CrossFPC distributions }
  5. { }
  6. { Copyright (C) 2005 Simon Kissel }
  7. { }
  8. { See the file COPYING.FPC, included in the FPC distribution, }
  9. { for details about the copyright. }
  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. }
  14. { }
  15. { *********************************************************************** }
  16. {
  17. This tool will update the fpc.resptrs section of an ELF executable to point
  18. to the various resource sections in the file. This is done so that the FPC
  19. RTL at runtime is able to get pointers to these sections.
  20. This tool is automatically run on any fpc compiled ELF executable that
  21. contains ELF resources.
  22. fpcresfix builds with Delphi, Kylix and FPC.
  23. Currently this only works on 32Bit targets, but support for 64Bit targets
  24. is in the works. Support for big endian systems is completely missing,
  25. though.
  26. }
  27. {$ifdef fpc}
  28. {$mode objfpc}
  29. {$endif}
  30. {$h+}
  31. unit elfresfix;
  32. interface
  33. uses
  34. SysUtils, Classes, elfbfd;
  35. Type
  36. TLogEvent = Procedure(Const Msg : String) of object;
  37. { TElfResourceFixer }
  38. TElfResourceFixer = Class(TObject)
  39. private
  40. FFileName: String;
  41. FOnVerbose: TLogEvent;
  42. FVerbose: Boolean;
  43. Procedure DoVerbose(Msg : String);
  44. public
  45. Procedure FixFile(AFileName : String);
  46. Procedure DoFixStream(Stream : TStream); virtual; abstract;
  47. Property Verbose : Boolean read FVerbose write FVerbose;
  48. Property FileName : String Read FFileName;
  49. Property Onverbose : TLogEvent Read FOnVerbose Write FOnVerbose;
  50. end;
  51. { TElf32ResourceFixer }
  52. TElf32ResourceFixer = Class(TElfResourceFixer)
  53. Procedure DoFixStream(Stream : TStream); override;
  54. end;
  55. { TElf64ResourceFixer }
  56. TElf64ResourceFixer = Class(TElfResourceFixer)
  57. Procedure DoFixStream(Stream : TStream); override;
  58. end;
  59. EElfResFixError = Class(Exception);
  60. Implementation
  61. ResourceString
  62. SCheckingHeader = 'Checking ELF Header... ';
  63. SReadingSectionHeaders = 'Reading Section Headers...';
  64. SHeaderOK = 'ELF Header is OK';
  65. SCheckingHeaderTable = 'Checking Section Header table...';
  66. SStrTabFound = 'Found strtab...';
  67. SProcessingSection = 'Processing section: ';
  68. SUpdatingResptrs = 'Updating resptrs section...';
  69. SFileFixed = 'File fixed successfully!';
  70. SNothingToFix = 'There was nothing to fix in this file.';
  71. SErrUnsupportedHeaderSize = 'Unsupported Section Header size.';
  72. SErrInvalidELFHeader = 'Not a valid linux ELF binary.';
  73. SErrResPtrsNotFound = 'Unable to find resptrs section.';
  74. Procedure DoError (Msg : String);
  75. begin
  76. Raise EElfResFixError.Create(Msg);
  77. end;
  78. Procedure DoErrorFmt (Msg : String; Args : Array of const);
  79. begin
  80. Raise EElfResFixError.CreateFmt(Msg,Args);
  81. end;
  82. { TElfResourceFixer }
  83. procedure TElfResourceFixer.DoVerbose(Msg: String);
  84. begin
  85. If FVerbose and Assigned(FOnVerbose) then
  86. FOnVerbose(Msg);
  87. end;
  88. procedure TElfResourceFixer.FixFile(AFileName: String);
  89. Var
  90. F : TStream;
  91. begin
  92. FFileName:=AFileName;
  93. F:=TFileStream.Create(AFilename,fmOpenReadWrite or fmShareDenyWrite);
  94. Try
  95. DoFixStream(F);
  96. Finally
  97. F.Free;
  98. end;
  99. end;
  100. { TElf32ResourceFixer }
  101. procedure TElf32ResourceFixer.DoFixStream(Stream: TStream);
  102. var
  103. ElfHeader:TElf32header;
  104. ResourceSectionTable: TElf32ResourceSectionTable;
  105. SectionHeaders: array of TElf32sechdr;
  106. i:integer;
  107. sn:string;
  108. SectionHeaderOffset:integer;
  109. fixed: boolean;
  110. strtab:string;
  111. SectionName: string;
  112. ResPtrsSection: integer;
  113. ResHashSection: integer;
  114. ResSymSection: integer;
  115. ResourceInfo: TELF32ResourceInfo;
  116. DataIndex, StringIndex: integer;
  117. SymString: string;
  118. procedure DoAlign(var value:integer; const a: integer);
  119. var i: integer;
  120. begin
  121. i:=(4 - (value MOD a)) MOD a;
  122. if (i>0) then inc(value,i);
  123. end;
  124. begin
  125. Fixed:=False;
  126. Stream.Read(ElfHeader,sizeof(TElf32header));
  127. DoVerbose(SCheckingHeader);
  128. if (ElfHeader.magic0123<>$464C457F) then
  129. DoError(SErrInvalidELFheader);
  130. if ElfHeader.e_shentsize=sizeof(TElf32sechdr) then
  131. DoVerbose(SHeaderOK)
  132. else
  133. DoError(SErrUnSupportedHeaderSize);
  134. DoVerbose(SReadingSectionHeaders);
  135. setlength(SectionHeaders,ElfHeader.e_shnum);
  136. SectionHeaderOffset:=ElfHeader.e_shoff;
  137. Stream.Position:=SectionHeaderOffset;
  138. for i:=0 to ElfHeader.e_shnum-1 do
  139. begin
  140. Stream.Read(SectionHeaders[i],sizeof(TElf32sechdr));
  141. end;
  142. DoVerbose(SCheckingHeaderTable);
  143. // Get the section header strtab
  144. i:=ElfHeader.e_shstrndx;
  145. if SectionHeaders[i].sh_type=SHT_STRTAB then
  146. begin
  147. DoVerbose(SStrTabFound);
  148. // read the strtab
  149. Stream.Position:=SectionHeaders[i].sh_offset;
  150. setlength(strtab,SectionHeaders[i].sh_size);
  151. Stream.Read(strtab[1],SectionHeaders[i].sh_size);
  152. end
  153. else
  154. begin
  155. writeln('Error: Unable to find strtab.');
  156. halt(5);
  157. end;
  158. ResPtrsSection:=-1;
  159. ResHashSection:=-1;
  160. ResourceSectionTable.version:=66;
  161. // Next cycle through all sections to gather pointers to all the resource
  162. // sections, and note the index of the resptrs section
  163. for i:=0 to ElfHeader.e_shnum-1 do
  164. begin
  165. SectionName:=copy(strtab,SectionHeaders[i].sh_name+1,32);
  166. SectionName:=copy(SectionName,1,pos(#0,SectionName)-1);
  167. DoVerbose(SProcessingSection+SectionName);
  168. sn:=Copy(SectionName,1,4);
  169. // FPC section ?
  170. if (sn='fpc.') then
  171. begin
  172. sn:=SectionName;
  173. Delete(SN,1,4);
  174. if SN='resptrs' then
  175. begin
  176. ResPtrsSection:=i;
  177. end
  178. else if sn='ressym' then
  179. begin
  180. ResSymSection:=i;
  181. ResourceSectionTable.ressym.ptr:=SectionHeaders[i].sh_addr;
  182. ResourceSectionTable.ressym.size:=SectionHeaders[i].sh_size;
  183. end
  184. else if sn='reshash' then
  185. begin
  186. ResHashSection:=i;
  187. ResourceSectionTable.reshash.ptr:=SectionHeaders[i].sh_addr;
  188. ResourceSectionTable.reshash.size:=SectionHeaders[i].sh_size;
  189. ResourceSectionTable.resentries:=SectionHeaders[i].sh_size DIV sizeof(TELF32ResourceInfo);
  190. end
  191. else if sn='resdata' then
  192. begin
  193. ResourceSectionTable.resdata.ptr:=SectionHeaders[i].sh_addr;
  194. ResourceSectionTable.resdata.size:=SectionHeaders[i].sh_size;
  195. end
  196. else if sn='resspare' then
  197. begin
  198. ResourceSectionTable.resspare.ptr:=SectionHeaders[i].sh_addr;
  199. ResourceSectionTable.resspare.size:=SectionHeaders[i].sh_size;
  200. end
  201. else if SectionName='resstr' then
  202. begin
  203. ResourceSectionTable.resstr.ptr:=SectionHeaders[i].sh_addr;
  204. ResourceSectionTable.resstr.size:=SectionHeaders[i].sh_size;
  205. end;
  206. end
  207. end;
  208. // Ok, we now have pointers to all resource sections and also
  209. // know the number of resources.
  210. // Now update the resptrs table
  211. if (ResPtrsSection>-1) and (ResHashSection>-1) and (ResSymSection>-1) then
  212. begin
  213. Doverbose(SUpdatingResPtrs);
  214. Stream.Position:=SectionHeaders[ResPtrsSection].sh_offset;
  215. Stream.Write(ResourceSectionTable,sizeof(TELF32ResourceSectionTable));
  216. // LD might have merged the sections of several linked .or together
  217. // Therefore our data and stringtable offsets might be messed up and need to recalculated
  218. // First get the symbol string
  219. Stream.Position:=SectionHeaders[ResSymSection].sh_offset;
  220. setlength(SymString, SectionHeaders[ResSymSection].sh_size);
  221. Stream.Read(SymString[1], SectionHeaders[ResSymSection].sh_size);
  222. DataIndex:=0;
  223. StringIndex:=0;
  224. Stream.Position:=SectionHeaders[ResHashSection].sh_offset;
  225. for i:=0 to ResourceSectionTable.resentries-1 do
  226. begin
  227. Stream.Position:=SectionHeaders[ResHashSection].sh_offset+i*sizeof(TELF32ResourceInfo);
  228. Stream.Read(ResourceInfo, sizeof(TELF32ResourceInfo));
  229. ResourceInfo.ptr:=DataIndex;
  230. ResourceInfo.name:=StringIndex;
  231. // advance for next entry
  232. DataIndex:=DataIndex+ResourceInfo.size;
  233. DoAlign(DataIndex,4); // The data blocks are 32bit aligned
  234. // find end of current string
  235. while SymString[StringIndex+1]<>#0 do
  236. inc(StringIndex,1);
  237. inc(StringIndex,1);
  238. // this should be the start of the next string
  239. // write back the entry
  240. Stream.Position:=SectionHeaders[ResHashSection].sh_offset+i*sizeof(TELF32ResourceInfo);
  241. Stream.Write(ResourceInfo, sizeof(TELF32ResourceInfo));
  242. end;
  243. fixed:=true;
  244. end
  245. else
  246. DoError(SErrREsptrsNotFound);
  247. if fixed then
  248. DoVerbose(SFileFixed)
  249. else
  250. writeln(SNothingToFix);
  251. end;
  252. { TElf64ResourceFixer }
  253. procedure TElf64ResourceFixer.DoFixStream(Stream: TStream);
  254. begin
  255. DoError('64-bit resources not yet supported');
  256. end;
  257. end.