cpuelf.pas 18 KB

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