Linker.Mod 39 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112
  1. MODULE Linker; (* AUTHOR "negelef"; PURPOSE "Object File Linker"; *)
  2. IMPORT Commands, Options, Diagnostics, Files, GenericLinker, ObjectFile, BitSets, Strings, Streams;
  3. TYPE
  4. ExportName = ARRAY 128 OF CHAR;
  5. PatchBlock*= POINTER TO RECORD
  6. baseAdr: LONGINT;
  7. addresses: LONGINT;
  8. address: ARRAY 1024 OF LONGINT;
  9. next: PatchBlock;
  10. END;
  11. RelocationInfo= OBJECT
  12. CONST blockSize=4096;
  13. VAR
  14. patchBlock: PatchBlock;
  15. PROCEDURE & Init;
  16. BEGIN
  17. patchBlock := NIL;
  18. END Init;
  19. PROCEDURE GetPatchBlock(adr: LONGINT): PatchBlock;
  20. VAR p, t: PatchBlock;
  21. BEGIN
  22. t := patchBlock;
  23. IF (patchBlock = NIL) OR (patchBlock.baseAdr > adr) THEN
  24. NEW(p); p.next := patchBlock; patchBlock := p; p.baseAdr := adr;
  25. ELSIF patchBlock.baseAdr = adr THEN p := patchBlock
  26. ELSE
  27. t := patchBlock;
  28. WHILE (t.next # NIL) & (t.next.baseAdr <= adr) DO
  29. t := t.next;
  30. END;
  31. IF t.baseAdr = adr THEN p := t
  32. ELSE
  33. NEW(p); p.next := t.next; t.next := p; p.baseAdr := adr
  34. END;
  35. END;
  36. RETURN p
  37. END GetPatchBlock;
  38. PROCEDURE AddReloc(adr: LONGINT);
  39. VAR aligned: LONGINT; p: PatchBlock;
  40. BEGIN
  41. aligned := adr - adr MOD blockSize;
  42. p := GetPatchBlock(aligned);
  43. p.address[p.addresses] := adr (*- aligned*); INC(p.addresses);
  44. END AddReloc;
  45. END RelocationInfo;
  46. ExportInfo=OBJECT
  47. VAR
  48. exports: LONGINT;
  49. name: ExportName;
  50. symbolNames: POINTER TO ARRAY OF ExportName;
  51. exportNames: POINTER TO ARRAY OF ExportName;
  52. exportAddresses: POINTER TO ARRAY OF GenericLinker.Address;
  53. PROCEDURE &Init;
  54. BEGIN
  55. exports := 0;
  56. END Init;
  57. PROCEDURE Swap(i,j: LONGINT);
  58. VAR name: ExportName; adr: GenericLinker.Address;
  59. BEGIN
  60. name := exportNames[i]; exportNames[i] := exportNames[j]; exportNames[j] := name;
  61. name := symbolNames[i]; symbolNames[i] := symbolNames[j]; symbolNames[j] := name;
  62. adr := exportAddresses[i]; exportAddresses[i] := exportAddresses[j]; exportAddresses[j] := adr;
  63. END Swap;
  64. PROCEDURE QuickSort(lo, hi: LONGINT);
  65. VAR
  66. i, j,m: LONGINT;
  67. BEGIN
  68. i := lo; j := hi; m := (lo + hi) DIV 2;
  69. WHILE i <= j DO
  70. WHILE exportNames[i] < exportNames[m] DO INC(i) END;
  71. WHILE exportNames[m] < exportNames[j] DO DEC(j) END;
  72. IF i <= j THEN
  73. IF m = i THEN m := j (* pivot will be moved by Swap *)
  74. ELSIF m = j THEN m := i (* pivot will be moved by Swap *)
  75. END;
  76. Swap(i,j);
  77. INC(i); DEC(j)
  78. END
  79. END;
  80. IF lo < j THEN QuickSort( lo, j) END;
  81. IF i < hi THEN QuickSort(i, hi) END
  82. END QuickSort;
  83. PROCEDURE Sort;
  84. BEGIN
  85. QuickSort(0, exports-1)
  86. END Sort;
  87. END ExportInfo;
  88. Arrangement* = OBJECT (GenericLinker.Arrangement);
  89. VAR
  90. displacement: GenericLinker.Address;
  91. bits: BitSets.BitSet;
  92. maxUnitSize: ObjectFile.Bits;
  93. exportInfo: ExportInfo;
  94. relocInfo: RelocationInfo;
  95. PROCEDURE & InitArrangement* (displacement: GenericLinker.Address);
  96. BEGIN
  97. SELF.displacement := displacement; maxUnitSize := 1; NEW (bits, 0); exportInfo := NIL; NEW(relocInfo);
  98. END InitArrangement;
  99. PROCEDURE Allocate* (CONST section: ObjectFile.Section): GenericLinker.Address;
  100. VAR address, alignment: ObjectFile.Bits; i, j: LONGINT; name: ObjectFile.SegmentedName;
  101. BEGIN
  102. IF section.unit > maxUnitSize THEN maxUnitSize := section.unit END;
  103. IF section.fixed THEN
  104. address := ObjectFile.Bits((section.alignment - displacement) * section.unit);
  105. ELSE
  106. address := bits.GetSize (); alignment := section.alignment * section.unit;
  107. IF alignment = 0 THEN alignment := section.unit; END;
  108. INC (address, (alignment - address MOD alignment) MOD alignment);
  109. END;
  110. IF bits.GetSize () < section.bits.GetSize () + address THEN
  111. bits.Resize (address + section.bits.GetSize ());
  112. END;
  113. (*dest pos is for some reason negative*)
  114. ASSERT(address>=0);
  115. BitSets.CopyBits (section.bits, 0, bits, address, section.bits.GetSize ());
  116. IF exportInfo # NIL THEN
  117. FOR i := 0 TO exportInfo.exports-1 DO
  118. ObjectFile.StringToSegmentedName(exportInfo.symbolNames[i], name);
  119. IF name = section.identifier.name THEN
  120. exportInfo.exportAddresses[i] := address DIV section.unit + displacement;
  121. END;
  122. FOR j := 0 TO section.aliases - 1 DO
  123. IF name = section.alias[j].identifier.name THEN
  124. exportInfo.exportAddresses[i] := address DIV section.unit + section.alias[j].offset + displacement;
  125. END;
  126. END;
  127. END
  128. END;
  129. RETURN address DIV section.unit + displacement;
  130. END Allocate;
  131. PROCEDURE SizeInBits*(): LONGINT;
  132. BEGIN RETURN bits.GetSize()
  133. END SizeInBits;
  134. PROCEDURE Patch* (pos, value: GenericLinker.Address; offset, bits, unit: ObjectFile.Bits);
  135. BEGIN SELF.bits.SetBits ((pos - displacement) * unit + offset, bits, value);
  136. END Patch;
  137. PROCEDURE CheckReloc*(target: GenericLinker.Address; pattern: ObjectFile.Pattern; CONST patch: ObjectFile.Patch);
  138. BEGIN
  139. IF (pattern.mode = ObjectFile.Absolute) & (relocInfo # NIL) THEN
  140. relocInfo.AddReloc(LONGINT(target+patch.offset));
  141. END;
  142. END CheckReloc;
  143. END Arrangement;
  144. TYPE FileFormat = PROCEDURE (linker: GenericLinker.Linker; arrangement: Arrangement; writer: Files.Writer);
  145. VAR defaults: Options.Defaults;
  146. processing, finished: GenericLinker.Block;
  147. PROCEDURE Align(this, to: LONGINT): LONGINT;
  148. BEGIN
  149. this := this + (-this) MOD to;
  150. RETURN this;
  151. END Align;
  152. PROCEDURE ReadObjectFile*(CONST moduleName, path, extension: ARRAY OF CHAR; linker: GenericLinker.Linker; moduleList: GenericLinker.HashTableSegmentedName);
  153. VAR fileName: Files.FileName; file: Files.File; reader: Files.Reader; i: LONGINT;
  154. offers, requires: ObjectFile.NameList;name: ARRAY 256 OF CHAR; sn: ObjectFile.SegmentedName;
  155. BEGIN
  156. sn := moduleName;
  157. IF (moduleList # NIL) & (moduleList.Get(sn) = finished) THEN
  158. linker.Warning(moduleName, "duplicate");
  159. RETURN;
  160. ELSIF (moduleList # NIL) & (moduleList.Get(sn) = processing) THEN
  161. linker.Error(moduleName, "cyclic import");
  162. RETURN;
  163. END;
  164. IF path # "" THEN Files.JoinPath (path, moduleName, fileName); ELSE COPY(moduleName,fileName); END;
  165. file := Files.Old (fileName);
  166. IF file = NIL THEN (* try with extension *)
  167. Files.JoinExtension (fileName, extension, fileName);
  168. file := Files.Old (fileName);
  169. END;
  170. IF file = NIL THEN (* try without extension *)
  171. END;
  172. IF file = NIL THEN linker.Error (fileName, "failed to open file"); RETURN; END;
  173. Files.OpenReader (reader, file, 0);
  174. GenericLinker.OffersRequires(reader, offers, requires);
  175. IF (moduleList # NIL) & (offers # NIL) THEN
  176. FOR i := 0 TO LEN(offers)-1 DO
  177. moduleList.Put(offers[i],processing);
  178. END;
  179. END;
  180. IF (moduleList # NIL) & (requires # NIL) THEN
  181. FOR i := 0 TO LEN(requires)-1 DO
  182. IF moduleList.Get(requires[i]) = NIL THEN
  183. name := requires[i];
  184. (*linker.Information(name, "auto");*)
  185. ReadObjectFile(name,path,extension, linker, moduleList);
  186. IF linker.error THEN RETURN END;
  187. ELSIF moduleList.Get(requires[i]) = processing THEN
  188. name := requires[i];
  189. Strings.Append(name, moduleName);
  190. linker.Error(name, "cyclic import!");
  191. RETURN;
  192. END;
  193. END;
  194. END;
  195. linker.Information (moduleName, "processing");
  196. reader.SetPos(0);
  197. GenericLinker.Process (reader, linker);
  198. IF (moduleList # NIL) & (offers # NIL) THEN
  199. FOR i := 0 TO LEN(offers)-1 DO
  200. moduleList.Put(offers[i],finished);
  201. END;
  202. END;
  203. IF reader.res # Files.Ok THEN linker.Error (fileName, "failed to parse"); END;
  204. END ReadObjectFile;
  205. PROCEDURE WriteRelocations( arr: Arrangement; w: Files.Writer );
  206. VAR pb: PatchBlock; i, relocations, binsize: LONGINT;
  207. BEGIN
  208. binsize := w.Pos();
  209. pb := arr.relocInfo.patchBlock; relocations := 0;
  210. WHILE pb # NIL DO
  211. FOR i := 0 TO pb.addresses -1 DO
  212. w.RawLInt( pb.address[i] ); INC( relocations );
  213. END;
  214. pb := pb.next;
  215. END;
  216. (* now patch header (cf Unix.Glue.Mod) *)
  217. w.SetPos( 24 );
  218. w.RawLInt( binsize );
  219. w.RawLInt( relocations );
  220. END WriteRelocations;
  221. PROCEDURE WriteOutputFile* (arrangement: Arrangement; CONST fileName: Files.FileName; linker: GenericLinker.Linker; fileFormat: FileFormat);
  222. VAR file: Files.File; writer: Files.Writer; msg: ARRAY 64 OF CHAR; number: ARRAY 32 OF CHAR;
  223. BEGIN
  224. file := Files.New (fileName);
  225. Files.OpenWriter (writer, file, 0);
  226. fileFormat (linker, arrangement, writer);
  227. writer.Update; Files.Register (file);
  228. msg := "written ";
  229. Strings.IntToStr(arrangement.SizeInBits(), number);
  230. Strings.Append(msg, number);
  231. Strings.Append(msg, " bits (= ");
  232. Strings.IntToStr(arrangement.SizeInBits() DIV arrangement.maxUnitSize, number);
  233. Strings.Append(msg, number);
  234. Strings.Append(msg, " units).");
  235. linker.Information (fileName, msg);
  236. END WriteOutputFile;
  237. PROCEDURE WriteBinaryFile (linker: GenericLinker.Linker; arrangement: Arrangement; writer: Files.Writer);
  238. VAR i: LONGINT;
  239. BEGIN
  240. FOR i := 0 TO arrangement.bits.GetSize () - 1 BY 8 DO
  241. writer.Char (CHR (arrangement.bits.GetBits (i, 8)));
  242. END;
  243. END WriteBinaryFile;
  244. PROCEDURE WriteUnixBinaryFile (linker: GenericLinker.Linker; arrangement: Arrangement; writer: Files.Writer);
  245. VAR i: LONGINT;
  246. BEGIN
  247. FOR i := 0 TO arrangement.bits.GetSize () - 1 BY 8 DO
  248. writer.Char (CHR (arrangement.bits.GetBits (i, 8)));
  249. END;
  250. WriteRelocations( arrangement, writer )
  251. END WriteUnixBinaryFile;
  252. PROCEDURE WriteTRMFile (arrangement: Arrangement; writer: Files.Writer; bitsPerLine, lines:LONGINT);
  253. VAR i,j,size,end,nonZeroInLeadingNibble: LONGINT;
  254. PROCEDURE GetBits(pos: LONGINT): WORD;
  255. BEGIN
  256. IF pos >= size THEN RETURN 0
  257. ELSIF pos+4 > size THEN RETURN arrangement.bits.GetBits(pos,size-pos)
  258. ELSE RETURN arrangement.bits.GetBits(pos,4)
  259. END;
  260. END GetBits;
  261. PROCEDURE GetFirstNibble(pos: LONGINT): WORD;
  262. BEGIN
  263. RETURN arrangement.bits.GetBits(pos,nonZeroInLeadingNibble);
  264. END GetFirstNibble;
  265. BEGIN
  266. (*allow line size not divisible by 4 by padding the line to a multiple of 4 bits with leading zeros*)
  267. size := arrangement.bits.GetSize();
  268. end := (size-1) DIV bitsPerLine + 1;
  269. (*
  270. D.String("bits per line: ");D.Int(bitsPerLine,0);
  271. D.String(" number of bits: ");D.Int(size,0);
  272. D.String(" number of words: ");D.Int(end,0);D.Ln;
  273. *)
  274. nonZeroInLeadingNibble:=bitsPerLine MOD 4;
  275. FOR i := 0 TO end-1 DO
  276. (*be aware we are writing msb first here*)
  277. (*padded first nibble*)
  278. IF nonZeroInLeadingNibble > 0 THEN
  279. writer.Char(ObjectFile.NibbleToCharacter(GetFirstNibble(i*bitsPerLine+bitsPerLine-nonZeroInLeadingNibble) ) ) ;
  280. END;
  281. (*rest of the instruction word*)
  282. FOR j := bitsPerLine DIV 4 -1 TO 0 BY -1 DO
  283. writer.Char(ObjectFile.NibbleToCharacter(GetBits(i*bitsPerLine+j*4)));
  284. END;
  285. writer.Ln;
  286. END;
  287. IF end > 0 THEN (* allow at least one block, if size = 0 *)
  288. lines := (((end-1) DIV lines)+1)*lines; (* round up to next multiple of lines *)
  289. END;
  290. FOR i := end TO lines -1 DO
  291. FOR j:=1 TO (bitsPerLine-1) DIV 4 + 1 BY 1 DO
  292. writer.Char('F');
  293. END;
  294. writer.Ln;
  295. END;
  296. END WriteTRMFile;
  297. PROCEDURE WriteTRMCodeFile* (linker: GenericLinker.Linker; arrangement: Arrangement; writer: Files.Writer);
  298. BEGIN
  299. WriteTRMFile (arrangement, writer, arrangement.maxUnitSize,1024);
  300. END WriteTRMCodeFile;
  301. PROCEDURE WriteTRMDataFile* (linker: GenericLinker.Linker; arrangement: Arrangement; writer: Files.Writer);
  302. BEGIN WriteTRMFile (arrangement, writer, 32,512);
  303. END WriteTRMDataFile;
  304. PROCEDURE WritePEFile (linker: GenericLinker.Linker; arrangement: Arrangement; writer: Files.Writer; bitmode, subSystem: INTEGER; isDLL: BOOLEAN);
  305. CONST DOSText = "This program cannot be run in DOS mode.$";
  306. CONST DOSHeaderSize = 64; DOSCodeSize = 14; DOSTextSize = 40; DOSStubSize = ((DOSHeaderSize + DOSCodeSize + DOSTextSize + 15) DIV 16) * 16;
  307. CONST FileAlignment = 200H; SectionAlignment = 1000H; HeaderSize = 24; SectionHeaderSize = 40; DirectoryEntries = 16;
  308. VAR OptionalHeaderSize, CodeSize, AlignedCodeSize, HeadersSize: LONGINT; BaseCodeAddress, BaseAddress: LONGINT; exportInfo: ExportInfo; relocInfo: RelocationInfo;
  309. pos: LONGINT;
  310. PROCEDURE Reserve (size: LONGINT);
  311. BEGIN INC(pos, size); WHILE size # 0 DO writer.Char (0X); DEC (size); END;
  312. END Reserve;
  313. PROCEDURE WriteBYTE (value: LONGINT);
  314. BEGIN writer.Char (CHR (value)); INC(pos);
  315. END WriteBYTE;
  316. PROCEDURE WriteWORD (value: LONGINT);
  317. BEGIN WriteBYTE (value MOD 100H); WriteBYTE (value DIV 100H);
  318. END WriteWORD;
  319. PROCEDURE WriteDWORD (value: LONGINT);
  320. BEGIN WriteWORD (value MOD 10000H); WriteWORD (value DIV 10000H);
  321. END WriteDWORD;
  322. PROCEDURE WritePTR (value: GenericLinker.Address);
  323. BEGIN WriteDWORD (LONGINT(value)); IF bitmode = 64 THEN WriteDWORD (0) END;
  324. END WritePTR;
  325. PROCEDURE WriteString(s: ARRAY OF CHAR);
  326. BEGIN
  327. writer.String(s); INC(pos, Strings.Length(s));
  328. END WriteString;
  329. PROCEDURE WriteString0(s: ARRAY OF CHAR);
  330. BEGIN
  331. WriteString(s); WriteBYTE(0);
  332. END WriteString0;
  333. PROCEDURE AlignTo(alignment: LONGINT);
  334. BEGIN alignment := Align(pos, alignment); Reserve(alignment - pos);
  335. END AlignTo;
  336. PROCEDURE ReserveTo(p: LONGINT);
  337. BEGIN ASSERT(p >= pos); Reserve(p-pos);
  338. END ReserveTo;
  339. PROCEDURE HasExports(): BOOLEAN;
  340. BEGIN
  341. RETURN (exportInfo # NIL) & (exportInfo.exports # 0)
  342. END HasExports;
  343. PROCEDURE ExportTableSize(): LONGINT;
  344. VAR i,offset: LONGINT;
  345. BEGIN
  346. IF ~HasExports() THEN RETURN 0 END;
  347. offset := 40 + Strings.Length(exportInfo.name)+1;
  348. FOR i := 0 TO exportInfo.exports-1 DO
  349. INC(offset, Strings.Length(exportInfo.exportNames[i])+1);
  350. INC(offset, 10);
  351. END;
  352. RETURN offset
  353. END ExportTableSize;
  354. PROCEDURE AlignedExportTableSize(): LONGINT;
  355. BEGIN
  356. RETURN Align(ExportTableSize(), SectionAlignment);
  357. END AlignedExportTableSize;
  358. PROCEDURE HasRelocs(): BOOLEAN;
  359. BEGIN
  360. RETURN (relocInfo # NIL) & (relocInfo.patchBlock # NIL)
  361. END HasRelocs;
  362. PROCEDURE RelocTableSize(): LONGINT;
  363. VAR p: PatchBlock; size: LONGINT;
  364. BEGIN
  365. IF ~HasRelocs() THEN RETURN 0 END;
  366. size := 0;
  367. p := relocInfo.patchBlock;
  368. WHILE p # NIL DO
  369. INC(size, 8 + p.addresses * 2);
  370. IF ODD(p.addresses) THEN INC(size, 2) END;
  371. p := p.next
  372. END;
  373. RETURN size
  374. END RelocTableSize;
  375. PROCEDURE AlignedRelocTableSize(): LONGINT;
  376. BEGIN RETURN Align(RelocTableSize(), SectionAlignment);
  377. END AlignedRelocTableSize;
  378. (*PROCEDURE SectionHeaderOffset(): LONGINT;
  379. BEGIN RETURN DOSStubSize + HeaderSize + OptionalHeaderSize
  380. END SectionHeaderOffset;*)
  381. PROCEDURE WriteDOSStub;
  382. BEGIN
  383. WriteWORD (5A4DH); (* e_magic *)
  384. WriteWORD (DOSStubSize); (* e_cblp *)
  385. WriteWORD (1); (* e_cp *)
  386. WriteWORD (0); (* e_crlc *)
  387. WriteWORD (DOSHeaderSize DIV 16); (* e_cparhdr *)
  388. WriteWORD (0); (* e_minalloc *)
  389. WriteWORD (0); (* e_maxalloc *)
  390. WriteWORD (0); (* e_ss *)
  391. WriteWORD (0); (* e_sp *)
  392. WriteWORD (0); (* e_csum *)
  393. WriteWORD (0); (* e_ip *)
  394. WriteWORD (0); (* e_cs *)
  395. WriteWORD (DOSHeaderSize); (* e_lfarlc *)
  396. WriteWORD (0); (* e_ovno *)
  397. Reserve (32); (* e_res *)
  398. WriteDWORD (DOSStubSize); (* e_lfanew *)
  399. WriteBYTE (00EH); WriteBYTE (01FH); WriteBYTE (0BAH); WriteWORD (DOSCodeSize);
  400. WriteBYTE (0B4H); WriteBYTE (009H); WriteBYTE (0CDH); WriteBYTE (021H); WriteBYTE (0B8H);
  401. WriteBYTE (001H); WriteBYTE (04CH); WriteBYTE (0CDH); WriteBYTE (021H); WriteString (DOSText);
  402. Reserve (DOSStubSize - DOSHeaderSize - DOSCodeSize - DOSTextSize);
  403. END WriteDOSStub;
  404. PROCEDURE WriteHeader;
  405. VAR characteristics, sections: LONGINT;
  406. BEGIN
  407. WriteDWORD (000004550H); (* Signature *)
  408. IF bitmode = 64 THEN
  409. WriteWORD (08664H); (* Machine *)
  410. ELSE
  411. WriteWORD (0014CH); (* Machine *)
  412. END;
  413. sections := 1;
  414. IF HasRelocs() THEN INC(sections) END;
  415. IF HasExports()THEN INC(sections) END;
  416. WriteWORD (sections); (* NumberOfSections *)
  417. WriteDWORD (0); (* TimeDateStamp *)
  418. WriteDWORD (0); (* PointerToSymbolTable *)
  419. WriteDWORD (0); (* NumberOfSymbols *)
  420. WriteWORD (OptionalHeaderSize); (* SizeOfOptionalHeader *)
  421. characteristics := 222H;
  422. IF ~HasRelocs() THEN characteristics := characteristics + 1 END;
  423. IF isDLL THEN characteristics := characteristics + 2000H END;
  424. IF bitmode#64 THEN characteristics := characteristics + 100H END;
  425. WriteWORD(characteristics);
  426. END WriteHeader;
  427. PROCEDURE WriteOptionalHeader;
  428. VAR ImageSize: LONGINT;
  429. BEGIN
  430. ImageSize := Align(BaseCodeAddress+AlignedCodeSize+AlignedExportTableSize()+AlignedRelocTableSize(), SectionAlignment);
  431. (* 0 *) IF bitmode = 64 THEN
  432. WriteWORD (0020BH); (* Magic *)
  433. ELSE
  434. WriteWORD (0010BH); (* Magic *)
  435. END;
  436. (* 2 *) WriteBYTE (0); (* MajorLinkerVersion *)
  437. (* 3 *) WriteBYTE (0); (* MinorLinkerVersion *)
  438. (* 4 *) WriteDWORD (CodeSize); (* SizeOfCode *)
  439. (* 8 *) WriteDWORD (0); (* SizeOfInitializedData *)
  440. (* 12 *) WriteDWORD (0); (* SizeOfUninitializedData *)
  441. (* 16 *) WriteDWORD (BaseCodeAddress); (* AddressOfEntryPoint *)
  442. (* 20 *) WriteDWORD (BaseCodeAddress); (* BaseOfCode *)
  443. (* 24 *) IF bitmode # 64 THEN
  444. WriteDWORD (ImageSize); (* BaseOfData *)
  445. END;
  446. (* 28 / 24 *) WritePTR (arrangement.displacement - BaseCodeAddress); (* ImageBase *)
  447. (* 32 *) WriteDWORD (SectionAlignment); (* SectionAlignment *)
  448. (* 36 *)WriteDWORD (FileAlignment); (* FileAlignment *)
  449. (* 40 *)WriteWORD (4); (* MajorOperatingSystemVersion *)
  450. (* 42 *)WriteWORD (0); (* MinorOperatingSystemVersion *)
  451. (* 44 *)WriteWORD (0); (* MajorImageVersion *)
  452. (* 46 *)WriteWORD (0); (* MinorImageVersion *)
  453. (* 48 *)WriteWORD (4); (* MajorSubsystemVersion *)
  454. (* 50 *)WriteWORD (0); (* MinorSubsystemVersion *)
  455. (* 52 *)WriteDWORD (0); (* Win32VersionValue *)
  456. (* 56 *)WriteDWORD (ImageSize); (* SizeOfImage *)
  457. (* 60 *)WriteDWORD (HeadersSize); (* SizeOfHeaders *)
  458. (* 64 *)WriteDWORD (0); (* CheckSum *)
  459. (* 68 *)WriteWORD (subSystem); (* Subsystem *)
  460. (* 70 *)IF isDLL THEN WriteWORD (40H); (* DllCharacteristics *)
  461. ELSE WriteWORD(0)
  462. END;
  463. (* 72 *)WritePTR (0100000H); (* SizeOfStackReserve *)
  464. (* 76 / 80 *)WritePTR (01000H); (* SizeOfStackCommit *)
  465. (* 80 / 88 *)WritePTR (0100000H); (* SizeOfHeapReserve *)
  466. (* 84 / 96 *)WritePTR (01000H); (* SizeOfHeapCommit *)
  467. (* 88 / 104 *)WriteDWORD (0); (* LoaderFlags *)
  468. (* 92 / 108 *)WriteDWORD (DirectoryEntries); (* NumberOfRvaAndSizes *)
  469. IF HasExports() THEN
  470. (* 96 / 112 *) WriteDWORD(BaseCodeAddress + AlignedCodeSize); WriteDWORD(ExportTableSize()); (* location and size of export table *)
  471. ELSE Reserve(8)
  472. END;
  473. (* 104 / 120 *) WriteDWORD (BaseCodeAddress +8) ; WriteDWORD (40); (* location and size of of idata section *)
  474. Reserve (3 * 8);
  475. IF HasRelocs() THEN
  476. WriteDWORD (BaseCodeAddress + AlignedCodeSize+AlignedExportTableSize()) ; WriteDWORD (RelocTableSize()); (* location and size of of reloc section *)
  477. ELSE Reserve(8)
  478. END;
  479. Reserve ((DirectoryEntries - 6) * 8);
  480. END WriteOptionalHeader;
  481. PROCEDURE WriteSections;
  482. VAR ExportNameTableAddress, i, offset: LONGINT; p: PatchBlock;
  483. BEGIN
  484. (* code section header *)
  485. (* 0 *) WriteString (".text"); Reserve (3); (* Name *)
  486. (* 8 *) WriteDWORD (CodeSize); (* VirtualSize *)
  487. (* 12 *) WriteDWORD (BaseCodeAddress); (* VirtualAddress *)
  488. (* 16 *) WriteDWORD (AlignedCodeSize); (* SizeOfRawData *)
  489. (* 20 *) WriteDWORD (BaseCodeAddress); (* PointerToRawData *)
  490. (* 24 *) WriteDWORD (0); (* PointerToRelocations *)
  491. (* 28 *) WriteDWORD (0); (* PointerToLinenumbers *)
  492. (* 32 *) WriteWORD (0); (* NumberOfRelocations *)
  493. (* 34 *) WriteWORD (0); (* NumberOfLinenumbers *)
  494. (* 36 *) WriteDWORD (LONGINT (0E0000020H)); (* Characteristics *)
  495. IF HasExports() THEN
  496. (* export table header *)
  497. (* 0 *) WriteString(".edata"); Reserve(2); (* name *)
  498. (* 8 *) WriteDWORD(ExportTableSize()); (* virtual size *)
  499. (* 12 *) WriteDWORD(BaseCodeAddress + AlignedCodeSize (* address *));
  500. (* 16 *) WriteDWORD(AlignedExportTableSize()); (* raw data size *)
  501. (* 20 *) WriteDWORD(BaseCodeAddress + AlignedCodeSize); (* raw data pointer *)
  502. (* 24 *) WriteDWORD (0); (* PointerToRelocations *)
  503. (* 28 *) WriteDWORD (0); (* PointerToLinenumbers *)
  504. (* 32 *) WriteWORD (0); (* NumberOfRelocations *)
  505. (* 34 *) WriteWORD (0); (* NumberOfLinenumbers *)
  506. (* 36 *) WriteDWORD (LONGINT (040000000H)); (* Characteristics *)
  507. END;
  508. IF HasRelocs() THEN
  509. (* reloc table header *)
  510. (* 0 *) WriteString(".reloc"); Reserve(2); (* name *)
  511. (* 8 *) WriteDWORD(RelocTableSize()); (* virtual size *)
  512. (* 12 *) WriteDWORD(BaseCodeAddress + AlignedCodeSize + AlignedExportTableSize() (* address *));
  513. (* 16 *) WriteDWORD(AlignedRelocTableSize()); (* raw data size *)
  514. (* 20 *) WriteDWORD(BaseCodeAddress + AlignedCodeSize+ AlignedExportTableSize()); (* raw data pointer *)
  515. (* 24 *) WriteDWORD (0); (* PointerToRelocations *)
  516. (* 28 *) WriteDWORD (0); (* PointerToLinenumbers *)
  517. (* 32 *) WriteWORD (0); (* NumberOfRelocations *)
  518. (* 34 *) WriteWORD (0); (* NumberOfLinenumbers *)
  519. (* 36 *) WriteDWORD (LONGINT (040000000H)); (* Characteristics *)
  520. END;
  521. (* 40 / 80 / 120 *) ReserveTo(BaseCodeAddress);
  522. (**** code section *****)
  523. (* BaseCodeAddress *)
  524. WriteBinaryFile (linker, arrangement, writer); INC(pos, arrangement.bits.GetSize () DIV 8);
  525. (* BaseCodeAddress +CodeSize *)
  526. Reserve (AlignedCodeSize-CodeSize);
  527. (* BaseCodeAddress + AlignedCodeSize *)
  528. IF HasExports() THEN
  529. (***** export section *****)
  530. (* BaseCodeAddress + AlignedCodeSize *)
  531. (* 0 *) WriteDWORD(0); (* reserved *)
  532. (* 4 *) WriteDWORD(0); (* time / date *)
  533. (* 6 *) WriteWORD(0); (* major version *)
  534. (* 8 *) WriteWORD(0); (* minor version *)
  535. (* 12 *) WriteDWORD(BaseCodeAddress+AlignedCodeSize + 40 + 10* exportInfo.exports); (* RVA of DLL name *)
  536. (* 16 *) WriteDWORD(1); (* start ordinal number *)
  537. (* 20 *) WriteDWORD(exportInfo.exports); (* number of entries in export table *)
  538. (* 24 *) WriteDWORD(exportInfo.exports); (* number of entries in name pointer table *)
  539. (* 28 *) WriteDWORD(BaseCodeAddress+AlignedCodeSize + 40 ); (* export address table RVA *)
  540. (* 32 *) WriteDWORD(BaseCodeAddress+AlignedCodeSize + 40 + 4* exportInfo.exports); (* name pointer RVA *)
  541. (* 36 *) WriteDWORD(BaseCodeAddress+AlignedCodeSize + 40 + 8* exportInfo.exports); (* ordinal table RVA *)
  542. (* 40 *)
  543. (* export address table *)
  544. FOR i := 0 TO exportInfo.exports-1 DO
  545. ASSERT(exportInfo.exportAddresses[i] # 0);
  546. WriteDWORD(LONGINT(exportInfo.exportAddresses[i]-BaseAddress+BaseCodeAddress)); (* RVA ! *)
  547. END;
  548. (* export name pointer table *)
  549. (* 40 + 4 * Number exports *)
  550. ExportNameTableAddress := BaseCodeAddress + AlignedCodeSize + 40 + 10* exportInfo.exports ;
  551. offset := Strings.Length(exportInfo.name)+1;
  552. FOR i := 0 TO exportInfo.exports-1 DO
  553. WriteDWORD(ExportNameTableAddress + offset);
  554. INC(offset, Strings.Length(exportInfo.exportNames[i])+1);
  555. END;
  556. (* export ordinal table *)
  557. (* 40 + 8* NumberExports *)
  558. FOR i := 0 TO exportInfo.exports-1 DO
  559. WriteWORD(i);
  560. END;
  561. (* 40 + 10* NumberExports *)
  562. (* export name table *)
  563. WriteString0(exportInfo.name);
  564. FOR i := 0 TO exportInfo.exports-1 DO
  565. WriteString0(exportInfo.exportNames[i]); (* 0x terminated *)
  566. END;
  567. Reserve(AlignedExportTableSize() - ExportTableSize());
  568. END;
  569. IF HasRelocs() THEN
  570. p := relocInfo.patchBlock;
  571. WHILE p # NIL DO
  572. WriteDWORD(p.baseAdr -BaseAddress+BaseCodeAddress) (* RVA of block *);
  573. WriteDWORD(8+p.addresses*2+2*(p.addresses MOD 2)); (* number bytes of this block *)
  574. FOR i := 0 TO p.addresses-1 DO
  575. WriteWORD(p.address[i] - p.baseAdr+ 3000H);(* block-relative addresses, highlow *)
  576. END;
  577. AlignTo(4);
  578. p := p.next;
  579. END;
  580. Reserve(AlignedRelocTableSize() - RelocTableSize());
  581. END;
  582. END WriteSections;
  583. BEGIN
  584. pos := 0;
  585. exportInfo := arrangement.exportInfo;
  586. relocInfo := arrangement.relocInfo;
  587. IF HasExports() THEN exportInfo.Sort END;
  588. BaseAddress := LONGINT(arrangement.displacement); (* ASSERT (arrangement.displacement = BaseAddress); *)
  589. OptionalHeaderSize := 96 + DirectoryEntries * 8;
  590. IF bitmode = 64 THEN INC (OptionalHeaderSize, 16); END;
  591. CodeSize := arrangement.bits.GetSize () DIV 8;
  592. AlignedCodeSize := Align(CodeSize, SectionAlignment);
  593. HeadersSize := Align(DOSStubSize + HeaderSize + OptionalHeaderSize + SectionHeaderSize, FileAlignment);
  594. BaseCodeAddress := Align(HeadersSize, SectionAlignment);
  595. WriteDOSStub; WriteHeader; WriteOptionalHeader; WriteSections;
  596. END WritePEFile;
  597. PROCEDURE WriteDLL32File (linker: GenericLinker.Linker; arrangement: Arrangement; writer: Files.Writer);
  598. BEGIN WritePEFile (linker, arrangement, writer, 32, 2, TRUE);
  599. END WriteDLL32File;
  600. PROCEDURE WriteDLL64File (linker: GenericLinker.Linker; arrangement: Arrangement; writer: Files.Writer);
  601. BEGIN WritePEFile (linker, arrangement, writer, 64, 2, TRUE);
  602. END WriteDLL64File;
  603. PROCEDURE WritePE32File (linker: GenericLinker.Linker; arrangement: Arrangement; writer: Files.Writer);
  604. BEGIN WritePEFile (linker, arrangement, writer, 32, 2, FALSE);
  605. END WritePE32File;
  606. PROCEDURE WritePE64File (linker: GenericLinker.Linker; arrangement: Arrangement; writer: Files.Writer);
  607. BEGIN WritePEFile (linker, arrangement, writer, 64, 2, FALSE);
  608. END WritePE64File;
  609. PROCEDURE WritePE32CUIFile (linker: GenericLinker.Linker; arrangement: Arrangement; writer: Files.Writer);
  610. BEGIN WritePEFile (linker, arrangement, writer, 32, 3, FALSE);
  611. END WritePE32CUIFile;
  612. PROCEDURE WritePE64CUIFile (linker: GenericLinker.Linker; arrangement: Arrangement; writer: Files.Writer);
  613. BEGIN WritePEFile (linker, arrangement, writer, 64, 3, FALSE);
  614. END WritePE64CUIFile;
  615. PROCEDURE WriteEFI32File (linker: GenericLinker.Linker; arrangement: Arrangement; writer: Files.Writer);
  616. BEGIN WritePEFile (linker, arrangement, writer, 32, 10, FALSE);
  617. END WriteEFI32File;
  618. PROCEDURE WriteEFI64File (linker: GenericLinker.Linker; arrangement: Arrangement; writer: Files.Writer);
  619. BEGIN WritePEFile (linker, arrangement, writer, 64, 10, FALSE);
  620. END WriteEFI64File;
  621. PROCEDURE WriteELFFile (linker: GenericLinker.Linker; arrangement: Arrangement; writer: Files.Writer);
  622. CONST ELFHeaderSize = 52; ProgramHeaderSize = 32; HeadersSize = ELFHeaderSize + ProgramHeaderSize;
  623. CONST BaseAddress = 08048000H; EntryAddress = BaseAddress + HeadersSize;
  624. PROCEDURE Reserve (size: LONGINT);
  625. BEGIN WHILE size # 0 DO writer.Char (0X); DEC (size); END;
  626. END Reserve;
  627. PROCEDURE WriteByte (value: LONGINT);
  628. BEGIN writer.Char (CHR (value));
  629. END WriteByte;
  630. PROCEDURE WriteHalf (value: LONGINT);
  631. BEGIN WriteByte (value MOD 100H); WriteByte (value DIV 100H);
  632. END WriteHalf;
  633. PROCEDURE WriteWord (value: LONGINT);
  634. BEGIN WriteHalf (value MOD 10000H); WriteHalf (value DIV 10000H);
  635. END WriteWord;
  636. PROCEDURE WriteELFHeader;
  637. BEGIN
  638. WriteByte (7FH); (* e_ident[EI_MAG0] *)
  639. WriteByte (ORD('E')); (* e_ident[EI_MAG1] *)
  640. WriteByte (ORD('L')); (* e_ident[EI_MAG2] *)
  641. WriteByte (ORD('F')); (* e_ident[EI_MAG3] *)
  642. WriteByte (1); (* e_ident[EI_CLASS] *)
  643. WriteByte (1); (* e_ident[EI_DATA] *)
  644. WriteByte (1); (* e_ident[EI_VERSION] *)
  645. WriteByte (0); (* e_ident[EI_PAD] *)
  646. Reserve (8); (* e_ident[EI_NIDENT] *)
  647. WriteHalf (2); (* e_type *)
  648. WriteHalf (3); (* e_machine *)
  649. WriteWord (1); (* e_version *)
  650. WriteWord (EntryAddress); (* e_entry *)
  651. WriteWord (ELFHeaderSize); (* e_phoff *)
  652. WriteWord (0); (* e_phoff *)
  653. WriteWord (0); (* e_flags *)
  654. WriteHalf (ELFHeaderSize); (* e_ehsize *)
  655. WriteHalf (ProgramHeaderSize); (* e_phentsize *)
  656. WriteHalf (1); (* e_phnum *)
  657. WriteHalf (0); (* e_shentsize *)
  658. WriteHalf (0); (* e_shnum *)
  659. WriteHalf (0); (* e_shstrndx *)
  660. END WriteELFHeader;
  661. PROCEDURE WriteProgramHeader;
  662. VAR FileSize: LONGINT;
  663. BEGIN
  664. FileSize := HeadersSize + arrangement.bits.GetSize () DIV 8;
  665. WriteWord (1); (* p_type *)
  666. WriteWord (0); (* p_offset *)
  667. WriteWord (BaseAddress); (* p_vaddr *)
  668. WriteWord (0); (* p_paddr *)
  669. WriteWord (FileSize); (* p_filesz *)
  670. WriteWord (FileSize); (* p_memsz *)
  671. WriteWord (7); (* p_flags *)
  672. WriteWord (1000H); (* p_align *)
  673. END WriteProgramHeader;
  674. BEGIN
  675. ASSERT (arrangement.displacement = BaseAddress);
  676. WriteELFHeader;
  677. WriteProgramHeader;
  678. WriteBinaryFile (linker, arrangement, writer);
  679. END WriteELFFile;
  680. PROCEDURE WriteMachOFile (linker: GenericLinker.Linker; arrangement: Arrangement; writer: Files.Writer);
  681. CONST SegmentName = "__TEXT"; SectionName = "__text";
  682. CONST MachHeaderSize = 28; LoadCommandSize = 124; ThreadCommandSize = 80;
  683. CONST CommandsSize = LoadCommandSize + ThreadCommandSize; Start = MachHeaderSize + CommandsSize;
  684. CONST BaseAddress = 000010E8H;
  685. PROCEDURE Write (value: LONGINT);
  686. BEGIN writer.Char (CHR (value)); writer.Char (CHR (value DIV 100H)); writer.Char (CHR (value DIV 10000H)); writer.Char (CHR (value DIV 1000000H));
  687. END Write;
  688. PROCEDURE WriteName (CONST name: ARRAY OF CHAR);
  689. VAR i: INTEGER;
  690. BEGIN i := 0; WHILE name[i] # 0X DO writer.Char (name[i]); INC (i); END;
  691. WHILE i # 16 DO writer.Char (0X); INC (i); END;
  692. END WriteName;
  693. PROCEDURE WriteMachHeader;
  694. BEGIN
  695. Write (LONGINT (0FEEDFACEH)); (* magic *)
  696. Write (7); (* cputype *)
  697. Write (3); (* cpusubtype *)
  698. Write (2); (* filetype *)
  699. Write (2); (* ncmds *)
  700. Write (CommandsSize); (* sizeofcmds *)
  701. Write (0); (* flags *)
  702. END WriteMachHeader;
  703. PROCEDURE WriteLoadCommand;
  704. VAR FileSize: LONGINT;
  705. BEGIN
  706. FileSize := MachHeaderSize + CommandsSize + arrangement.bits.GetSize () DIV 8;
  707. Write (1); (* cmd *)
  708. Write (LoadCommandSize); (* cmdsize *)
  709. WriteName (SegmentName); (* segname *)
  710. Write (BaseAddress - Start); (* vmaddr *)
  711. Write (FileSize); (* vmsize *)
  712. Write (0); (* fileoff *)
  713. Write (FileSize); (* filesize *)
  714. Write (7); (* maxprot *)
  715. Write (7); (* initprot *)
  716. Write (1); (* nsects *)
  717. Write (0); (* flags *)
  718. WriteName (SectionName); (* sectname *)
  719. WriteName (SegmentName); (* segname *)
  720. Write (BaseAddress); (* addr *)
  721. Write (arrangement.bits.GetSize () DIV 8); (* size *)
  722. Write (Start); (* offset *)
  723. Write (2); (* align *)
  724. Write (0); (* reloff *)
  725. Write (0); (* nreloc *)
  726. Write (0); (* flags *)
  727. Write (0); (* reserved1 *)
  728. Write (0); (* reserved2 *)
  729. END WriteLoadCommand;
  730. PROCEDURE WriteThreadCommand;
  731. BEGIN
  732. Write (5); (* cmd *)
  733. Write (ThreadCommandSize); (* cmdsize *)
  734. Write (1); (* flavor *)
  735. Write (16); (* count *)
  736. Write (0); (* eax *)
  737. Write (0); (* ebx *)
  738. Write (0); (* ecx *)
  739. Write (0); (* edx *)
  740. Write (0); (* edi *)
  741. Write (0); (* esi *)
  742. Write (0); (* ebp *)
  743. Write (0); (* esp *)
  744. Write (0); (* ss *)
  745. Write (0); (* eflags *)
  746. Write (BaseAddress); (* eip *)
  747. Write (0); (* cs *)
  748. Write (0); (* ds *)
  749. Write (0); (* es *)
  750. Write (0); (* fs *)
  751. Write (0); (* gs *)
  752. END WriteThreadCommand;
  753. BEGIN
  754. ASSERT (arrangement.displacement = BaseAddress);
  755. WriteMachHeader;
  756. WriteLoadCommand;
  757. WriteThreadCommand;
  758. WriteBinaryFile (linker, arrangement, writer);
  759. END WriteMachOFile;
  760. PROCEDURE GetFileFormat (options: Options.Options; CONST name: Options.Name; default: FileFormat): FileFormat;
  761. VAR format: ARRAY 10 OF CHAR;
  762. BEGIN
  763. IF ~options.GetString (name, format) THEN RETURN default;
  764. ELSIF format = "TRMCode" THEN RETURN WriteTRMCodeFile;
  765. ELSIF format = "TRMData" THEN RETURN WriteTRMDataFile;
  766. ELSIF format = "PE32" THEN RETURN WritePE32File;
  767. ELSIF format = "PE64" THEN RETURN WritePE64File;
  768. ELSIF format = "PE32CUI" THEN RETURN WritePE32CUIFile;
  769. ELSIF format = "PE64CUI" THEN RETURN WritePE64CUIFile;
  770. ELSIF format = "EFI32" THEN RETURN WriteEFI32File;
  771. ELSIF format = "EFI64" THEN RETURN WriteEFI64File;
  772. ELSIF format = "ELF" THEN RETURN WriteELFFile;
  773. ELSIF format = "MACHO" THEN RETURN WriteMachOFile;
  774. ELSIF format = "DLL32" THEN RETURN WriteDLL32File;
  775. ELSIF format = "DLL64" THEN RETURN WriteDLL64File;
  776. ELSIF format = "UnixRaw" THEN RETURN WriteUnixBinaryFile;
  777. ELSE RETURN default; END;
  778. END GetFileFormat;
  779. PROCEDURE ParseExports(CONST names: ARRAY OF CHAR): ExportInfo;
  780. VAR number: LONGINT; info: ExportInfo; pos: LONGINT; name: ExportName;
  781. PROCEDURE SkipWhitespace;
  782. BEGIN WHILE (names[pos] # 0X) & (names[pos] <= " ") DO INC(pos) END;
  783. END SkipWhitespace;
  784. PROCEDURE ReadName(VAR name: ARRAY OF CHAR): BOOLEAN;
  785. VAR i: LONGINT;
  786. BEGIN
  787. i := 0;
  788. WHILE (names[pos] # 0X) & (names[pos] > " ") & (names[pos] # ",") & (names[pos] # "=") DO name[i] := names[pos]; INC(i); INC(pos) END;
  789. name[i] := 0X;
  790. RETURN i > 0;
  791. END ReadName;
  792. PROCEDURE ParseEntry(VAR symbolName, exportName: ARRAY OF CHAR): BOOLEAN;
  793. BEGIN
  794. SkipWhitespace;
  795. IF ReadName(symbolName) THEN
  796. SkipWhitespace;
  797. IF names[pos] = "=" THEN
  798. INC(pos); SkipWhitespace;
  799. IF ~ReadName(exportName) THEN RETURN FALSE END;
  800. SkipWhitespace;
  801. ELSE COPY(symbolName, exportName);
  802. END;
  803. IF names[pos] = "," THEN INC(pos); SkipWhitespace END;
  804. RETURN TRUE
  805. ELSE RETURN FALSE
  806. END;
  807. END ParseEntry;
  808. BEGIN
  809. pos := 0; number := 0;
  810. WHILE ParseEntry(name, name) DO INC(number) END;
  811. IF (names[pos] # 0X) OR (number = 0) THEN RETURN NIL END;
  812. NEW(info);
  813. NEW(info.symbolNames, number);
  814. NEW(info.exportNames, number);
  815. NEW(info.exportAddresses, number);
  816. info.exports := number;
  817. number := 0; pos := 0;
  818. WHILE (names[pos] # 0X) & ParseEntry(info.symbolNames[number], info.exportNames[number]) DO
  819. INC(number)
  820. END;
  821. RETURN info
  822. END ParseExports;
  823. PROCEDURE CheckExports(info: ExportInfo; error: Streams.Writer): BOOLEAN;
  824. VAR success: BOOLEAN; i: LONGINT;
  825. BEGIN
  826. IF info = NIL THEN RETURN TRUE END;
  827. success := TRUE;
  828. FOR i := 0 TO info.exports-1 DO
  829. IF info.exportAddresses[i] = 0 THEN
  830. error.String("Export symbol not found: "); error.String(info.symbolNames[i]); error.String(" (= ");
  831. error.String(info.exportNames[i]); error.String(")"); error.Ln;
  832. success := FALSE;
  833. END;
  834. END;
  835. RETURN success
  836. END CheckExports;
  837. PROCEDURE Link* (context: Commands.Context);
  838. VAR options: Options.Options;
  839. silent, useAll, strict: BOOLEAN;
  840. codeFileFormat, dataFileFormat: FileFormat;
  841. codeDisplacement, dataDisplacement: LONGINT;
  842. path, extension, codeFileName, dataFileName, moduleName, logFileName, tempName: Files.FileName;
  843. diagnostics: Diagnostics.StreamDiagnostics; code, data: Arrangement; linker: GenericLinker.Linker;
  844. linkRoot: ARRAY 256 OF CHAR; logFile: Files.File; log: Files.Writer;
  845. use: SET;
  846. exportString: ARRAY 1024 OF CHAR;
  847. parsed: BOOLEAN;
  848. position: LONGINT;
  849. name: ARRAY 32 OF CHAR;
  850. reader: Streams.Reader;
  851. moduleList: GenericLinker.HashTableSegmentedName;
  852. segmentedName: ObjectFile.SegmentedName;
  853. BEGIN
  854. NEW (options);
  855. options.Add('p',"platform", Options.String);
  856. options.Add (0X, "silent", Options.Flag);
  857. options.Add ('a', "useAll", Options.Flag);
  858. options.Add ('s', "strict", Options.Flag);
  859. options.Add (0X, "path", Options.String); options.Add (0X, "extension", Options.String);
  860. options.Add (0X, "fileName", Options.String); options.Add (0X, "dataFileName", Options.String);
  861. options.Add (0X, "displacement", Options.Integer); options.Add (0X, "dataDisplacement", Options.Integer);
  862. options.Add (0X, "fileFormat", Options.String); options.Add (0X, "dataFileFormat", Options.String);
  863. options.Add (0X, "logFileName", Options.String);
  864. options.Add(0X,"linkRoot", Options.String);
  865. options.Add(0X,"exports", Options.String);
  866. position := context.arg.Pos();
  867. parsed := options.ParseStaged(context.arg, context.error);
  868. IF options.GetString("platform", name) THEN
  869. reader := defaults.Get(name);
  870. IF reader = NIL THEN
  871. context.error.String("Unknown platform"); context.error.Ln;
  872. context.result := Commands.CommandError;
  873. RETURN;
  874. ELSE
  875. parsed := options.ParseStaged(reader, context.error) & parsed;
  876. context.arg.SetPos(position);
  877. context.arg.SetPos(position);
  878. parsed := options.Parse(context.arg, context.error) & parsed; (* reparse overwrites *)
  879. END;
  880. END;
  881. IF ~parsed THEN context.result := Commands.CommandParseError; RETURN; END;
  882. silent := options.GetFlag ("silent");
  883. useAll := options.GetFlag ("useAll");
  884. strict := options.GetFlag ("strict");
  885. IF ~options.GetString ("path", path) THEN path := ""; END;
  886. IF ~options.GetString ("extension", extension) THEN extension := ObjectFile.DefaultExtension; END;
  887. IF ~options.GetString ("fileName", codeFileName) THEN codeFileName := "linker.bin"; END;
  888. IF ~options.GetString ("dataFileName", dataFileName) THEN dataFileName := codeFileName; END;
  889. IF ~options.GetString ("logFileName", logFileName) THEN
  890. COPY(codeFileName, logFileName); Files.SplitExtension(logFileName,logFileName,tempName); Files.JoinExtension(logFileName,"log",logFileName);
  891. END;
  892. IF ~options.GetInteger ("displacement", codeDisplacement) THEN codeDisplacement := 0; END;
  893. IF ~options.GetInteger ("dataDisplacement", codeDisplacement) THEN dataDisplacement := codeDisplacement; END;
  894. codeFileFormat := GetFileFormat (options, "fileFormat", WriteBinaryFile);
  895. dataFileFormat := GetFileFormat (options, "dataFileFormat", codeFileFormat);
  896. NEW (code, codeDisplacement);
  897. IF options.GetString("exports", exportString) THEN
  898. code.exportInfo := ParseExports(exportString);
  899. IF code.exportInfo = NIL THEN
  900. context.error.String("Syntax error in export list or empty export list."); context.error.Ln;
  901. context.result := Commands.CommandError;
  902. RETURN;
  903. ELSE
  904. COPY(codeFileName, code.exportInfo.name);
  905. END;
  906. END;
  907. IF codeFileName # dataFileName THEN NEW (data, dataDisplacement); ELSE data := code; END;
  908. NEW (diagnostics, context.error);
  909. logFile := Files.New(logFileName);
  910. IF logFile # NIL THEN NEW(log, logFile,0) ELSE log := NIL END;
  911. IF useAll THEN use := GenericLinker.UseAll ELSE use := GenericLinker.UseInitCode END;
  912. NEW (linker, diagnostics, log, use, code, data);
  913. IF options.GetString("linkRoot",linkRoot) THEN linker.SetLinkRoot(linkRoot) END;
  914. NEW(moduleList,32);
  915. segmentedName := "SYSTEM";
  916. moduleList.Put(segmentedName, finished);
  917. segmentedName := "system";
  918. moduleList.Put(segmentedName, finished);
  919. WHILE ~linker.error & context.arg.GetString (moduleName) DO
  920. ReadObjectFile (moduleName, path, extension, linker, moduleList);
  921. IF strict & ~linker.error THEN linker.Resolve END;
  922. END;
  923. (* do linking after having read in all blocks to account for potential constraints *)
  924. IF ~linker.error THEN linker.Link; END;
  925. IF ~linker.error & CheckExports(code.exportInfo, context.error) THEN
  926. IF (code.displacement # 0) & (linker.log # NIL) THEN linker.log.String("code displacement 0"); linker.log.Hex(code.displacement,-8); linker.log.String("H"); linker.log.Ln END;
  927. WriteOutputFile (code, codeFileName, linker, codeFileFormat);
  928. IF data # code THEN
  929. IF (data.displacement # 0) & (linker.log # NIL) THEN linker.log.String("data displacement 0"); linker.log.Hex(data.displacement,-8); linker.log.String("H"); linker.log.Ln END;
  930. WriteOutputFile (data, dataFileName, linker, dataFileFormat);
  931. END;
  932. IF ~silent THEN
  933. context.out.String("Link successful. Written files: ");
  934. context.out.String(codeFileName);
  935. IF data # code THEN context.out.String(", "); context.out.String(dataFileName) END;
  936. IF logFile # NIL THEN context.out.String(", "); context.out.String(logFileName); END;
  937. context.out.Ln
  938. END;
  939. IF log # NIL THEN
  940. log.Update; Files.Register(logFile);
  941. END;
  942. ELSE
  943. context.result := Commands.CommandError;
  944. END;
  945. END Link;
  946. PROCEDURE ShowDefaults*(context: Commands.Context);
  947. BEGIN
  948. defaults.Show(context.out);
  949. END ShowDefaults;
  950. BEGIN
  951. NEW(processing);
  952. NEW(finished);
  953. NEW(defaults);
  954. defaults.Add("Bios32","--fileName=kernel.img --extension=.Gof --displacement=0100000H");
  955. defaults.Add("Bios64","--fileName=kernel.img --extension=.Goff --displacement=0100000H");
  956. defaults.Add("Win32CUI","--fileFormat=PE32CUI --fileName=oberon.exe --extension=GofW --displacement=401000H");
  957. defaults.Add("Linux32","--fileFormat=Raw --fileName=oberon --extension=.GofU --displacement=08048000H");
  958. defaults.Add("Linux64","--fileFormat=Raw --fileName=oberon --extension=.GofUu --displacement=08048000H");
  959. defaults.Add("RPI","--fileName=kernel.img --extension=.Goa --displacement=8000H -sa ");
  960. defaults.Add("Solaris32", "--fileFormat=UnixRaw --fileName=oberon.bin --extension=.GofU --displacement=0" );
  961. defaults.Add("Solaris64", "--fileFormat=UnixRaw --fileName=oberon.bin --extension=.GofUu --displacement=0" );
  962. defaults.Add("Darwin32", "--fileFormat=UnixRaw --fileName=oberon.bin --extension=.GofU --displacement=0" );
  963. defaults.Add("Darwin64", "--fileFormat=UnixRaw --fileName=oberon.bin --extension=.GofUu --displacement=0" );
  964. END Linker.
  965. Linker.Link --fileName=test.exe --fileFormat=PE32 --displacement=401000H Test ~
  966. Linker.Link --fileName=a.out --fileFormat=ELF --displacement=08048000H Test ~
  967. Linker.Link --fileName=a.out --fileFormat=MACHO --displacement=000010E8H Test ~
  968. System.Free Linker ~