cpuelf.pas 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459
  1. {
  2. Copyright (c) 1998-2006 by Peter Vreman
  3. Includes ELF-related code specific to i386
  4. This program is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation; either version 2 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with this program; if not, write to the Free Software
  14. Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  15. ****************************************************************************
  16. }
  17. unit cpuelf;
  18. {$i fpcdefs.inc}
  19. interface
  20. implementation
  21. uses
  22. globtype,cclasses,
  23. verbose,
  24. systems,ogbase,ogelf,assemble;
  25. type
  26. TElfTarget386=class(TElfTarget)
  27. class function encodereloc(objrel:TObjRelocation):byte;override;
  28. class procedure loadreloc(objrel:TObjRelocation);override;
  29. end;
  30. TElfExeOutput386=class(TElfExeOutput)
  31. private
  32. procedure MaybeWriteGOTEntry(reltyp:byte;relocval:aint;exesym:TExeSymbol);
  33. protected
  34. procedure WriteFirstPLTEntry;override;
  35. procedure WritePLTEntry(exesym:TExeSymbol);override;
  36. procedure WriteIndirectPLTEntry(exesym:TExeSymbol);override;
  37. procedure GOTRelocPass1(objsec:TObjSection;ObjReloc:TObjRelocation);override;
  38. procedure DoRelocationFixup(objsec:TObjSection);override;
  39. end;
  40. const
  41. { Relocation types }
  42. R_386_NONE = 0;
  43. R_386_32 = 1; { ordinary absolute relocation }
  44. R_386_PC32 = 2; { PC-relative relocation }
  45. R_386_GOT32 = 3; { an offset into GOT }
  46. R_386_PLT32 = 4; { a PC-relative offset into PLT }
  47. R_386_COPY = 5;
  48. R_386_GLOB_DAT = 6;
  49. R_386_JUMP_SLOT = 7;
  50. R_386_RELATIVE = 8;
  51. R_386_GOTOFF = 9; { an offset from GOT base }
  52. R_386_GOTPC = 10; { a PC-relative offset _to_ GOT }
  53. R_386_TLS_TPOFF = 14;
  54. R_386_TLS_IE = 15;
  55. R_386_TLS_GOTIE = 16;
  56. R_386_TLS_LE = 17;
  57. R_386_TLS_GD = 18;
  58. R_386_TLS_LDM = 19;
  59. R_386_16 = 20;
  60. R_386_PC16 = 21;
  61. R_386_8 = 22;
  62. R_386_PC8 = 23;
  63. R_386_TLS_GD_32 = 24;
  64. R_386_TLS_GD_PUSH = 25;
  65. R_386_TLS_GD_CALL = 26;
  66. R_386_TLS_GD_POP = 27;
  67. R_386_TLS_LDM_32 = 28;
  68. R_386_TLS_LDM_PUSH = 29;
  69. R_386_TLS_LDM_CALL = 30;
  70. R_386_TLS_LDM_POP = 31;
  71. R_386_TLS_LDO_32 = 32;
  72. R_386_TLS_IE_32 = 33;
  73. R_386_TLS_LE_32 = 34;
  74. R_386_TLS_DTPMOD32 = 35;
  75. R_386_TLS_DTPOFF32 = 36;
  76. R_386_TLS_TPOFF32 = 37;
  77. { 38 is unused }
  78. R_386_TLS_GOTDESC = 39;
  79. R_386_TLS_DESC_CALL = 40;
  80. R_386_TLS_DESC = 41;
  81. R_386_IRELATIVE = 42;
  82. R_386_GNU_VTINHERIT = 250;
  83. R_386_GNU_VTENTRY = 251;
  84. {****************************************************************************
  85. TElfTarget386
  86. ****************************************************************************}
  87. class function TElfTarget386.encodereloc(objrel:TObjRelocation):byte;
  88. begin
  89. case objrel.typ of
  90. RELOC_NONE :
  91. result:=R_386_NONE;
  92. RELOC_RELATIVE :
  93. result:=R_386_PC32;
  94. RELOC_ABSOLUTE :
  95. result:=R_386_32;
  96. RELOC_GOT32 :
  97. result:=R_386_GOT32;
  98. RELOC_GOTPC :
  99. result:=R_386_GOTPC;
  100. RELOC_PLT32 :
  101. result:=R_386_PLT32;
  102. else
  103. result:=0;
  104. InternalError(2012082301);
  105. end;
  106. end;
  107. class procedure TElfTarget386.loadreloc(objrel:TObjRelocation);
  108. begin
  109. end;
  110. {****************************************************************************
  111. TElfExeOutput386
  112. ****************************************************************************}
  113. procedure TElfExeOutput386.WriteFirstPLTEntry;
  114. begin
  115. if IsSharedLibrary then
  116. // push 4(%ebx); jmp *8(%ebx)
  117. pltobjsec.writeBytes(#$FF#$B3#$04#$00#$00#$00#$FF#$A3#$08#$00#$00#$00)
  118. else
  119. begin
  120. pltobjsec.writeBytes(#$FF#$35); // push got+4
  121. pltobjsec.writeReloc_internal(gotpltobjsec,sizeof(pint),4,RELOC_ABSOLUTE);
  122. pltobjsec.writeBytes(#$FF#$25); // jmp *got+8
  123. pltobjsec.writeReloc_internal(gotpltobjsec,2*sizeof(pint),4,RELOC_ABSOLUTE);
  124. end;
  125. pltobjsec.writeBytes(#$90#$90#$90#$90); // nop
  126. end;
  127. procedure TElfExeOutput386.WritePLTEntry(exesym:TExeSymbol);
  128. var
  129. got_offset: aword;
  130. tmp:pint;
  131. begin
  132. got_offset:=gotpltobjsec.size;
  133. if IsSharedLibrary then
  134. begin
  135. pltobjsec.writeBytes(#$FF#$A3); // jmp got+x(%ebx)
  136. pltobjsec.write(got_offset,4);
  137. end
  138. else
  139. begin
  140. pltobjsec.writeBytes(#$FF#$25); // jmp *got+x
  141. pltobjsec.writeReloc_internal(gotpltobjsec,got_offset,4,RELOC_ABSOLUTE);
  142. end;
  143. pltobjsec.writeBytes(#$68); // push $index
  144. tmp:=pltrelocsec.size;
  145. pltobjsec.write(tmp,4);
  146. pltobjsec.writeBytes(#$E9); // jmp .plt
  147. tmp:=-(4+pltobjsec.Size);
  148. pltobjsec.write(tmp,4);
  149. { write a .got.plt slot pointing back to the 'push' instruction }
  150. gotpltobjsec.writeReloc_internal(pltobjsec,pltobjsec.size-(16-6),sizeof(pint),RELOC_ABSOLUTE);
  151. { write a .rel.plt entry }
  152. pltrelocsec.writeReloc_internal(gotpltobjsec,got_offset,sizeof(pint),RELOC_ABSOLUTE);
  153. got_offset:=(exesym.dynindex shl 8) or R_386_JUMP_SLOT;
  154. pltrelocsec.write(got_offset,sizeof(pint));
  155. if relocs_use_addend then
  156. pltrelocsec.writezeros(sizeof(pint));
  157. end;
  158. procedure TElfExeOutput386.WriteIndirectPLTEntry(exesym:TExeSymbol);
  159. begin
  160. // TODO
  161. inherited WriteIndirectPLTEntry(exesym);
  162. end;
  163. procedure TElfExeOutput386.GOTRelocPass1(objsec:TObjSection;ObjReloc:TObjRelocation);
  164. var
  165. objsym:TObjSymbol;
  166. sym:TExeSymbol;
  167. reltyp:byte;
  168. begin
  169. if (ObjReloc.flags and rf_raw)=0 then
  170. reltyp:=ElfTarget.encodereloc(ObjReloc)
  171. else
  172. reltyp:=ObjReloc.ftype;
  173. case reltyp of
  174. R_386_PLT32:
  175. begin
  176. objsym:=objreloc.symbol.exesymbol.ObjSymbol;
  177. objsym.refs:=objsym.refs or symref_plt;
  178. end;
  179. R_386_GOT32:
  180. begin
  181. sym:=ObjReloc.symbol.exesymbol;
  182. { Although local symbols should not be accessed through GOT,
  183. this isn't strictly forbidden. In this case we need to fake up
  184. the exesym to store the GOT offset in it.
  185. TODO: name collision; maybe use a different symbol list object? }
  186. if sym=nil then
  187. begin
  188. sym:=TExeSymbol.Create(ExeSymbolList,objreloc.symbol.name+'*local*');
  189. sym.objsymbol:=objreloc.symbol;
  190. objreloc.symbol.exesymbol:=sym;
  191. end;
  192. if sym.GotOffset>0 then
  193. exit;
  194. gotobjsec.alloc(sizeof(pint));
  195. sym.GotOffset:=gotobjsec.size;
  196. { In shared library, every GOT entry needs a RELATIVE dynamic reloc,
  197. imported/exported symbols need GLOB_DAT instead. For executables,
  198. only the latter applies. }
  199. if IsSharedLibrary or (sym.dynindex>0) then
  200. dynrelocsec.alloc(dynrelocsec.shentsize);
  201. end;
  202. R_386_32:
  203. begin
  204. { TODO: How to handle absolute relocation to *weak* external symbol
  205. from executable? See test/tweaklib2, symbol test2, ld handles it
  206. differently for PIC and non-PIC code. In non-PIC code it drops
  207. dynamic relocation altogether. }
  208. if not IsSharedLibrary then
  209. exit;
  210. if (oso_executable in objsec.SecOptions) or
  211. not (oso_write in objsec.SecOptions) then
  212. hastextrelocs:=True;
  213. dynrelocsec.alloc(dynrelocsec.shentsize);
  214. objreloc.flags:=objreloc.flags or rf_dynamic;
  215. end;
  216. R_386_PC32:
  217. begin
  218. if not IsSharedLibrary then
  219. exit;
  220. { In shared library PC32 reloc to external symbol cannot be redirected
  221. to PLT entry, because PIC PLT relies on ebx register set properly. }
  222. if assigned(objreloc.symbol) and
  223. (
  224. (objreloc.symbol.objsection=nil) or
  225. (oso_plt in objreloc.symbol.objsection.SecOptions)
  226. ) then
  227. begin
  228. { Must be a dynamic symbol }
  229. if not (assigned(objreloc.symbol.exesymbol) and
  230. (objreloc.symbol.exesymbol.dynindex<>0)) then
  231. InternalError(2012101201);
  232. if (oso_executable in objsec.SecOptions) or
  233. not (oso_write in objsec.SecOptions) then
  234. hastextrelocs:=True;
  235. dynrelocsec.alloc(dynrelocsec.shentsize);
  236. objreloc.flags:=objreloc.flags or rf_dynamic;
  237. end;
  238. end;
  239. end;
  240. end;
  241. procedure TElfExeOutput386.MaybeWriteGOTEntry(reltyp:byte;relocval:aint;exesym:TExeSymbol);
  242. var
  243. gotoff,dynidx,tmp:aword;
  244. begin
  245. gotoff:=exesym.gotoffset;
  246. if gotoff=0 then
  247. InternalError(2012060902);
  248. { the GOT slot itself, and a dynamic relocation for it }
  249. { TODO: only data symbols must get here }
  250. if gotoff=gotobjsec.Data.size+sizeof(pint) then
  251. begin
  252. dynidx:=exesym.dynindex;
  253. gotobjsec.write(relocval,sizeof(pint));
  254. tmp:=gotobjsec.mempos+gotoff-sizeof(pint);
  255. if (dynidx>0) then
  256. WriteDynRelocEntry(tmp,R_386_GLOB_DAT,dynidx,0)
  257. else if IsSharedLibrary then
  258. WriteDynRelocEntry(tmp,R_386_RELATIVE,0,relocval);
  259. end;
  260. end;
  261. procedure TElfExeOutput386.DoRelocationFixup(objsec:TObjSection);
  262. var
  263. i,zero:longint;
  264. objreloc: TObjRelocation;
  265. address,
  266. relocval : aint;
  267. relocsec : TObjSection;
  268. data: TDynamicArray;
  269. reltyp: byte;
  270. begin
  271. data:=objsec.data;
  272. for i:=0 to objsec.ObjRelocations.Count-1 do
  273. begin
  274. objreloc:=TObjRelocation(objsec.ObjRelocations[i]);
  275. case objreloc.typ of
  276. RELOC_NONE:
  277. continue;
  278. RELOC_ZERO:
  279. begin
  280. data.Seek(objreloc.dataoffset);
  281. zero:=0;
  282. data.Write(zero,4);
  283. continue;
  284. end;
  285. end;
  286. if (objreloc.flags and rf_raw)=0 then
  287. reltyp:=ElfTarget.encodereloc(objreloc)
  288. else
  289. reltyp:=objreloc.ftype;
  290. if relocs_use_addend then
  291. address:=objreloc.orgsize
  292. else
  293. begin
  294. data.Seek(objreloc.dataoffset);
  295. data.Read(address,4);
  296. end;
  297. if assigned(objreloc.symbol) then
  298. begin
  299. relocsec:=objreloc.symbol.objsection;
  300. relocval:=objreloc.symbol.address;
  301. end
  302. else if assigned(objreloc.objsection) then
  303. begin
  304. relocsec:=objreloc.objsection;
  305. relocval:=objreloc.objsection.mempos
  306. end
  307. else
  308. internalerror(2012060702);
  309. { Only debug sections are allowed to have relocs pointing to unused sections }
  310. if assigned(relocsec) and not (relocsec.used and assigned(relocsec.exesection)) and
  311. not (oso_debug in objsec.secoptions) then
  312. begin
  313. writeln(objsec.fullname,' references ',relocsec.fullname);
  314. internalerror(2012060703);
  315. end;
  316. { TODO: if relocsec=nil, relocations must be copied to .rel.dyn section }
  317. if (relocsec=nil) or (relocsec.used) then
  318. case reltyp of
  319. R_386_PC32:
  320. begin
  321. if (objreloc.flags and rf_dynamic)<>0 then
  322. WriteDynRelocEntry(objreloc.dataoffset+objsec.mempos,R_386_PC32,objreloc.symbol.exesymbol.dynindex,0)
  323. else
  324. address:=address+relocval-(objsec.mempos+objreloc.dataoffset);
  325. end;
  326. R_386_PLT32:
  327. begin
  328. { If target is in current object, treat as RELOC_RELATIVE }
  329. address:=address+relocval-(objsec.mempos+objreloc.dataoffset);
  330. end;
  331. R_386_32:
  332. begin
  333. if (objreloc.flags and rf_dynamic)<>0 then
  334. begin
  335. if (objreloc.symbol=nil) or
  336. (objreloc.symbol.exesymbol=nil) or
  337. (objreloc.symbol.exesymbol.dynindex=0) then
  338. begin
  339. address:=address+relocval;
  340. WriteDynRelocEntry(objreloc.dataoffset+objsec.mempos,R_386_RELATIVE,0,address);
  341. end
  342. else
  343. { Don't modify address in this case, as it serves as addend for RTLD }
  344. WriteDynRelocEntry(objreloc.dataoffset+objsec.mempos,R_386_32,objreloc.symbol.exesymbol.dynindex,0);
  345. end
  346. else
  347. address:=address+relocval;
  348. end;
  349. R_386_GOTPC:
  350. begin
  351. address:=address+gotsymbol.address-(objsec.mempos+objreloc.dataoffset);
  352. end;
  353. R_386_GOT32:
  354. begin
  355. MaybeWriteGOTEntry(reltyp,relocval,objreloc.symbol.exesymbol);
  356. relocval:=gotobjsec.mempos+objreloc.symbol.exesymbol.gotoffset-sizeof(pint)-gotsymbol.address;
  357. address:=address+relocval;
  358. end;
  359. R_386_GOTOFF:
  360. begin
  361. address:=address+relocval-gotsymbol.address;
  362. end;
  363. else
  364. begin
  365. writeln(objreloc.typ);
  366. internalerror(200604014);
  367. end;
  368. end
  369. else { not relocsec.Used }
  370. address:=0; { Relocation in debug section points to unused section, which is eliminated by linker }
  371. data.Seek(objreloc.dataoffset);
  372. data.Write(address,4);
  373. end;
  374. end;
  375. {*****************************************************************************
  376. Initialize
  377. *****************************************************************************}
  378. const
  379. as_i386_elf32_info : tasminfo =
  380. (
  381. id : as_i386_elf32;
  382. idtxt : 'ELF';
  383. asmbin : '';
  384. asmcmd : '';
  385. supported_targets : [system_i386_linux,system_i386_beos,
  386. system_i386_freebsd,system_i386_haiku,
  387. system_i386_openbsd,system_i386_netbsd,
  388. system_i386_Netware,system_i386_netwlibc,
  389. system_i386_solaris,system_i386_embedded];
  390. flags : [af_outputbinary,af_smartlink_sections,af_supports_dwarf];
  391. labelprefix : '.L';
  392. comment : '';
  393. dollarsign: '$';
  394. );
  395. initialization
  396. RegisterAssembler(as_i386_elf32_info,TElfAssembler);
  397. ElfExeOutputClass:=TElfExeOutput386;
  398. ElfTarget:=TElfTarget386;
  399. end.