Modules.Mod 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761
  1. MODULE Modules; (** AUTHOR "pjm"; PURPOSE "Modules and types"; *)
  2. IMPORT SYSTEM, Trace, Machine, Heaps;
  3. CONST
  4. Ok* = 0;
  5. AddressSize = SIZEOF (ADDRESS); (* architecture dependent size of addresses in bytes *)
  6. MaxTags* = 16; (* in type descriptor *)
  7. (** type descriptor field offsets relative to root (middle) *)
  8. Tag0Ofs* = -AddressSize * 2; (** first tag *)
  9. Mth0Ofs* = Tag0Ofs - AddressSize*MaxTags; (** first method *)
  10. Ptr0Ofs* = AddressSize; (** first pointer offset *)
  11. MaxObjFormats = 5; (* maximum number of object file formats installed *)
  12. (** flags in TypeDesc, RoundUp(log2(MaxTags)) low bits reserved for extLevel *)
  13. ProtTypeBit* = Heaps.ProtTypeBit;
  14. None* = 0; PowerDown* = 1; Reboot* = 2;
  15. ClearCode = TRUE;
  16. InitTableLen = 1024;
  17. InitPtrTableLen = 2048;
  18. DefaultContext* = "A2";
  19. TYPE
  20. (* definitions for object-model loader support *)
  21. Name* = ARRAY 32 OF CHAR;
  22. Command* = RECORD
  23. (* Fields exported for initialization by loader/linker only! Consider read-only! *)
  24. name*: Name; (* name of the procedure *)
  25. argTdAdr*, retTdAdr* : ADDRESS; (* address of type descriptors of argument and return type, 0 if no type *)
  26. entryAdr* : ADDRESS; (* entry address of procedure *)
  27. END;
  28. ExportDesc* = RECORD
  29. fp*: ADDRESS;
  30. adr*: ADDRESS;
  31. exports*: LONGINT;
  32. dsc*: ExportArray
  33. END;
  34. ExportArray* = POINTER TO ARRAY OF ExportDesc;
  35. Bytes* = POINTER TO ARRAY OF CHAR;
  36. TerminationHandler* = PROCEDURE;
  37. TypeDesc* = POINTER TO RECORD (* ug: adapt constant TypeDescRecSize if this type is changed !!! *)
  38. descSize: LONGINT;
  39. sentinel: LONGINT; (* = MPO-4 *)
  40. tag*: ADDRESS; (* pointer to static type descriptor, only used by linker and loader *)
  41. flags*: SET;
  42. mod*: Module; (* hint only, because module may have been freed (at Heaps.ModOfs) *)
  43. name*: Name;
  44. END;
  45. ExceptionTableEntry* = RECORD
  46. pcFrom*: ADDRESS;
  47. pcTo*: ADDRESS;
  48. pcHandler*: ADDRESS;
  49. END;
  50. ExceptionTable* = POINTER TO ARRAY OF ExceptionTableEntry;
  51. ProcTableEntry* = RECORD
  52. pcFrom*, pcLimit*, pcStatementBegin*, pcStatementEnd*: ADDRESS;
  53. noPtr*: LONGINT;
  54. END;
  55. ProcTable* = POINTER TO ARRAY OF ProcTableEntry;
  56. PtrTable* = POINTER TO ARRAY OF ADDRESS;
  57. ProcOffsetEntry* = RECORD
  58. data*: ProcTableEntry; (* code offsets of procedures *)
  59. startIndex*: LONGINT; (* index into global ptrOffsets table *)
  60. END;
  61. ProcOffsetTable* = POINTER TO ARRAY OF ProcOffsetEntry;
  62. (* only for interface compatibility with generic loader based system
  63. -- does not provide functionality. Used for precise GC.
  64. *)
  65. ProcedureDescPointer* = POINTER TO ProcedureDesc;
  66. ProcedureDesc*= RECORD
  67. pcFrom-, pcLimit-: ADDRESS;
  68. offsets- {UNTRACED}: POINTER TO ARRAY OF ADDRESS;
  69. END;
  70. Module* = OBJECT (Heaps.RootObject) (* cf. Linker0 & Heaps.WriteType *)
  71. VAR
  72. next*: Module; (** once a module is published, all fields are read-only *)
  73. name*: Name;
  74. init, published: BOOLEAN;
  75. refcnt*: LONGINT; (* counts loaded modules that import this module *)
  76. sb*: ADDRESS; (* reference address between constants and local variables *)
  77. entry*: POINTER TO ARRAY OF ADDRESS;
  78. command*: POINTER TO ARRAY OF Command;
  79. ptrAdr*: POINTER TO ARRAY OF ADDRESS;
  80. typeInfo*: POINTER TO ARRAY OF TypeDesc; (* traced explicitly in FindRoots *)
  81. module*: POINTER TO ARRAY OF Module; (* imported modules: for reference counting *)
  82. procTable*: ProcTable; (* information inserted by loader, removed after use in Publish *)
  83. ptrTable*: PtrTable; (* information inserted by loader, removed after use in Publish *)
  84. data*, code*, staticTypeDescs* (* ug *), refs*: Bytes;
  85. export*: ExportDesc;
  86. term*: TerminationHandler;
  87. exTable*: ExceptionTable;
  88. noProcs*: LONGINT; (* used for removing proc offsets when unloading module *)
  89. firstProc*: ADDRESS; (* procedure with lowest PC in module, also used for unloading *)
  90. maxPtrs*: LONGINT;
  91. crc*: LONGINT; (* crc of the object file -- for unique identification *)
  92. PROCEDURE FindRoots; (* override *)
  93. VAR i: LONGINT; ptr: ANY; moduleName: Name; protRecBlockAdr: ADDRESS; protRecBlock: Heaps.ProtRecBlock;
  94. BEGIN
  95. IF published THEN (* mark global pointers *)
  96. moduleName := name;
  97. FOR i := 0 TO LEN(ptrAdr) - 1 DO
  98. SYSTEM.GET (ptrAdr[i], ptr);
  99. IF ptr # NIL THEN Heaps.Mark(ptr) END
  100. END;
  101. (* mark prot rec fields, for whatever reasons this does not work correctly in the statically linked heap *)
  102. SYSTEM.GET(SYSTEM.VAL(ADDRESS,SELF)+Heaps.HeapBlockOffset, protRecBlockAdr);
  103. protRecBlock := SYSTEM.VAL(Heaps.ProtRecBlock, protRecBlockAdr);
  104. Heaps.Mark(protRecBlock.awaitingLock.head);
  105. Heaps.Mark(protRecBlock.awaitingCond.head);
  106. Heaps.Mark(protRecBlock.lockedBy);
  107. Heaps.Mark(protRecBlock.lock);
  108. Heaps.AddRootObject(next);
  109. (* all other fields are being traversed by Mark of the Garbage Collector *)
  110. END;
  111. END FindRoots;
  112. END Module;
  113. LoaderProc* = PROCEDURE (CONST name, fileName: ARRAY OF CHAR; VAR res: LONGINT;
  114. VAR msg: ARRAY OF CHAR): Module; (** load an object file *)
  115. VAR
  116. extension-: ARRAY MaxObjFormats, 8 OF CHAR;
  117. loader: ARRAY MaxObjFormats OF LoaderProc;
  118. numLoaders: LONGINT;
  119. kernelProc*: ARRAY 11 OF ADDRESS; (** kernel call addresses for loader *)
  120. freeRoot*: Module; (** list of freed modules (temporary) *)
  121. (* the following two variables are initialized by Linker *)
  122. root-: Module; (** list of modules (read-only) *)
  123. initBlock: ANY; (* placeholder - anchor for module init code (initialized by linker) *)
  124. procOffsets-: ProcOffsetTable; (* global table containing procedure code offsets and pointer offsets, sorted in ascending order of procedure code offsets *)
  125. numProcs: LONGINT; (* number of entries in procOffsets *)
  126. ptrOffsets-: PtrTable;
  127. numPtrs: LONGINT;
  128. shutdown*: LONGINT; (** None, Reboot, PowerDown *)
  129. trace: BOOLEAN;
  130. ptrOffsetsLock: BOOLEAN;
  131. (** Register a module loader. *)
  132. PROCEDURE AddLoader*(CONST ext: ARRAY OF CHAR; proc: LoaderProc);
  133. BEGIN
  134. Machine.Acquire(Machine.Modules);
  135. ASSERT(numLoaders < MaxObjFormats);
  136. loader[numLoaders] := proc;
  137. COPY(ext, extension[numLoaders]);
  138. ASSERT(ext = extension[numLoaders]); (* no overflow *)
  139. INC(numLoaders);
  140. Machine.Release(Machine.Modules)
  141. END AddLoader;
  142. (** Remove a module loader. *)
  143. PROCEDURE RemoveLoader*(CONST ext: ARRAY OF CHAR; proc: LoaderProc);
  144. VAR i, j: LONGINT;
  145. BEGIN
  146. Machine.Acquire(Machine.Modules);
  147. i := 0;
  148. WHILE (i # numLoaders) & ((loader[i] # proc) OR (extension[i] # ext)) DO INC(i) END;
  149. IF i # numLoaders THEN
  150. FOR j := i TO numLoaders - 2 DO
  151. loader[j] := loader[j + 1]; extension[j] := extension[j + 1];
  152. END;
  153. loader[numLoaders - 1] := NIL; extension[numLoaders - 1] := "";
  154. DEC(numLoaders)
  155. END;
  156. Machine.Release(Machine.Modules)
  157. END RemoveLoader;
  158. (** Append string from to to, truncating on overflow. *)
  159. PROCEDURE Append*(CONST from: ARRAY OF CHAR; VAR to: ARRAY OF CHAR);
  160. VAR i, j, m: LONGINT;
  161. BEGIN
  162. j := 0; WHILE to[j] # 0X DO INC(j) END;
  163. m := LEN(to)-1;
  164. i := 0; WHILE (from[i] # 0X) & (j # m) DO to[j] := from[i]; INC(i); INC(j) END;
  165. to[j] := 0X
  166. END Append;
  167. (** Add a module to the pool of accessible modules, or return named module. *)
  168. PROCEDURE Publish*(VAR m: Module; VAR new: BOOLEAN);
  169. VAR n: Module; i: LONGINT;
  170. BEGIN
  171. ASSERT((m.code # NIL) & (LEN(m.code^) > 0));
  172. Machine.Acquire(Machine.Modules);
  173. n := root; WHILE (n # NIL) & (n.name # m.name) DO n := n.next END;
  174. IF n # NIL THEN (* module with same name exists, return it and ignore new m *)
  175. m := n; new := FALSE;
  176. Machine.Release(Machine.Modules);
  177. ELSE
  178. m.published := TRUE;
  179. m.next := root; root := m;
  180. m.refcnt := 0;
  181. FOR i := 0 TO LEN(m.module)-1 DO INC(m.module[i].refcnt) END;
  182. new := TRUE;
  183. (* another process may still be busy with entering procOffsets in the global table, wait here until not locked *)
  184. REPEAT UNTIL ~ptrOffsetsLock; (* only one process at a time can check this and only one process at a time can set and reset --> no race problem *)
  185. ptrOffsetsLock := TRUE;
  186. Machine.Release(Machine.Modules);
  187. (* InsertProcOffsets may not be called with the modules lock, cf. comment in InsertProcOffsets *)
  188. InsertProcOffsets(m.procTable, m.ptrTable, m.maxPtrs);
  189. m.procTable := NIL; m.ptrTable := NIL; (* not used any more as entered in global variable *)
  190. END;
  191. END Publish;
  192. (* runtime call for new compiler -- called by body of loaded module *)
  193. PROCEDURE PublishThis*(m: Module): BOOLEAN;
  194. VAR new: BOOLEAN;
  195. BEGIN
  196. Publish(m,new);
  197. RETURN new
  198. END PublishThis;
  199. (* runtime call for new compiler -- called by body of loaded module *)
  200. PROCEDURE SetInitialized*(m: Module);
  201. BEGIN
  202. m.init := TRUE;
  203. END SetInitialized;
  204. (** Return the named module or NIL if it is not loaded yet. *)
  205. PROCEDURE ModuleByName*(CONST name: ARRAY OF CHAR): Module;
  206. VAR m: Module;
  207. BEGIN
  208. Machine.Acquire(Machine.Modules);
  209. m := root; WHILE (m # NIL) & (m.name # name) DO m := m.next END;
  210. Machine.Release(Machine.Modules);
  211. RETURN m
  212. END ModuleByName;
  213. (* Generate a module file name. *)
  214. PROCEDURE GetFileName(CONST name, extension: ARRAY OF CHAR; VAR fileName: ARRAY OF CHAR);
  215. VAR i, j: LONGINT;
  216. BEGIN
  217. i := 0; WHILE name[i] # 0X DO fileName[i] := name[i]; INC(i) END;
  218. j := 0; WHILE extension[j] # 0X DO fileName[i] := extension[j]; INC(i); INC(j) END;
  219. fileName[i] := 0X
  220. END GetFileName;
  221. (** Load the module if it is not already loaded. *) (* Algorithm J. Templ, ETHZ, 1994 *)
  222. PROCEDURE ThisModule*(CONST name: ARRAY OF CHAR; VAR res: LONGINT; VAR msg: ARRAY OF CHAR): Module;
  223. TYPE Body = PROCEDURE;
  224. VAR m, p: Module; fileName: ARRAY 64 OF CHAR; body: Body; new: BOOLEAN; i: LONGINT;
  225. BEGIN
  226. res := Ok; msg[0] := 0X; m := ModuleByName(name);
  227. IF m = NIL THEN
  228. IF trace THEN
  229. Machine.Acquire (Machine.TraceOutput);
  230. Trace.String(">"); Trace.StringLn (name);
  231. Machine.Release (Machine.TraceOutput);
  232. END;
  233. i := 0;
  234. REPEAT
  235. GetFileName(name, extension[i], fileName);
  236. m := loader[i](name, fileName, res, msg);
  237. INC(i)
  238. UNTIL (i = numLoaders) OR (m # NIL);
  239. IF trace THEN
  240. Machine.Acquire (Machine.TraceOutput);
  241. Trace.String("?"); Trace.StringLn (name);
  242. Machine.Release (Machine.TraceOutput);
  243. END;
  244. p := m;
  245. IF (m # NIL) & ~m.published THEN (* no race on m.published, as update is done below in Publish *)
  246. Publish(m, new);
  247. IF new THEN (* m was successfully published *)
  248. body := SYSTEM.VAL (Body, ADDRESSOF(m.code[0]));
  249. body; res := Ok; msg[0] := 0X;
  250. m.init := TRUE (* allow ThisCommand *)
  251. ELSE
  252. (* m was part of cycle, replaced by existing module *)
  253. END
  254. END;
  255. IF trace THEN
  256. Machine.Acquire (Machine.TraceOutput);
  257. IF m = NIL THEN
  258. Trace.String("could not load "); Trace.StringLn(name)
  259. ELSIF ~m.published THEN
  260. Trace.String("not published "); Trace.StringLn(name)
  261. ELSE
  262. Trace.String("<"); Trace.StringLn (name);
  263. END;
  264. Machine.Release (Machine.TraceOutput);
  265. END;
  266. END;
  267. RETURN m
  268. END ThisModule;
  269. (** Return the module that contains code address pc or NIL if not found. Can also return freed modules. -- non-blocking variant for Reflection. *)
  270. PROCEDURE ThisModuleByAdr0*(pc: ADDRESS): Module;
  271. VAR m: Module; cbase, dbase: ADDRESS; i: LONGINT; found: BOOLEAN;
  272. BEGIN
  273. i := 0; found := FALSE;
  274. REPEAT
  275. CASE i OF
  276. 0: m := root
  277. |1: m := freeRoot
  278. END;
  279. WHILE (m # NIL) & ~found DO
  280. cbase := ADDRESSOF(m.code[0]); dbase := ADDRESSOF(m.data[0]);
  281. (* include 1 byte after module in module, therefore <= below *)
  282. IF (cbase <= pc) & (pc <= cbase + LEN(m.code^)) THEN
  283. found := TRUE
  284. ELSIF (dbase <= pc) & (pc <= dbase + LEN(m.data^)) THEN
  285. found := TRUE
  286. ELSE
  287. m := m.next
  288. END
  289. END;
  290. INC(i)
  291. UNTIL found OR (i = 2);
  292. RETURN m
  293. END ThisModuleByAdr0;
  294. (** Return the module that contains code address pc or NIL if not found. Can also return freed modules. *)
  295. PROCEDURE ThisModuleByAdr*(pc: ADDRESS): Module;
  296. VAR m: Module;
  297. BEGIN
  298. Machine.Acquire(Machine.Modules);
  299. m := ThisModuleByAdr0(pc);
  300. Machine.Release(Machine.Modules);
  301. RETURN m
  302. END ThisModuleByAdr;
  303. (* Retrieve a procedure given a module name, the procedure name and some type information (kernel call) *)
  304. PROCEDURE GetProcedure*(CONST moduleName, procedureName : ARRAY OF CHAR; argTdAdr, retTdAdr : ADDRESS; VAR entryAdr : ADDRESS);
  305. VAR module : Module; ignoreMsg : ARRAY 32 OF CHAR; i, res : LONGINT;
  306. BEGIN
  307. module := ThisModule(moduleName, res, ignoreMsg);
  308. IF (res = Ok) THEN
  309. IF ~module.init THEN (* give the module a chance to initialize, no timer available here, no yield ... *)
  310. i := 1000000;
  311. REPEAT DEC(i) UNTIL (i = 0) OR module.init
  312. END;
  313. (*
  314. ASSERT(module.init); (* module body must have been called (see note at end of module) *)
  315. *)
  316. IF module.init THEN
  317. Machine.Acquire(Machine.Modules);
  318. i := 0; entryAdr := Heaps.NilVal;
  319. WHILE (entryAdr = Heaps.NilVal) & (i # LEN(module.command^)) DO
  320. IF (module.command[i].name = procedureName) & (module.command[i].argTdAdr = argTdAdr) & (module.command[i].retTdAdr = retTdAdr) THEN
  321. entryAdr := module.command[i].entryAdr;
  322. END;
  323. INC(i)
  324. END;
  325. Machine.Release(Machine.Modules);
  326. END;
  327. END;
  328. END GetProcedure;
  329. (** Return the named type *)
  330. PROCEDURE ThisType*(m: Module; CONST name: ARRAY OF CHAR): TypeDesc;
  331. VAR i: LONGINT; type: TypeDesc;
  332. BEGIN
  333. Machine.Acquire(Machine.Modules);
  334. i := 0;
  335. WHILE (i < LEN(m.typeInfo)) & (m.typeInfo[i].name # name) DO INC(i) END;
  336. IF i = LEN(m.typeInfo) THEN
  337. type := NIL
  338. ELSE
  339. type := m.typeInfo[i]
  340. END;
  341. Machine.Release(Machine.Modules);
  342. RETURN type
  343. END ThisType;
  344. PROCEDURE ThisTypeByAdr*(adr: ADDRESS; VAR m: Module; VAR t: TypeDesc);
  345. BEGIN
  346. IF adr # 0 THEN
  347. Machine.Acquire(Machine.Modules);
  348. SYSTEM.GET (adr + Heaps.TypeDescOffset, adr);
  349. t := SYSTEM.VAL(TypeDesc, adr);
  350. m := t.mod;
  351. Machine.Release(Machine.Modules)
  352. ELSE
  353. m := NIL; t := NIL
  354. END
  355. END ThisTypeByAdr;
  356. (** create a new object given its type descriptor *)
  357. PROCEDURE NewObj*(t : TypeDesc; isRealtime: BOOLEAN) : ANY;
  358. VAR x : ANY;
  359. BEGIN
  360. Heaps.NewRec(x, SYSTEM.VAL (ADDRESS, t.tag), isRealtime);
  361. RETURN x;
  362. END NewObj;
  363. (** return the type descriptor of an object *)
  364. PROCEDURE TypeOf*(obj : ANY): TypeDesc;
  365. VAR
  366. m : Module;
  367. t : TypeDesc;
  368. adr : ADDRESS;
  369. BEGIN
  370. SYSTEM.GET(SYSTEM.VAL(ADDRESS, obj) + Heaps.TypeDescOffset, adr);
  371. ThisTypeByAdr(adr, m, t);
  372. RETURN t;
  373. END TypeOf;
  374. PROCEDURE FindPos(key: ADDRESS; VAR pos: LONGINT): BOOLEAN;
  375. VAR l, r, x: LONGINT; isHit: BOOLEAN;
  376. BEGIN
  377. l := 0; r := numProcs - 1;
  378. REPEAT
  379. x := (l + r) DIV 2;
  380. IF key < procOffsets[x].data.pcFrom THEN r := x - 1 ELSE l := x + 1 END;
  381. isHit := ((procOffsets[x].data.pcFrom <= key) & (key < procOffsets[x].data.pcLimit));
  382. UNTIL isHit OR (l > r);
  383. IF isHit THEN
  384. pos := x;
  385. RETURN TRUE
  386. ELSE
  387. RETURN FALSE
  388. END
  389. END FindPos;
  390. (** searches for the given pc in the global ProcKeyTable, if found it returns the corresponding data element *)
  391. PROCEDURE FindProc*(pc: ADDRESS; VAR data: ProcTableEntry; VAR index: LONGINT; VAR success: BOOLEAN);
  392. VAR x: LONGINT;
  393. BEGIN
  394. success := FindPos(pc, x);
  395. IF success THEN
  396. data := procOffsets[x].data;
  397. index := procOffsets[x].startIndex
  398. END
  399. END FindProc;
  400. PROCEDURE FindInsertionPos(VAR entry: ProcTableEntry; VAR pos: LONGINT): BOOLEAN;
  401. VAR l, r, x: LONGINT; success, isHit: BOOLEAN;
  402. BEGIN
  403. pos := -1;
  404. success := FALSE;
  405. IF numProcs = 0 THEN (* empty table *)
  406. pos := 0; success := TRUE
  407. ELSE
  408. l := 0; r := numProcs - 1;
  409. REPEAT
  410. x := (l + r) DIV 2;
  411. IF entry.pcLimit < procOffsets[x].data.pcFrom THEN r := x - 1 ELSE l := x + 1 END;
  412. isHit := ((x = 0) OR (procOffsets[x - 1].data.pcLimit <= entry.pcFrom)) & (entry.pcLimit <= procOffsets[x].data.pcFrom);
  413. UNTIL isHit OR (l > r);
  414. IF isHit THEN
  415. pos := x; success := TRUE
  416. ELSE
  417. IF (x = numProcs - 1) & (procOffsets[x].data.pcLimit <= entry.pcFrom) THEN
  418. pos := x + 1; success := TRUE
  419. END
  420. END
  421. END;
  422. RETURN success
  423. END FindInsertionPos;
  424. PROCEDURE NumTotalPtrs(procTable: ProcTable): LONGINT;
  425. VAR i, num: LONGINT;
  426. BEGIN
  427. num := 0;
  428. FOR i := 0 TO LEN(procTable) - 1 DO
  429. num := num + procTable[i].noPtr
  430. END;
  431. RETURN num
  432. END NumTotalPtrs;
  433. (* insert the procedure code offsets and pointer offsets of a single module into the global table *)
  434. PROCEDURE InsertProcOffsets(procTable: ProcTable; ptrTable: PtrTable; maxPtr: LONGINT);
  435. VAR success: BOOLEAN; i, j, pos, poslast, newLen, num: LONGINT; newProcOffsets: ProcOffsetTable; newPtrOffsets: PtrTable;
  436. BEGIN
  437. (* this procedure is called by procedure Publish only and is protected by the ptrOffsetsLock lock *)
  438. (* the Modules lock may not be taken because there is a NEW statement in this procedure:
  439. - this would violate the locking order precondition of Machine locks (no process holding a lower level lock can aquire a higher level lock) to prevent deadlocks
  440. - if this is violated (and Machine.strongChecks = FALSE, otherwise trap) then a deadlock can occur in the following subtle way:
  441. - this process takes the Module lock and stores the current interrupt state and disables interrupt
  442. - this process temporarily aquires the Heaps lock (no problem)
  443. - in case of a garbage collection, this process acquires the Objects lock (in Heaps.gcStatus of type Objects.GCStatus)
  444. - the scheduler is configured to only schedule processes of at least GC priority and normally the timer interrupt is expected to collect all still running processes
  445. - however, upon releasing the Objects lock, the interrupts are not enabled (because the ModulesLock is still acquired) and therefore the GC process can never be scheduled
  446. -> deadlock
  447. *)
  448. IF LEN(procTable) > 0 THEN
  449. IF numProcs + LEN(procTable) > LEN(procOffsets) THEN
  450. newLen := LEN(procOffsets) + InitTableLen;
  451. WHILE numProcs + LEN(procTable) > newLen DO newLen := newLen + InitTableLen END;
  452. NEW(newProcOffsets, newLen);
  453. FOR i := 0 TO numProcs - 1 DO
  454. newProcOffsets[i] := procOffsets[i]
  455. END;
  456. procOffsets := newProcOffsets
  457. END;
  458. num := NumTotalPtrs(procTable);
  459. IF numPtrs + num > LEN(ptrOffsets) THEN
  460. newLen := LEN(ptrOffsets) + InitPtrTableLen;
  461. WHILE numPtrs + num > newLen DO newLen := newLen + InitPtrTableLen END;
  462. NEW(newPtrOffsets, newLen);
  463. FOR i := 0 TO numPtrs - 1 DO
  464. newPtrOffsets[i] := ptrOffsets[i]
  465. END;
  466. ptrOffsets := newPtrOffsets
  467. END;
  468. success := FindInsertionPos(procTable[0], pos); success := success & FindInsertionPos(procTable[LEN(procTable) - 1], poslast);
  469. IF (~success) OR (pos # poslast) THEN Machine.Release(Machine.Modules); HALT(2001) END;
  470. FOR i := numProcs - 1 TO pos BY -1 DO procOffsets[i + LEN(procTable)] := procOffsets[i] END;
  471. FOR i := 0 TO LEN(procTable) - 1 DO
  472. procOffsets[pos + i].data := procTable[i];
  473. procOffsets[pos + i].startIndex := numPtrs; (* this field is never accessed in case of procTable[i].noPtr = 0, so we may as well put numPtrs in there *)
  474. FOR j := 0 TO procTable[i].noPtr - 1 DO
  475. ptrOffsets[numPtrs + j] := ptrTable[i * maxPtr + j]
  476. END;
  477. numPtrs := numPtrs + procTable[i].noPtr;
  478. END;
  479. numProcs := numProcs + LEN(procTable);
  480. END;
  481. (* release the ptrOffsetLock *)
  482. ptrOffsetsLock := FALSE;
  483. END InsertProcOffsets;
  484. (** deletes a sequence of entries given in procTable from the global procOffsets table - the table remains sorted,
  485. this procedure is called within AosLocks.AosModules, so no lock is taken here. *)
  486. PROCEDURE DeleteProcOffsets(firstProcPC: ADDRESS; noProcsInMod: LONGINT);
  487. VAR pos, i, noPtrsInMod, oldIndex: LONGINT; success: BOOLEAN;
  488. BEGIN
  489. IF noProcsInMod > 0 THEN
  490. success := FindPos(firstProcPC, pos);
  491. IF success THEN
  492. (* delete entries in ptrOffsets first *)
  493. noPtrsInMod := 0;
  494. FOR i := pos TO pos + noProcsInMod - 1 DO
  495. noPtrsInMod := noPtrsInMod + procOffsets[i].data.noPtr
  496. END;
  497. oldIndex := procOffsets[pos].startIndex;
  498. FOR i := procOffsets[pos].startIndex + noPtrsInMod TO numPtrs - 1 DO
  499. ptrOffsets[i - noPtrsInMod] := ptrOffsets[i]
  500. END;
  501. numPtrs := numPtrs - noPtrsInMod;
  502. (* delete entries in procOffsets *)
  503. FOR i := pos + noProcsInMod TO numProcs - 1 DO
  504. procOffsets[i - noProcsInMod] := procOffsets[i]
  505. END;
  506. numProcs := numProcs - noProcsInMod;
  507. (* adjust startIndex of procOffsets entries greater than those that have been deleted *)
  508. FOR i := 0 TO numProcs - 1 DO
  509. IF procOffsets[i].startIndex > oldIndex THEN
  510. procOffsets[i].startIndex := procOffsets[i].startIndex - noPtrsInMod
  511. END
  512. END;
  513. ELSE
  514. Trace.String("corrupt global procOffsets table"); Trace.Ln;
  515. HALT(2000)
  516. END
  517. END
  518. END DeleteProcOffsets;
  519. (** Install procedure to execute when module is freed or shut down. The handler can distinguish the two cases by checking Modules.shutdown. If it is None, the module is being freed, otherwise the system is being shut down or rebooted. Only one handler may be installed per module. The last handler installed is active. *)
  520. PROCEDURE InstallTermHandler*(h: TerminationHandler);
  521. VAR m: Module;
  522. BEGIN
  523. m := ThisModuleByAdr(SYSTEM.VAL (ADDRESS, h));
  524. IF m # NIL THEN
  525. m.term := h (* overwrite existing handler, if any *)
  526. END
  527. END InstallTermHandler;
  528. (** Free a module. The module's termination handler, if any, is called first. Then all objects that have finalizers in this module are finalized (even if they are still reachable). Then the module's data and code are invalidated. *)
  529. PROCEDURE FreeModule*(CONST name: ARRAY OF CHAR; VAR res: LONGINT; VAR msg: ARRAY OF CHAR);
  530. VAR p, m: Module; term: TerminationHandler; i: LONGINT;
  531. BEGIN
  532. m := ModuleByName(name);
  533. IF (m # NIL) & (m.refcnt = 0) THEN (* will be freed below *)
  534. IF m.term # NIL THEN (* call termination handler *)
  535. term := m.term; m.term := NIL; term (* may trap *)
  536. END;
  537. Heaps.CleanupModuleFinalizers(ADDRESSOF(m.code[0]), LEN(m.code), m.name)
  538. END;
  539. res := Ok; msg[0] := 0X;
  540. Machine.Acquire(Machine.Modules);
  541. p := NIL; m := root;
  542. WHILE (m # NIL) & (m.name # name) DO p := m; m := m.next END;
  543. IF m # NIL THEN
  544. IF m.refcnt = 0 THEN (* free the module *)
  545. FOR i := 0 TO LEN(m.module)-1 DO DEC(m.module[i].refcnt) END;
  546. m.init := FALSE; (* disallow ThisCommand *)
  547. Append("?", m.name);
  548. (* move module to free list *)
  549. IF p = NIL THEN root := root.next ELSE p.next := m.next END;
  550. m.next := freeRoot; freeRoot := m;
  551. (* clear global pointers and code *)
  552. FOR i := 0 TO LEN(m.ptrAdr)-1 DO SYSTEM.PUT (m.ptrAdr[i], NIL) END;
  553. IF ClearCode THEN
  554. FOR i := 0 TO LEN(m.code)-1 DO m.code[i] := 0CCX END
  555. END;
  556. (* remove references to module data *)
  557. m.published := FALSE;
  558. m.entry := NIL; m.command := NIL; m.ptrAdr := NIL;
  559. (* do not clear m.type or m.module, as old heap block tags might reference type descs indirectly. *) (* m.staticTypeDescs, m.typeInfo ??? *)
  560. (* do not clear m.data or m.code, as they are used in ThisModuleByAdr (for debugging). *)
  561. (* do not clear m.refs, as they are used in Traps (for debugging). *)
  562. m.export.dsc := NIL; m.exTable := NIL;
  563. DeleteProcOffsets(m.firstProc, m.noProcs);
  564. ELSE
  565. res := 1901; (* can not free module in use *)
  566. COPY(name, msg); Append(" reference count not zero", msg)
  567. END
  568. ELSE
  569. res := 1902; (* module not found *)
  570. COPY(name, msg); Append(" not found", msg)
  571. END;
  572. Machine.Release(Machine.Modules)
  573. END FreeModule;
  574. PROCEDURE Terminate(term: TerminationHandler);
  575. BEGIN
  576. term
  577. FINALLY
  578. (* trapped in module finalization -- just bad luck, let's try finalizing *)
  579. END Terminate;
  580. (** Shut down all modules by calling their termination handlers and then call Machine.Shutdown. *)
  581. PROCEDURE Shutdown*(code: LONGINT);
  582. VAR m: Module; term: TerminationHandler;
  583. BEGIN
  584. IF code # None THEN
  585. LOOP
  586. Machine.Acquire(Machine.Modules);
  587. m := root; WHILE (m # NIL) & (m.term = NIL) DO m := m.next END;
  588. IF m # NIL THEN term := m.term; m.term := NIL END; (* finalizer only called once *)
  589. Machine.Release(Machine.Modules);
  590. IF m = NIL THEN EXIT END;
  591. IF trace THEN
  592. Machine.Acquire (Machine.TraceOutput);
  593. Trace.String("TermHandler "); Trace.StringLn (m.name);
  594. Machine.Release (Machine.TraceOutput);
  595. END;
  596. Terminate(term) (* if this causes hangs, another shutdown call will retry -- is this a good solution ? *)
  597. END;
  598. (* clean up finalizers *)
  599. m := root;
  600. WHILE m # NIL DO
  601. Heaps.CleanupModuleFinalizers(ADDRESSOF(m.code[0]), LEN(m.code), m.name);
  602. m := m.next
  603. END;
  604. IF trace THEN
  605. Machine.Acquire (Machine.TraceOutput);
  606. Trace.StringLn ("Modules.Shutdown finished");
  607. Machine.Release (Machine.TraceOutput);
  608. END;
  609. Machine.Shutdown(code = Reboot) (* does not return *)
  610. END
  611. END Shutdown;
  612. (* Is this PC handled in the corresponding module. deep = scan the whole stack. *)
  613. PROCEDURE IsExceptionHandled*(VAR pc, fp: ADDRESS; deep: BOOLEAN): BOOLEAN;
  614. VAR
  615. handler: ADDRESS;
  616. BEGIN
  617. IF deep THEN
  618. handler := GetExceptionHandler(pc);
  619. IF handler # -1 THEN (* Handler in the current PAF *)
  620. RETURN TRUE
  621. ELSE
  622. WHILE (fp # 0) & (handler = -1) DO
  623. SYSTEM.GET (fp + 4, pc);
  624. pc := pc - 1; (* CALL instruction, machine dependant!!! *)
  625. handler := GetExceptionHandler(pc);
  626. SYSTEM.GET (fp, fp) (* Unwind PAF *)
  627. END;
  628. IF handler = -1 THEN RETURN FALSE ELSE pc := handler; RETURN TRUE END
  629. END
  630. ELSE
  631. RETURN GetExceptionHandler(pc) # -1
  632. END
  633. END IsExceptionHandled;
  634. (* Is this PC handled in the corresponding module. If the PC is handled the PC of the
  635. handler is return else -1 is return. There is no problem concurrently accessing this
  636. procedure, there is only reading work. *)
  637. PROCEDURE GetExceptionHandler*(pc: ADDRESS): ADDRESS;
  638. VAR
  639. m: Module;
  640. PROCEDURE BinSearch(exTable: ExceptionTable; key: ADDRESS): ADDRESS;
  641. VAR
  642. x, l, r: LONGINT;
  643. BEGIN
  644. l := 0; r:=LEN(exTable) - 1;
  645. REPEAT
  646. x := (l + r) DIV 2;
  647. IF key < exTable[x].pcFrom THEN r := x - 1 ELSE l := x + 1 END;
  648. UNTIL ((key >= exTable[x].pcFrom) & (key < exTable[x].pcTo) ) OR (l > r);
  649. IF (key >= exTable[x].pcFrom) & (key < exTable[x].pcTo) THEN
  650. RETURN exTable[x].pcHandler;
  651. ELSE
  652. RETURN -1;
  653. END
  654. END BinSearch;
  655. BEGIN
  656. m := ThisModuleByAdr(pc);
  657. IF (m # NIL) & (m.exTable # NIL) & (LEN(m.exTable) > 0) THEN
  658. RETURN BinSearch(m.exTable, pc);
  659. END;
  660. RETURN -1;
  661. END GetExceptionHandler;
  662. (** fof: to make custom solutions to the race process, described below, possible. This is not a solution to the generic problem !! *)
  663. PROCEDURE Initialized*(m: Module): BOOLEAN;
  664. BEGIN
  665. RETURN m.init;
  666. END Initialized;
  667. PROCEDURE Init;
  668. VAR
  669. s: ARRAY 4 OF CHAR;
  670. BEGIN
  671. (* root and initBlock are initialized by the linker *)
  672. ptrOffsetsLock := FALSE;
  673. shutdown := None;
  674. numLoaders := 0;
  675. freeRoot := NIL;
  676. Machine.GetConfig("TraceModules", s);
  677. trace := (s[0] = "1")
  678. END Init;
  679. BEGIN
  680. Init
  681. END Modules.
  682. (*
  683. 19.03.1998 pjm Started
  684. 06.10.1998 pjm FreeModule
  685. Note:
  686. o ThisCommand race: process A calls ThisModule, the module is published, but before its body has finished executing, process B calls ThisCommand, causing the assert (m.init) to fail. Process B should perhaps wait in this case until the body has executed, or ThisCommand should return NIL (but that will just move the race to the user).
  687. *)