GenericLinker.Mod 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497
  1. MODULE GenericLinker; (* AUTHOR "negelef"; PURPOSE "Generic Object File Linker"; *)
  2. IMPORT ObjectFile, Streams, Diagnostics, Strings, SYSTEM;
  3. TYPE Address* = ObjectFile.Unit;
  4. CONST
  5. InvalidAddress* = MAX (Address);
  6. CONST
  7. Fixed* = 0; InitCode*=1; BodyCode* = 2; Code* = 3; Data* = 4; Const* = 5; Empty* = 6;
  8. UseAll *= {Fixed .. Empty};
  9. UseInitCode*={Fixed, InitCode};
  10. UseAllButInitCode*={Fixed, BodyCode..Empty};
  11. TYPE
  12. HashEntrySegmentedName = RECORD
  13. key: ObjectFile.SegmentedName; (* key[0]= MIN(LONGINT) <=> empty *)
  14. value: Block;
  15. END;
  16. HashSegmentedNameArray = POINTER TO ARRAY OF HashEntrySegmentedName;
  17. HashTableSegmentedName = OBJECT
  18. VAR
  19. table: HashSegmentedNameArray;
  20. size: LONGINT;
  21. used-: LONGINT;
  22. maxLoadFactor: REAL;
  23. (* Interface *)
  24. PROCEDURE & Init (initialSize: LONGINT);
  25. BEGIN
  26. ASSERT(initialSize > 2);
  27. NEW(table, initialSize);
  28. size := initialSize;
  29. used := 0;
  30. maxLoadFactor := 0.75;
  31. Clear;
  32. END Init;
  33. PROCEDURE Put(CONST key: ObjectFile.SegmentedName; value: Block);
  34. VAR hash: LONGINT;
  35. BEGIN
  36. ASSERT(used < size);
  37. hash := HashValue(key);
  38. IF table[hash].key[0] < 0 THEN
  39. INC(used, 1);
  40. END;
  41. table[hash].key := key;
  42. table[hash].value := value;
  43. IF (used / size) > maxLoadFactor THEN Grow END;
  44. END Put;
  45. PROCEDURE Get(CONST key: ObjectFile.SegmentedName):Block;
  46. BEGIN
  47. RETURN table[HashValue(key)].value;
  48. END Get;
  49. PROCEDURE Clear;
  50. VAR i: LONGINT;
  51. BEGIN FOR i := 0 TO size - 1 DO table[i].key[0] := -1; END; END Clear;
  52. (* Internals *)
  53. PROCEDURE Hash(CONST name: ObjectFile.SegmentedName): LONGINT;
  54. VAR fp,i: LONGINT;
  55. BEGIN
  56. fp := name[0]; i := 1;
  57. WHILE (i<LEN(name)) & (name[i] >= 0) DO
  58. fp:=SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, ROT(fp, 7)) / SYSTEM.VAL(SET, name[i]));
  59. INC(i);
  60. END;
  61. RETURN fp
  62. END Hash;
  63. PROCEDURE HashValue(CONST key: ObjectFile.SegmentedName):LONGINT;
  64. VAR value, h,i: LONGINT;
  65. BEGIN
  66. ASSERT(key[0] >= 0);
  67. h := Hash(key);
  68. i := 0;
  69. REPEAT
  70. value := (h + i) MOD size;
  71. INC(i);
  72. UNTIL((table[value].key[0] < 0) OR (table[value].key = key) OR (i > size));
  73. ASSERT((table[value].key[0] <0 ) OR (table[value].key = key));
  74. RETURN value;
  75. END HashValue;
  76. PROCEDURE Grow;
  77. VAR oldTable: HashSegmentedNameArray; oldSize, i: LONGINT; key: ObjectFile.SegmentedName;
  78. BEGIN
  79. oldSize := size;
  80. oldTable := table;
  81. Init(size*2);
  82. FOR i := 0 TO oldSize-1 DO
  83. key := oldTable[i].key;
  84. IF key[0] # MIN(LONGINT) THEN
  85. IF oldTable[i].value # NIL THEN
  86. Put(key, oldTable[i].value);
  87. END;
  88. END;
  89. END;
  90. END Grow;
  91. END HashTableSegmentedName;
  92. TYPE Arrangement* = OBJECT
  93. PROCEDURE Preallocate* (CONST section: ObjectFile.Section);
  94. END Preallocate;
  95. PROCEDURE Allocate* (CONST section: ObjectFile.Section): Address;
  96. END Allocate;
  97. PROCEDURE Patch* (pos, value: Address; offset, bits, unit: ObjectFile.Bits);
  98. END Patch;
  99. PROCEDURE CheckReloc*(target: Address; pattern: ObjectFile.Pattern; CONST patch: ObjectFile.Patch);
  100. BEGIN
  101. (* to be able to provide relocation information in an image*)
  102. END CheckReloc;
  103. END Arrangement;
  104. TYPE Block* = POINTER TO RECORD (ObjectFile.Section)
  105. next: Block;
  106. address*: Address;
  107. aliasOf*: Block;
  108. referenced, used: BOOLEAN;
  109. prioType: LONGINT; (* priority cache *)
  110. END;
  111. TYPE Linker* = OBJECT
  112. VAR
  113. diagnostics: Diagnostics.Diagnostics;
  114. usedCategories: SET;
  115. error-: BOOLEAN;
  116. log-: Streams.Writer;
  117. code, data: Arrangement;
  118. firstBlock, firstLinkedBlock: Block;
  119. linkRoot: ObjectFile.SectionName;
  120. hash: HashTableSegmentedName;
  121. PROCEDURE &InitLinker* (diagnostics: Diagnostics.Diagnostics; log: Streams.Writer; useCategories: SET; code, data: Arrangement);
  122. BEGIN
  123. SELF.diagnostics := diagnostics; SELF.log := log; SELF.usedCategories := useCategories;
  124. error := FALSE; SELF.code := code; SELF.data := data; firstBlock := NIL; firstLinkedBlock := NIL;
  125. linkRoot := "";
  126. NEW(hash,64);
  127. END InitLinker;
  128. PROCEDURE SetLinkRoot*(CONST root: ARRAY OF CHAR);
  129. BEGIN COPY(root, linkRoot)
  130. END SetLinkRoot;
  131. PROCEDURE Error* (CONST source, message: ARRAY OF CHAR);
  132. BEGIN diagnostics.Error (source, Diagnostics.Invalid, Diagnostics.Invalid, message); error := TRUE;
  133. END Error;
  134. PROCEDURE ErrorP*(CONST pooledName: ObjectFile.SegmentedName; CONST message: ARRAY OF CHAR);
  135. VAR source: ARRAY 256 OF CHAR;
  136. BEGIN
  137. ObjectFile.SegmentedNameToString(pooledName, source); Error(source, message);
  138. END ErrorP;
  139. PROCEDURE Information* (CONST source, message: ARRAY OF CHAR);
  140. BEGIN IF log#NIL THEN log.String(source); log.String(":"); log.String(message); log.Ln END;
  141. END Information;
  142. PROCEDURE InformationP*(CONST pooledName: ObjectFile.SegmentedName; CONST message: ARRAY OF CHAR);
  143. VAR source: ARRAY 256 OF CHAR;
  144. BEGIN
  145. ObjectFile.SegmentedNameToString(pooledName, source); Information(source, message);
  146. END InformationP;
  147. PROCEDURE FindBlock* (CONST identifier: ObjectFile.Identifier): Block;
  148. BEGIN
  149. RETURN hash.Get(identifier.name);
  150. END FindBlock;
  151. PROCEDURE ImportBlock*(CONST fixup: ObjectFile.Fixup): Block;
  152. BEGIN
  153. RETURN NIL
  154. END ImportBlock;
  155. PROCEDURE ExportBlock*(block: Block);
  156. BEGIN
  157. (* can be overwritten by implementers, for example for hashing the block *)
  158. END ExportBlock;
  159. PROCEDURE GetArrangement (block: Block): Arrangement;
  160. BEGIN IF ObjectFile.IsCode (block.type) THEN RETURN code; ELSE RETURN data; END;
  161. END GetArrangement;
  162. (* this procedure may be overwritten by implementations of the linker that need a special ordering, as, for example, the bodycode in the front or so *)
  163. PROCEDURE Precedes* (this, that: Block): BOOLEAN;
  164. VAR leftType, rightType: LONGINT;
  165. BEGIN
  166. leftType := this.prioType;
  167. rightType := that.prioType;
  168. RETURN (leftType < rightType) OR (leftType = rightType) & (this.priority < that.priority)
  169. END Precedes;
  170. PROCEDURE AddSection* (CONST section: ObjectFile.Section);
  171. VAR block, current, previous,newBlock: Block; name: ARRAY 256 OF CHAR; i: LONGINT; alias: ObjectFile.Alias;
  172. BEGIN
  173. IF FindBlock (section.identifier) # NIL THEN ObjectFile.SegmentedNameToString(section.identifier.name,name); Error (name, "duplicated section"); RETURN; END;
  174. NEW (block); ObjectFile.CopySection (section, block^); block.address := InvalidAddress; block.referenced := FALSE; block.used := FALSE;
  175. current := firstBlock; previous := NIL;
  176. block.prioType := GetPriority(block);
  177. WHILE (current # NIL) & ~Precedes(block,current) DO previous := current; current := current.next; END;
  178. IF previous # NIL THEN previous.next := block; ELSE firstBlock := block; END; block.next := current;
  179. hash.Put(block.identifier.name, block);
  180. ExportBlock(block);
  181. current := block;
  182. (* append all alias blocks after the block *)
  183. FOR i := 0 TO block.aliases-1 DO
  184. alias := block.alias[i];
  185. NEW(newBlock);
  186. newBlock.identifier := alias.identifier;
  187. newBlock.address := alias.offset;
  188. newBlock.aliasOf := block;
  189. newBlock.used := block.used;
  190. newBlock.next := current.next;
  191. current.next := newBlock;
  192. current := newBlock;
  193. hash.Put(current.identifier.name, current);
  194. ExportBlock(current);
  195. END;
  196. END AddSection;
  197. PROCEDURE Resolve*;
  198. VAR block: Block; used: BOOLEAN; name: ARRAY 256 OF CHAR;
  199. BEGIN
  200. IF ~error THEN block := firstBlock;
  201. WHILE block # firstLinkedBlock DO
  202. ObjectFile.SegmentedNameToString(block.identifier.name, name);
  203. used := (GetType (block) IN usedCategories) OR (linkRoot # "") & Strings.StartsWith(linkRoot,0,name) OR (block.aliases > 0);
  204. Reference (block, used); block := block.next;
  205. END;
  206. END;
  207. END Resolve;
  208. (*
  209. PROCEDURE Aliases*(CONST block: Block);
  210. VAR newBlock: Block; alias: ObjectFile.Alias; i: LONGINT; name: ARRAY 256 OF CHAR;
  211. BEGIN
  212. FOR i := 0 TO block.aliases-1 DO
  213. alias := block.alias[i];
  214. NEW(newBlock);
  215. newBlock.identifier := alias.identifier;
  216. newBlock.address := alias.offset;
  217. newBlock.aliasOf := block;
  218. newBlock.used := block.used;
  219. newBlock.next := firstBlock;
  220. firstBlock := newBlock;
  221. END;
  222. END Aliases;
  223. *)
  224. PROCEDURE PatchAlias*(block: Block);
  225. BEGIN
  226. IF block.aliasOf # NIL THEN INC(block.address, block.aliasOf.address) END;
  227. END PatchAlias;
  228. PROCEDURE Link*;
  229. VAR block: Block;
  230. BEGIN
  231. (*
  232. IF ~error THEN block := firstBlock; WHILE block # firstLinkedBlock DO Aliases (block); block := block.next; END; END;
  233. *)
  234. Resolve;
  235. IF ~error THEN block := firstBlock; WHILE block # firstLinkedBlock DO IF block.used & (block.aliasOf=NIL) THEN Prearrange (block); END; block := block.next; END; END;
  236. IF ~error THEN block := firstBlock; WHILE block # firstLinkedBlock DO IF block.used & (block.aliasOf=NIL) THEN Arrange (block); END; block := block.next; END; END;
  237. IF ~error THEN block := firstBlock; WHILE block # firstLinkedBlock DO PatchAlias (block); block := block.next; END; END;
  238. IF ~error THEN block := firstBlock; WHILE block # firstLinkedBlock DO IF block.used & (block.aliasOf = NIL) THEN Patch (block); END; block := block.next; END; END;
  239. IF ~error THEN firstLinkedBlock := firstBlock; END;
  240. IF ~error & (log # NIL) THEN block := firstBlock; WHILE block # NIL DO Diagnose (block); block := block.next; END; END;
  241. END Link;
  242. PROCEDURE Reference (block: Block; used: BOOLEAN);
  243. VAR i: LONGINT;
  244. PROCEDURE ReferenceFixup (CONST fixup: ObjectFile.Fixup);
  245. VAR reference: Block; str,name: ARRAY 256 OF CHAR;
  246. BEGIN
  247. reference := FindBlock (fixup.identifier);
  248. IF reference = NIL THEN reference := ImportBlock(fixup) END;
  249. IF reference = NIL THEN
  250. ObjectFile.SegmentedNameToString(fixup.identifier.name,str); Strings.Append(str," in " );
  251. ObjectFile.SegmentedNameToString(block.identifier.name,name);
  252. Strings.Append(str, name);
  253. Error(str, "unresolved");
  254. ELSIF (reference.identifier.fingerprint # 0) & (block.fixup[i].identifier.fingerprint # 0) & (reference.identifier.fingerprint # block.fixup[i].identifier.fingerprint) THEN
  255. ObjectFile.SegmentedNameToString(fixup.identifier.name,str); Strings.Append(str," in " );
  256. ObjectFile.SegmentedNameToString(block.identifier.name,name);
  257. Strings.Append(str, name);
  258. Error (str, "incompatible");
  259. ELSE Reference (reference, block.used); END;
  260. END ReferenceFixup;
  261. BEGIN
  262. IF used & ~block.used THEN block.used := TRUE;
  263. ELSIF block.referenced THEN RETURN; END; block.referenced := TRUE;
  264. IF ~used THEN RETURN END;
  265. FOR i := 0 TO block.fixups - 1 DO ReferenceFixup (block.fixup[i]); END;
  266. END Reference;
  267. PROCEDURE Prearrange (block: Block);
  268. VAR arrangement: Arrangement;
  269. BEGIN
  270. ASSERT (block.used);
  271. arrangement := GetArrangement (block);
  272. arrangement.Preallocate (block^);
  273. END Prearrange;
  274. PROCEDURE Arrange (block: Block);
  275. VAR arrangement: Arrangement;
  276. BEGIN
  277. ASSERT (block.used);
  278. arrangement := GetArrangement (block);
  279. block.address := arrangement.Allocate (block^);
  280. IF block.address = InvalidAddress THEN ErrorP (block.identifier.name, "failed to allocate"); RETURN; END;
  281. IF block.fixed THEN IF block.address # block.alignment THEN ErrorP (block.identifier.name, "address allocation problem"); RETURN END;
  282. ELSE ASSERT ((block.alignment = 0) OR (block.address MOD block.alignment = 0)); END;
  283. END Arrange;
  284. PROCEDURE Patch (block: Block);
  285. VAR arrangement: Arrangement; i: LONGINT;
  286. PROCEDURE PatchFixup (CONST fixup: ObjectFile.Fixup);
  287. VAR reference: Block; target, address: Address; i: LONGINT;
  288. PROCEDURE PatchPattern (CONST pattern: ObjectFile.FixupPattern);
  289. BEGIN arrangement.Patch (target, address, pattern.offset, pattern.bits, block.unit); address := ASH (address, -pattern.bits);
  290. END PatchPattern;
  291. PROCEDURE CheckBits(pattern: ObjectFile.Pattern; offset: LONGINT);
  292. VAR i, nobits,remainder: LONGINT; minval, maxval: ObjectFile.Unit; name: ObjectFile.SectionName; number: ARRAY 32 OF CHAR;
  293. BEGIN
  294. nobits := 0;
  295. FOR i := 0 TO pattern.patterns-1 DO
  296. INC(nobits,pattern.pattern[i].bits);
  297. END;
  298. remainder := ASH(address,-nobits);
  299. IF (nobits <32) & ((remainder > 0) OR (remainder < -1)) THEN
  300. IF pattern.mode = ObjectFile.Relative THEN (* negative values allowed *)
  301. maxval := ASH(1,nobits-1)-1; minval := -maxval-1
  302. ELSE
  303. minval := 0; maxval := ASH(1,nobits);
  304. END;
  305. ObjectFile.SegmentedNameToString(block.identifier.name,name);
  306. Strings.Append(name,":");
  307. Strings.IntToStr(offset,number);
  308. Strings.Append(name,number);
  309. Error(name,"fixup out of range");
  310. END;
  311. END CheckBits;
  312. PROCEDURE ApplyPatch(pattern: ObjectFile.Pattern; CONST patch: ObjectFile.Patch);
  313. VAR j: LONGINT;
  314. BEGIN
  315. target := block.address + patch.offset;
  316. address := reference.address + patch.displacement;
  317. IF pattern.mode = ObjectFile.Relative THEN
  318. DEC(address,target)
  319. END;
  320. address := ASH (address, pattern.scale);
  321. CheckBits(pattern, patch.offset);
  322. FOR j := 0 TO pattern.patterns-1 DO PatchPattern(pattern.pattern[j]) END;
  323. END ApplyPatch;
  324. BEGIN
  325. reference := FindBlock (fixup.identifier);
  326. IF reference = NIL THEN reference := ImportBlock(fixup) END;
  327. ASSERT (reference # NIL);
  328. FOR i := 0 TO fixup.patches-1 DO
  329. ApplyPatch(fixup.pattern, fixup.patch[i]);
  330. arrangement.CheckReloc(block.address, fixup.pattern, fixup.patch[i])
  331. END;
  332. END PatchFixup;
  333. BEGIN
  334. ASSERT (block.used);
  335. arrangement := GetArrangement (block);
  336. FOR i := 0 TO block.fixups - 1 DO
  337. PatchFixup (block.fixup[i])
  338. END;
  339. END Patch;
  340. PROCEDURE Diagnose (block: Block);
  341. VAR source, num,name: ARRAY 128 OF CHAR; msg: ARRAY 512 OF CHAR;
  342. BEGIN
  343. IF block.used THEN
  344. Strings.IntToHexStr(block.address, 8, num);
  345. source := "";
  346. Strings.Append(source,"0");
  347. Strings.Append(source, num);
  348. Strings.Append(source,"H");
  349. msg := "";
  350. ObjectFile.SegmentedNameToString(block.identifier.name, name);
  351. IF ObjectFile.IsCode(block.type) THEN msg := " code "
  352. ELSE msg := " data "
  353. END;
  354. Strings.Append(msg, name);
  355. IF block.bits # NIL THEN
  356. Strings.Append(msg, " to ");
  357. Strings.IntToHexStr(block.address+block.bits.GetSize() DIV block.unit-1, 8, num);
  358. Strings.Append(msg,"0");
  359. Strings.Append(msg, num);
  360. Strings.Append(msg,"H");
  361. (*Strings.IntToStr(block.address+block.bits.GetSize() DIV block.unit-1, num);
  362. Strings.Append(msg,num);
  363. *)
  364. END;
  365. (*
  366. Strings.IntToStr(block.address, num);
  367. Strings.Append(msg," ("); Strings.Append(msg,num); Strings.Append(msg,")");
  368. *)
  369. Information (source, msg);
  370. ELSE InformationP (block.identifier.name, "unused"); END;
  371. END Diagnose;
  372. END Linker;
  373. PROCEDURE GetType*(block: Block): LONGINT;
  374. BEGIN
  375. IF block.fixed THEN RETURN Fixed END;
  376. IF block.type = ObjectFile.InitCode THEN RETURN InitCode END;
  377. IF block.type = ObjectFile.BodyCode THEN RETURN BodyCode END;
  378. IF block.bits.GetSize () = 0 THEN RETURN Empty END;
  379. IF block.type = ObjectFile.Code THEN RETURN Code END;
  380. IF block.type = ObjectFile.Data THEN RETURN Data END;
  381. IF block.type = ObjectFile.Const THEN RETURN Const END;
  382. HALT(100); (* undefined type *)
  383. END GetType;
  384. PROCEDURE GetPriority(block: Block): LONGINT;
  385. BEGIN
  386. IF block.fixed THEN RETURN Fixed END;
  387. IF block.type = ObjectFile.InitCode THEN RETURN InitCode END;
  388. IF block.bits.GetSize () = 0 THEN RETURN Empty END;
  389. IF block.type = ObjectFile.BodyCode THEN RETURN Code END;
  390. IF block.type = ObjectFile.Code THEN RETURN Code END;
  391. IF block.type = ObjectFile.Data THEN RETURN Code END;
  392. IF block.type = ObjectFile.Const THEN RETURN Code END;
  393. HALT(100); (* undefined type *)
  394. END GetPriority;
  395. PROCEDURE Process* (reader: Streams.Reader; linker: Linker);
  396. VAR section: ObjectFile.Section; binary: BOOLEAN; poolMap: ObjectFile.PoolMap;
  397. PROCEDURE Header;
  398. VAR ch: CHAR; version: LONGINT; string: ARRAY 32 OF CHAR;
  399. BEGIN
  400. reader.String(string);
  401. binary := string="FoxOFB";
  402. IF ~binary THEN ASSERT(string="FoxOFT") END;
  403. reader.SkipWhitespace;
  404. reader.Char(ch); ASSERT(ch='v');
  405. reader.Int(version,FALSE);
  406. IF version <2 THEN linker.Error("","old object file version encountered. Recompile sources.") END;
  407. reader.Char(ch); ASSERT(ch='.');
  408. IF ~binary THEN reader.SkipWhitespace
  409. ELSE
  410. NEW(poolMap,64);
  411. poolMap.Read(reader);
  412. END;
  413. END Header;
  414. BEGIN
  415. Header;
  416. WHILE reader.Peek () # 0X DO
  417. ObjectFile.ReadSection (reader, section,binary,poolMap);
  418. reader.SkipWhitespace;
  419. IF reader.res = Streams.Ok THEN linker.AddSection (section); END;
  420. END;
  421. END Process;
  422. END GenericLinker.
  423. Compiler.Compile --objectFile=Generic --newObjectFile GenericLinker.Mod ~~~