cpuelf.pas 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582
  1. {
  2. Copyright (c) 2012 by Sergei Gorelkin
  3. Includes ELF-related code specific to MIPS
  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. interface
  19. {$i fpcdefs.inc}
  20. implementation
  21. uses
  22. globtype,sysutils,cutils,cclasses,
  23. verbose, elfbase,
  24. systems,aasmbase,ogbase,ogelf,assemble;
  25. type
  26. TElfExeOutputMIPS=class(TElfExeOutput)
  27. private
  28. gpdispsym: TObjSymbol;
  29. gnugpsym: TObjSymbol;
  30. dt_gotsym_value: longint;
  31. dt_local_gotno_value: longint;
  32. procedure MaybeWriteGOTEntry(reltyp:byte;relocval:aint;objsym:TObjSymbol);
  33. protected
  34. procedure PrepareGOT;override;
  35. function AllocGOTSlot(objsym:TObjSymbol):boolean;override;
  36. procedure CreateGOTSection;override;
  37. procedure CreatePLT;override;
  38. procedure WriteTargetDynamicTags;override;
  39. // procedure WriteFirstPLTEntry;override;
  40. procedure WritePLTEntry(exesym:TExeSymbol);override;
  41. // procedure WriteIndirectPLTEntry(exesym:TExeSymbol);override;
  42. procedure GOTRelocPass1(objsec:TObjSection;var idx:longint);override;
  43. procedure DoRelocationFixup(objsec:TObjSection);override;
  44. public
  45. procedure DataPos_Start;override;
  46. end;
  47. const
  48. { section types }
  49. SHT_MIPS_LIBLIST = $70000000;
  50. SHT_MIPS_CONFLICT = $70000002;
  51. SHT_MIPS_GPTAB = $70000003;
  52. SHT_MIPS_UCODE = $70000004;
  53. SHT_MIPS_DEBUG = $70000005;
  54. SHT_MIPS_REGINFO = $70000006;
  55. { section flags }
  56. SHF_MIPS_GPREL = $10000000;
  57. { relocations }
  58. R_MIPS_NONE = 0;
  59. R_MIPS_16 = 1;
  60. R_MIPS_32 = 2;
  61. R_MIPS_REL32 = 3;
  62. R_MIPS_26 = 4;
  63. R_MIPS_HI16 = 5;
  64. R_MIPS_LO16 = 6;
  65. R_MIPS_GPREL16 = 7;
  66. R_MIPS_LITERAL = 8;
  67. R_MIPS_GOT16 = 9;
  68. R_MIPS_PC16 = 10;
  69. R_MIPS_CALL16 = 11;
  70. R_MIPS_GPREL32 = 12;
  71. R_MIPS_GOT_HI16 = 21;
  72. R_MIPS_GOT_LO16 = 22;
  73. R_MIPS_CALL_HI16 = 30;
  74. R_MIPS_CALL_LO16 = 31;
  75. R_MIPS_JALR = 37;
  76. { dynamic tags }
  77. DT_MIPS_RLD_VERSION = $70000001;
  78. DT_MIPS_TIME_STAMP = $70000002;
  79. DT_MIPS_ICHECKSUM = $70000003;
  80. DT_MIPS_IVERSION = $70000004;
  81. DT_MIPS_FLAGS = $70000005;
  82. DT_MIPS_BASE_ADDRESS = $70000006;
  83. DT_MIPS_CONFLICT = $70000008;
  84. DT_MIPS_LIBLIST = $70000009;
  85. DT_MIPS_LOCAL_GOTNO = $7000000A;
  86. DT_MIPS_CONFLICTNO = $7000000B;
  87. DT_MIPS_LIBLISTNO = $70000010;
  88. DT_MIPS_SYMTABNO = $70000011;
  89. DT_MIPS_UNREFEXTNO = $70000012;
  90. DT_MIPS_GOTSYM = $70000013;
  91. DT_MIPS_HIPAGENO = $70000014;
  92. DT_MIPS_RLD_MAP = $70000016;
  93. { values of DT_MIPS_FLAGS }
  94. RHF_QUICKSTART = 1;
  95. RHF_NOTPOT = 2;
  96. type
  97. TElfReginfo=record
  98. ri_gprmask: longword;
  99. ri_cprmask: array[0..3] of longword;
  100. ri_gp_value: longint; // signed
  101. end;
  102. procedure MaybeSwapElfReginfo(var h:TElfReginfo);
  103. var
  104. i: longint;
  105. begin
  106. if source_info.endian<>target_info.endian then
  107. begin
  108. h.ri_gprmask:=swapendian(h.ri_gprmask);
  109. for i:=0 to 3 do
  110. h.ri_cprmask[i]:=swapendian(h.ri_cprmask[i]);
  111. h.ri_gp_value:=swapendian(h.ri_gp_value);
  112. end;
  113. end;
  114. {****************************************************************************
  115. ELF Target methods
  116. ****************************************************************************}
  117. function elf_mips_encodereloc(objrel:TObjRelocation):byte;
  118. begin
  119. case objrel.typ of
  120. RELOC_NONE:
  121. result:=R_MIPS_NONE;
  122. RELOC_ABSOLUTE:
  123. result:=R_MIPS_32;
  124. else
  125. result:=0;
  126. InternalError(2012110602);
  127. end;
  128. end;
  129. function elf_mips_relocname(reltyp:byte):string;
  130. begin
  131. result:='TODO';
  132. end;
  133. procedure elf_mips_loadreloc(objrel:TObjRelocation);
  134. begin
  135. end;
  136. function elf_mips_loadsection(objinput:TElfObjInput;objdata:TObjData;const shdr:TElfsechdr;shindex:longint):boolean;
  137. begin
  138. case shdr.sh_type of
  139. SHT_MIPS_REGINFO:
  140. result:=true;
  141. else
  142. writeln('elf_mips_loadsection: ',hexstr(shdr.sh_type,8),' ',objdata.name);
  143. result:=false;
  144. end;
  145. end;
  146. {*****************************************************************************
  147. TElfExeOutputMIPS
  148. *****************************************************************************}
  149. procedure TElfExeOutputMIPS.CreateGOTSection;
  150. var
  151. tmp: longword;
  152. begin
  153. gotobjsec:=TElfObjSection.create_ext(internalObjData,'.got',
  154. SHT_PROGBITS,SHF_ALLOC or SHF_WRITE or SHF_MIPS_GPREL,sizeof(pint),sizeof(pint));
  155. gotobjsec.SecOptions:=[oso_keep];
  156. { gotpltobjsec is what's pointed to by DT_PLTGOT }
  157. { TODO: this is not correct; under some circumstances ld can generate PLTs for MIPS,
  158. using classic model. We'll need to support it, too. }
  159. gotpltobjsec:=TElfObjSection(gotobjsec);
  160. internalObjData.SetSection(gotobjsec);
  161. { TODO: must be an absolute symbol; binutils use linker script to define it }
  162. gotsymbol:=internalObjData.SymbolDefine('_gp',AB_GLOBAL,AT_NONE);
  163. gotsymbol.offset:=$7ff0;
  164. { also define _gp_disp and __gnu_local_gp }
  165. gpdispsym:=internalObjData.SymbolDefine('_gp_disp',AB_GLOBAL,AT_NONE);
  166. gnugpsym:=internalObjData.SymbolDefine('__gnu_local_gp',AB_GLOBAL,AT_NONE);
  167. { reserved entries }
  168. gotobjsec.WriteZeros(sizeof(pint));
  169. tmp:=$80000000;
  170. if target_info.endian<>source_info.endian then
  171. tmp:=swapendian(tmp);
  172. gotobjsec.Write(tmp,sizeof(pint));
  173. end;
  174. procedure TElfExeOutputMIPS.CreatePLT;
  175. begin
  176. pltobjsec:=TElfObjSection.create_ext(internalObjData,'.plt',
  177. SHT_PROGBITS,SHF_ALLOC or SHF_EXECINSTR,4,16);
  178. pltobjsec.SecOptions:=[oso_keep];
  179. end;
  180. procedure TElfExeOutputMIPS.WriteTargetDynamicTags;
  181. begin
  182. writeDynTag(DT_MIPS_RLD_VERSION,1);
  183. if not IsSharedLibrary then
  184. {writeDynTag(DT_MIPS_RLD_MAP,rldmapsec)};
  185. writeDynTag(DT_MIPS_FLAGS,RHF_NOTPOT);
  186. if IsSharedLibrary then
  187. writeDynTag(DT_MIPS_BASE_ADDRESS,0)
  188. else
  189. writeDynTag(DT_MIPS_BASE_ADDRESS,ElfTarget.exe_image_base);
  190. writeDynTag(DT_MIPS_LOCAL_GOTNO,dt_local_gotno_value);
  191. writeDynTag(DT_MIPS_SYMTABNO,dynsymlist.count+1);
  192. { ABI says: "Index of first external dynamic symbol not referenced locally" }
  193. { What the hell is this? BFD writes number of output sections(!!),
  194. the values found in actual files do not match even that,
  195. and don't seem to be connected to reality at all... }
  196. //writeDynTag(DT_MIPS_UNREFEXTNO,0);
  197. {Index of first dynamic symbol in GOT }
  198. writeDynTag(DT_MIPS_GOTSYM,dt_gotsym_value+1);
  199. end;
  200. procedure TElfExeOutputMIPS.WritePLTEntry(exesym: TExeSymbol);
  201. begin
  202. end;
  203. function TElfExeOutputMIPS.AllocGOTSlot(objsym:TObjSymbol):boolean;
  204. var
  205. exesym: TExeSymbol;
  206. begin
  207. { MIPS has quite a different way of allocating GOT slots and dynamic relocations }
  208. result:=false;
  209. exesym:=objsym.exesymbol;
  210. { Although local symbols should not be accessed through GOT,
  211. this isn't strictly forbidden. In this case we need to fake up
  212. the exesym to store the GOT offset in it.
  213. TODO: name collision; maybe use a different symbol list object? }
  214. if exesym=nil then
  215. begin
  216. exesym:=TExeSymbol.Create(ExeSymbolList,objsym.name+'*local*');
  217. exesym.objsymbol:=objsym;
  218. objsym.exesymbol:=exesym;
  219. end;
  220. if exesym.GotOffset>0 then
  221. exit;
  222. make_dynamic_if_undefweak(exesym);
  223. if (exesym.dynindex>0) and (exesym.ObjSymbol.ObjSection=nil) then
  224. begin
  225. { External symbols must be located at the end of GOT, here just
  226. mark them for dealing later. }
  227. exesym.GotOffset:=high(aword);
  228. exit;
  229. end;
  230. gotobjsec.alloc(sizeof(pint));
  231. exesym.GotOffset:=gotobjsec.size;
  232. result:=true;
  233. end;
  234. function put_externals_last(p1,p2:pointer):longint;
  235. var
  236. sym1: TExeSymbol absolute p1;
  237. sym2: TExeSymbol absolute p2;
  238. begin
  239. result:=ord(sym1.gotoffset=high(aword))-ord(sym2.gotoffset=high(aword));
  240. end;
  241. procedure TElfExeOutputMIPS.PrepareGOT;
  242. var
  243. i: longint;
  244. exesym: TExeSymbol;
  245. begin
  246. inherited PrepareGOT;
  247. if not dynamiclink then
  248. exit;
  249. dynsymlist.sort(@put_externals_last);
  250. { reindex, as sorting could changed the order }
  251. for i:=0 to dynsymlist.count-1 do
  252. TExeSymbol(dynsymlist[i]).dynindex:=i+1;
  253. { find the symbol to be written as DT_GOTSYM }
  254. for i:=dynsymlist.count-1 downto 0 do
  255. begin
  256. exesym:=TExeSymbol(dynsymlist[i]);
  257. if exesym.gotoffset<>high(aword) then
  258. begin
  259. dt_gotsym_value:=i+1;
  260. break;
  261. end;
  262. end;
  263. { !! maybe incorrect, where do 'unmapped globals' belong? }
  264. dt_local_gotno_value:=gotobjsec.size div sizeof(pint);
  265. { actually allocate GOT slots for imported symbols }
  266. for i:=dt_gotsym_value to dynsymlist.count-1 do
  267. begin
  268. exesym:=TExeSymbol(dynsymlist[i]);
  269. gotobjsec.alloc(sizeof(pint));
  270. exesym.GotOffset:=gotobjsec.size;
  271. end;
  272. gotsize:=gotobjsec.size;
  273. end;
  274. procedure TElfExeOutputMIPS.DataPos_Start;
  275. begin
  276. { Since we omit GOT slots for imported symbols during inherited PrepareGOT, they don't
  277. get written in ResolveRelocations either. This must be compensated here.
  278. Or better override ResolveRelocations and handle there. }
  279. { TODO: shouldn't be zeroes, but address of stubs if address taken, etc. }
  280. gotobjsec.writeZeros(gotsize-gotobjsec.size);
  281. inherited DataPos_Start;
  282. end;
  283. procedure TElfExeOutputMIPS.MaybeWriteGOTEntry(reltyp:byte;relocval:aint;objsym:TObjSymbol);
  284. var
  285. gotoff:aword;
  286. begin
  287. gotoff:=objsym.exesymbol.gotoffset;
  288. if gotoff=0 then
  289. InternalError(2012060902);
  290. { the GOT slot itself, and a dynamic relocation for it }
  291. if gotoff=gotobjsec.Data.size+sizeof(pint) then
  292. begin
  293. if source_info.endian<>target_info.endian then
  294. relocval:=swapendian(relocval);
  295. gotobjsec.write(relocval,sizeof(pint));
  296. end;
  297. end;
  298. procedure TElfExeOutputMIPS.GOTRelocPass1(objsec:TObjSection;var idx:longint);
  299. var
  300. objreloc:TObjRelocation;
  301. reltyp:byte;
  302. begin
  303. objreloc:=TObjRelocation(objsec.ObjRelocations[idx]);
  304. if (ObjReloc.flags and rf_raw)=0 then
  305. reltyp:=ElfTarget.encodereloc(ObjReloc)
  306. else
  307. reltyp:=ObjReloc.ftype;
  308. case reltyp of
  309. R_MIPS_CALL16,
  310. R_MIPS_GOT16:
  311. begin
  312. //TODO: GOT16 against local symbols need specialized handling
  313. AllocGOTSlot(objreloc.symbol);
  314. end;
  315. end;
  316. end;
  317. type
  318. PRelocData=^TRelocData;
  319. TRelocData=record
  320. next:PRelocData;
  321. objsec:TObjSection;
  322. objrel:TObjRelocation;
  323. addend:aint;
  324. end;
  325. procedure TElfExeOutputMIPS.DoRelocationFixup(objsec:TObjSection);
  326. var
  327. i,zero:longint;
  328. objreloc: TObjRelocation;
  329. AHL_S,
  330. tmp,
  331. address,
  332. relocval : aint;
  333. relocsec : TObjSection;
  334. data: TDynamicArray;
  335. reltyp: byte;
  336. curloc: aword;
  337. reloclist,hr: PRelocData;
  338. is_gp_disp: boolean;
  339. begin
  340. data:=objsec.data;
  341. reloclist:=nil;
  342. for i:=0 to objsec.ObjRelocations.Count-1 do
  343. begin
  344. objreloc:=TObjRelocation(objsec.ObjRelocations[i]);
  345. case objreloc.typ of
  346. RELOC_NONE:
  347. continue;
  348. RELOC_ZERO:
  349. begin
  350. data.Seek(objreloc.dataoffset);
  351. zero:=0;
  352. data.Write(zero,4);
  353. continue;
  354. end;
  355. end;
  356. if (objreloc.flags and rf_raw)=0 then
  357. reltyp:=ElfTarget.encodereloc(objreloc)
  358. else
  359. reltyp:=objreloc.ftype;
  360. if ElfTarget.relocs_use_addend then
  361. address:=objreloc.orgsize
  362. else
  363. begin
  364. data.Seek(objreloc.dataoffset);
  365. data.Read(address,4);
  366. if source_info.endian<>target_info.endian then
  367. address:=swapendian(address);
  368. end;
  369. if assigned(objreloc.symbol) then
  370. begin
  371. relocsec:=objreloc.symbol.objsection;
  372. relocval:=objreloc.symbol.address;
  373. end
  374. else if assigned(objreloc.objsection) then
  375. begin
  376. relocsec:=objreloc.objsection;
  377. relocval:=objreloc.objsection.mempos
  378. end
  379. else
  380. internalerror(2012060702);
  381. { Only debug sections are allowed to have relocs pointing to unused sections }
  382. if assigned(relocsec) and not (relocsec.used and assigned(relocsec.exesection)) and
  383. not (oso_debug in objsec.secoptions) then
  384. begin
  385. writeln(objsec.fullname,' references ',relocsec.fullname);
  386. internalerror(2012060703);
  387. end;
  388. curloc:=objsec.mempos+objreloc.dataoffset;
  389. if (relocsec=nil) or (relocsec.used) then
  390. case reltyp of
  391. R_MIPS_32:
  392. begin
  393. if (objreloc.flags and rf_dynamic)<>0 then
  394. begin
  395. if (objreloc.symbol=nil) or
  396. (objreloc.symbol.exesymbol=nil) or
  397. (objreloc.symbol.exesymbol.dynindex=0) then
  398. begin
  399. end
  400. else
  401. ;
  402. end
  403. else
  404. address:=address+relocval;
  405. end;
  406. R_MIPS_26:
  407. begin
  408. tmp:=(address and $03FFFFFF) shl 2;
  409. tmp:=((tmp or (curloc and $F0000000))+relocval) shr 2;
  410. address:=(address and $FC000000) or (tmp and $3FFFFFF);
  411. end;
  412. R_MIPS_HI16:
  413. begin
  414. { This relocation can be handled only after seeing a matching LO16 one,
  415. moreover BFD supports any number of HI16 to precede a single LO16.
  416. So just add it to a queue. }
  417. new(hr);
  418. hr^.next:=reloclist;
  419. hr^.objrel:=objreloc;
  420. hr^.objsec:=objsec;
  421. hr^.addend:=address; //TODO: maybe it can be saved in objrel.orgsize field
  422. reloclist:=hr;
  423. end;
  424. R_MIPS_LO16:
  425. begin
  426. while assigned(reloclist) do
  427. begin
  428. hr:=reloclist;
  429. reloclist:=hr^.next;
  430. // if relocval<>hr^.relocval then // must be the same symbol
  431. // InternalError();
  432. { _gp_disp and __gnu_local_gp magic }
  433. if assigned(hr^.objrel.symbol) and
  434. assigned(hr^.objrel.symbol.exesymbol) then
  435. begin
  436. is_gp_disp:=(hr^.objrel.symbol.exesymbol.objsymbol=gpdispsym);
  437. if (hr^.objrel.symbol.exesymbol.objsymbol=gnugpsym) then
  438. relocval:=gotsymbol.address;
  439. end;
  440. if is_gp_disp then
  441. relocval:=gotsymbol.address-curloc;
  442. AHL_S:=(hr^.addend shl 16)+SmallInt(address)+relocval;
  443. { formula: ((AHL + S) – (short)(AHL + S)) >> 16 }
  444. tmp:=(hr^.addend and $FFFF0000) or ((AHL_S-SmallInt(AHL_S)) shr 16);
  445. data.seek(hr^.objrel.dataoffset);
  446. if source_info.endian<>target_info.endian then
  447. tmp:=swapendian(tmp);
  448. data.Write(tmp,4);
  449. dispose(hr);
  450. end;
  451. if is_gp_disp then
  452. Inc(AHL_S,4);
  453. address:=(address and $FFFF0000) or (AHL_S and $FFFF);
  454. end;
  455. R_MIPS_CALL16,
  456. R_MIPS_GOT16:
  457. begin
  458. //TODO: GOT16 relocations against local symbols need specialized handling
  459. MaybeWriteGOTEntry(reltyp,relocval,objreloc.symbol);
  460. // !! this is correct only while _gp symbol is defined relative to .got !!
  461. relocval:=-(gotsymbol.offset-(objreloc.symbol.exesymbol.gotoffset-sizeof(pint)));
  462. // TODO: check overflow
  463. address:=(address and $FFFF0000) or (relocval and $FFFF);
  464. end;
  465. R_MIPS_PC16:
  466. //TODO: check overflow
  467. address:=(address and $FFFF0000) or ((((SmallInt(address) shl 2)+relocval-curloc) shr 2) and $FFFF);
  468. R_MIPS_JALR: {optimization hint, ignore for now }
  469. ;
  470. else
  471. begin
  472. writeln(objsec.fullname,'+',objreloc.dataoffset,' ',objreloc.ftype);
  473. internalerror(200604014);
  474. end;
  475. end
  476. else { not relocsec.Used }
  477. address:=0; { Relocation in debug section points to unused section, which is eliminated by linker }
  478. data.Seek(objreloc.dataoffset);
  479. if source_info.endian<>target_info.endian then
  480. address:=swapendian(address);
  481. data.Write(address,4);
  482. end;
  483. end;
  484. {*****************************************************************************
  485. Initialize
  486. *****************************************************************************}
  487. const
  488. elf_target_mips: TElfTarget =
  489. (
  490. max_page_size: $10000;
  491. exe_image_base: $400000;
  492. machine_code: EM_MIPS;
  493. relocs_use_addend: false;
  494. dyn_reloc_codes: (
  495. 0,
  496. 0,
  497. 0,
  498. 0,
  499. 0
  500. );
  501. relocname: @elf_mips_relocName;
  502. encodereloc: @elf_mips_encodeReloc;
  503. loadreloc: @elf_mips_loadReloc;
  504. loadsection: @elf_mips_loadSection;
  505. );
  506. initialization
  507. ElfTarget:=elf_target_mips;
  508. ElfExeOutputClass:=TElfExeOutputMIPS;
  509. end.