cpuelf.pas 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503
  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;objsym:TObjSymbol);
  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_TLS_IE,
  180. R_386_GOT32:
  181. begin
  182. sym:=ObjReloc.symbol.exesymbol;
  183. { Although local symbols should not be accessed through GOT,
  184. this isn't strictly forbidden. In this case we need to fake up
  185. the exesym to store the GOT offset in it.
  186. TODO: name collision; maybe use a different symbol list object? }
  187. if sym=nil then
  188. begin
  189. sym:=TExeSymbol.Create(ExeSymbolList,objreloc.symbol.name+'*local*');
  190. sym.objsymbol:=objreloc.symbol;
  191. objreloc.symbol.exesymbol:=sym;
  192. end;
  193. if sym.GotOffset>0 then
  194. exit;
  195. gotobjsec.alloc(sizeof(pint));
  196. sym.GotOffset:=gotobjsec.size;
  197. { In shared library, every GOT entry needs a RELATIVE dynamic reloc,
  198. imported/exported symbols need GLOB_DAT instead. For executables,
  199. only the latter applies. }
  200. if IsSharedLibrary or (sym.dynindex>0) then
  201. dynrelocsec.alloc(dynrelocsec.shentsize);
  202. end;
  203. R_386_32:
  204. begin
  205. { TODO: How to handle absolute relocation to *weak* external symbol
  206. from executable? See test/tweaklib2, symbol test2, ld handles it
  207. differently for PIC and non-PIC code. In non-PIC code it drops
  208. dynamic relocation altogether. }
  209. if not IsSharedLibrary then
  210. exit;
  211. if (oso_executable in objsec.SecOptions) or
  212. not (oso_write in objsec.SecOptions) then
  213. hastextrelocs:=True;
  214. dynrelocsec.alloc(dynrelocsec.shentsize);
  215. objreloc.flags:=objreloc.flags or rf_dynamic;
  216. end;
  217. R_386_PC32:
  218. begin
  219. if not IsSharedLibrary then
  220. exit;
  221. { In shared library PC32 reloc to external symbol cannot be redirected
  222. to PLT entry, because PIC PLT relies on ebx register set properly. }
  223. if assigned(objreloc.symbol) and
  224. (
  225. (objreloc.symbol.objsection=nil) or
  226. (oso_plt in objreloc.symbol.objsection.SecOptions)
  227. ) then
  228. begin
  229. { Must be a dynamic symbol }
  230. if not (assigned(objreloc.symbol.exesymbol) and
  231. (objreloc.symbol.exesymbol.dynindex<>0)) then
  232. InternalError(2012101201);
  233. if (oso_executable in objsec.SecOptions) or
  234. not (oso_write in objsec.SecOptions) then
  235. hastextrelocs:=True;
  236. dynrelocsec.alloc(dynrelocsec.shentsize);
  237. objreloc.flags:=objreloc.flags or rf_dynamic;
  238. end;
  239. end;
  240. end;
  241. end;
  242. procedure TElfExeOutput386.MaybeWriteGOTEntry(reltyp:byte;relocval:aint;objsym:TObjSymbol);
  243. var
  244. gotoff,tmp:aword;
  245. begin
  246. gotoff:=objsym.exesymbol.gotoffset;
  247. if gotoff=0 then
  248. InternalError(2012060902);
  249. { the GOT slot itself, and a dynamic relocation for it }
  250. { TODO: only data symbols must get here }
  251. if gotoff=gotobjsec.Data.size+sizeof(pint) then
  252. begin
  253. gotobjsec.write(relocval,sizeof(pint));
  254. tmp:=gotobjsec.mempos+gotoff-sizeof(pint);
  255. if (objsym.exesymbol.dynindex>0) then
  256. begin
  257. if (reltyp=R_386_TLS_IE) then
  258. if IsSharedLibrary then
  259. WriteDynRelocEntry(tmp,R_386_TLS_TPOFF,objsym.exesymbol.dynindex,0)
  260. else
  261. else
  262. WriteDynRelocEntry(tmp,R_386_GLOB_DAT,objsym.exesymbol.dynindex,0)
  263. end
  264. else if IsSharedLibrary then
  265. WriteDynRelocEntry(tmp,R_386_RELATIVE,0,relocval);
  266. end;
  267. end;
  268. procedure TElfExeOutput386.DoRelocationFixup(objsec:TObjSection);
  269. var
  270. i,zero:longint;
  271. objreloc: TObjRelocation;
  272. address,
  273. relocval : aint;
  274. relocsec : TObjSection;
  275. data: TDynamicArray;
  276. reltyp: byte;
  277. PC: aword;
  278. begin
  279. data:=objsec.data;
  280. for i:=0 to objsec.ObjRelocations.Count-1 do
  281. begin
  282. objreloc:=TObjRelocation(objsec.ObjRelocations[i]);
  283. case objreloc.typ of
  284. RELOC_NONE:
  285. continue;
  286. RELOC_ZERO:
  287. begin
  288. data.Seek(objreloc.dataoffset);
  289. zero:=0;
  290. data.Write(zero,4);
  291. continue;
  292. end;
  293. end;
  294. if (objreloc.flags and rf_raw)=0 then
  295. reltyp:=ElfTarget.encodereloc(objreloc)
  296. else
  297. reltyp:=objreloc.ftype;
  298. if relocs_use_addend then
  299. address:=objreloc.orgsize
  300. else
  301. begin
  302. data.Seek(objreloc.dataoffset);
  303. data.Read(address,4);
  304. end;
  305. if assigned(objreloc.symbol) then
  306. begin
  307. relocsec:=objreloc.symbol.objsection;
  308. relocval:=objreloc.symbol.address;
  309. end
  310. else if assigned(objreloc.objsection) then
  311. begin
  312. relocsec:=objreloc.objsection;
  313. relocval:=objreloc.objsection.mempos
  314. end
  315. else
  316. internalerror(2012060702);
  317. { Only debug sections are allowed to have relocs pointing to unused sections }
  318. if assigned(relocsec) and not (relocsec.used and assigned(relocsec.exesection)) and
  319. not (oso_debug in objsec.secoptions) then
  320. begin
  321. writeln(objsec.fullname,' references ',relocsec.fullname);
  322. internalerror(2012060703);
  323. end;
  324. PC:=objsec.mempos+objreloc.dataoffset;
  325. { TODO: if relocsec=nil, relocations must be copied to .rel.dyn section }
  326. if (relocsec=nil) or (relocsec.used) then
  327. case reltyp of
  328. R_386_PC32:
  329. begin
  330. if (objreloc.flags and rf_dynamic)<>0 then
  331. WriteDynRelocEntry(PC,R_386_PC32,objreloc.symbol.exesymbol.dynindex,0)
  332. else
  333. address:=address+relocval-PC;
  334. end;
  335. R_386_PLT32:
  336. begin
  337. { If target is in current object, treat as RELOC_RELATIVE }
  338. address:=address+relocval-PC;
  339. end;
  340. R_386_32:
  341. begin
  342. if (objreloc.flags and rf_dynamic)<>0 then
  343. begin
  344. if (objreloc.symbol=nil) or
  345. (objreloc.symbol.exesymbol=nil) or
  346. (objreloc.symbol.exesymbol.dynindex=0) then
  347. begin
  348. address:=address+relocval;
  349. WriteDynRelocEntry(PC,R_386_RELATIVE,0,address);
  350. end
  351. else
  352. { Don't modify address in this case, as it serves as addend for RTLD }
  353. WriteDynRelocEntry(PC,R_386_32,objreloc.symbol.exesymbol.dynindex,0);
  354. end
  355. else
  356. address:=address+relocval;
  357. end;
  358. R_386_GOTPC:
  359. begin
  360. address:=address+gotsymbol.address-PC;
  361. end;
  362. R_386_GOT32:
  363. begin
  364. MaybeWriteGOTEntry(reltyp,relocval,objreloc.symbol);
  365. relocval:=gotobjsec.mempos+objreloc.symbol.exesymbol.gotoffset-sizeof(pint)-gotsymbol.address;
  366. address:=address+relocval;
  367. end;
  368. R_386_GOTOFF:
  369. begin
  370. address:=address+relocval-gotsymbol.address;
  371. end;
  372. R_386_TLS_IE:
  373. begin
  374. relocval:=-(tlsseg.MemPos+tlsseg.MemSize-relocval);
  375. MaybeWriteGOTEntry(reltyp,relocval,objreloc.symbol);
  376. { Resolves to *absolute* offset of GOT slot }
  377. relocval:=gotobjsec.mempos+objreloc.symbol.exesymbol.gotoffset-sizeof(pint);
  378. address:=address+relocval;
  379. end;
  380. R_386_TLS_LE_32,
  381. R_386_TLS_LE:
  382. begin
  383. if IsSharedLibrary then
  384. begin
  385. {
  386. if reltyp=R_386_TLS_LE_32 then
  387. begin
  388. WriteDynRelocEntry(PC,R_386_TLS_TPOFF32,symbol.exesymbol.dynindex,0);
  389. address:=tlsseg.MemPos-relocval;
  390. end;
  391. else
  392. begin
  393. WriteDynRelocEntry(PC,R_386_TLS_TPOFF,symbol.exesymbol.dynindex,0);
  394. address:=address-tlsseg.MemPos;
  395. end;
  396. }
  397. end
  398. else if (reltyp=R_386_TLS_LE) then
  399. address:=-(tlsseg.MemPos+tlsseg.MemSize-relocval)
  400. else
  401. address:=tlsseg.MemPos+tlsseg.MemSize-relocval;
  402. end;
  403. else
  404. begin
  405. writeln(reltyp);
  406. internalerror(200604014);
  407. end;
  408. end
  409. else { not relocsec.Used }
  410. address:=0; { Relocation in debug section points to unused section, which is eliminated by linker }
  411. data.Seek(objreloc.dataoffset);
  412. data.Write(address,4);
  413. end;
  414. end;
  415. {*****************************************************************************
  416. Initialize
  417. *****************************************************************************}
  418. const
  419. as_i386_elf32_info : tasminfo =
  420. (
  421. id : as_i386_elf32;
  422. idtxt : 'ELF';
  423. asmbin : '';
  424. asmcmd : '';
  425. supported_targets : [system_i386_linux,system_i386_beos,
  426. system_i386_freebsd,system_i386_haiku,
  427. system_i386_openbsd,system_i386_netbsd,
  428. system_i386_Netware,system_i386_netwlibc,
  429. system_i386_solaris,system_i386_embedded];
  430. flags : [af_outputbinary,af_smartlink_sections,af_supports_dwarf];
  431. labelprefix : '.L';
  432. comment : '';
  433. dollarsign: '$';
  434. );
  435. initialization
  436. RegisterAssembler(as_i386_elf32_info,TElfAssembler);
  437. ElfExeOutputClass:=TElfExeOutput386;
  438. ElfTarget:=TElfTarget386;
  439. end.