-- CTLoadImpl.Mesa, last edit March 14, 1983 2:09 pm -- Mesa 7.0/ Pilot 6.0 -- procedures to load and start modules in the compiler tool -- links: -- IF gfi > firstdummy, then gfi is index into Import table -- and ep is index into the export record pared with that import -- binding is simply to copy control link in the export record -- into this link -- IF gfi < firstdummy, then gfi in this link is an index into the config's -- moduletable. Do not alter the ep DIRECTORY BcdDefs: TYPE USING [Base, FTIndex, FTSelf, GFTIndex, Link, MTIndex, NameRecord, NullLink, UnboundLink, VersionID], BcdOps: TYPE USING [BcdBase, EXPHandle, FTHandle, MTHandle, NameString, ProcessModules], CedarLinkerOps: TYPE USING[FindVariableLink], CTLoad: TYPE USING [DummyMapSeq, DummyMapSeqRecord, ImpExpSeq, ImpExpSeqRecord, InterfaceSeq, InterfaceSeqRecord, LoadInfoSeq, LoadInfoSeqRecord, ReplaceResult], Directory: TYPE USING [UpdateDates], Environment: TYPE USING [PageCount, PageNumber], File: TYPE USING [Capability, PageCount, PageNumber, read, write], Frame: TYPE USING [GetReturnLink], Inline: TYPE USING [BITAND], IO: TYPE USING[card, Handle, PutF, rope], LoaderPrivate: TYPE USING[AssignCodeToFrames, AssignControlModules, CreateGlobalFrames, FindMappedSpace, NextMultipleOfFour], PilotLoaderOps: TYPE USING[DestroyMap, InitializeMap, IthLink, LinkSegmentLength], PilotLoadStateOps: TYPE USING[ConfigIndex], PrincOps: TYPE USING [ControlLink, ControlModule, Frame, GFTIndex, GlobalFrameHandle, NullLink, StateVector, UnboundLink], PrincOpsRuntime: TYPE USING [GetFrame, GFT], Rope: TYPE USING[Cat, Flatten, FromChar, ROPE, Text], RTOS: TYPE USING [CheckForModuleReplacement], Runtime: TYPE USING [ValidateGlobalFrame], RuntimeInternal: TYPE USING [Codebase], SDDefs: TYPE USING [SD, sStart, sSwapTrap], Space: TYPE USING [Create, Delete, Error, GetHandle, GetWindow, Handle, LongPointer, MakeReadOnly, MakeWritable, Map, PageFromLongPointer, virtualMemory], TimeStamp: TYPE USING [Stamp], Trap: TYPE USING [ReadOTP]; CTLoadImpl: MONITOR IMPORTS BcdOps, CedarLinkerOps, Directory, Frame, Inline, IO, LoaderPrivate, PilotLoaderOps, PrincOpsRuntime, Rope, RTOS, Runtime, RuntimeInternal, Space, Trap EXPORTS CTLoad SHARES File = { -- MDS Usage! waitCodeTrapCV: CONDITION; -- link space data links: LONG POINTER TO ARRAY[0 .. 0) OF PrincOps.ControlLink ← NIL; writeable: BOOL ← FALSE; long: BOOL ← FALSE; -- end of MDS usage LoadGlobalFrames: PUBLIC PROC[cap: File.Capability, config: PilotLoadStateOps.ConfigIndex, oldConfigGfi: PrincOps.GFTIndex, out: IO.Handle] RETURNS[loadinfoseq: CTLoad.LoadInfoSeq, newConfigGfi: PrincOps.GFTIndex] = { bcdbase: BcdOps.BcdBase; mod: CARDINAL ← 0; dummymapseq: CTLoad.DummyMapSeq; -- mod, dummymapseq, map is passed in ForEachModule: PROC[mth: BcdOps.MTHandle, mti: BcdDefs.MTIndex] RETURNS[stop: BOOL] = { gfi: CARDINAL; frame: PrincOps.GlobalFrameHandle; stop ← FALSE; frame ← PrincOpsRuntime.GetFrame[PrincOpsRuntime.GFT[loadinfoseq.map[mth.gfi]]]; loadinfoseq[mod] ← [frame: frame, framesize: mth.framesize, ngfi: mth.ngfi]; newConfigGfi ← newConfigGfi + mth.ngfi; -- now store in cgfi to rgfi map gfi ← loadinfoseq.map[mth.gfi]; FOR i: CARDINAL IN [0 .. mth.ngfi) DO dummymapseq[mth.gfi + i] ← [ind: gfi, whichone: i]; ENDLOOP; dummymapseq.size ← dummymapseq.size + mth.ngfi; mod ← mod + 1; out.PutF["%s: gfi = %bB, gfh = %bB, framesize = %d, ", IO.rope[NSToRope[bcdbase, mth.name]], IO.card[gfi], IO.card[LOOPHOLE[frame, CARDINAL]], IO.card[mth.framesize]]; out.PutF["#%slinks = %d\n", IO.rope[IF frame.codelinks THEN "code" ELSE "frame"], IO.card[PilotLoaderOps.LinkSegmentLength[mth, bcdbase]]]; }; { cap ← Directory.UpdateDates[cap, File.read]; [bcd: bcdbase] ← LoadUpBcd[cap, 1]; loadinfoseq ← AllocateLoadInfoSeq[bcdbase]; loadinfoseq.bcdbase ← bcdbase; loadinfoseq.size ← 0; loadinfoseq.configGfi ← oldConfigGfi; newConfigGfi ← oldConfigGfi; dummymapseq ← loadinfoseq.dummymapseq; dummymapseq[0] ← [0, 0]; dummymapseq.size ← 1; -- dummy module indices start at 1, not 0 loadinfoseq.map ← PilotLoaderOps.InitializeMap[bcdbase]; -- map is filled in by CreateGlobalFrames loadinfoseq.frameList ← LoaderPrivate.CreateGlobalFrames[bcdbase, loadinfoseq.map, config, FALSE]; [] ← BcdOps.ProcessModules[bcdbase, ForEachModule]; LoaderPrivate.AssignCodeToFrames[bcdbase, cap, 1, loadinfoseq.map]; SetLinksToNull[bcdbase, loadinfoseq]; loadinfoseq.cm ← LoaderPrivate.AssignControlModules[bcdbase, loadinfoseq.map]; loadinfoseq.size ← mod; }}; WaitForBroadcast: ENTRY PROC[frame: PrincOps.GlobalFrameHandle] = { WHILE frame.code.out DO WAIT waitCodeTrapCV; ENDLOOP; }; -- can only be called for modules (not configs) LoadIncremental: PUBLIC ENTRY PROC[bcdcap: File.Capability, loadinfoseq: CTLoad.LoadInfoSeq, window: IO.Handle] RETURNS[replaceResult: CTLoad.ReplaceResult] = { savemodellercode: PROC ← NIL; codetrapframe: PrincOps.GlobalFrameHandle ← NIL; { ENABLE UNWIND => IF savemodellercode ~= NIL THEN SDDefs.SD[SDDefs.sSwapTrap] ← savemodellercode; ModellerCodeTrap: PROC = { start: PROC[PrincOps.ControlModule]; dest: PrincOps.ControlLink; state: PrincOps.StateVector; frame: PrincOps.GlobalFrameHandle; state ← STATE; dest ← Trap.ReadOTP[]; state.dest ← Frame.GetReturnLink[]; DO IF dest.proc THEN { frame ← PrincOpsRuntime.GetFrame[PrincOpsRuntime.GFT[dest.gfi]]; EXIT} ELSE IF dest.indirect THEN dest ← dest.link↑ ELSE {frame ← dest.frame.accesslink; EXIT}; -- frame ENDLOOP; IF frame = codetrapframe THEN { -- this halts outside process until my procedure is finished WaitForBroadcast[frame]; RETURN; }; IF ~frame.started THEN { start ← LOOPHOLE[SDDefs.SD[SDDefs.sStart]]; start[[frame[frame]]]; }; frame.code.out ← FALSE; RETURN WITH state }; n: CARDINAL; mname: Rope.Text; gfi: CARDINAL; bcdbase: BcdOps.BcdBase; mth: BcdOps.MTHandle; frame: PrincOps.GlobalFrameHandle ← loadinfoseq[0].frame; replaceResult ← ok; IF loadinfoseq.size ~= 1 THEN RETURN[configNotReplaceable]; [bcdbase] ← LoadUpBcd[bcdcap, 1]; loadinfoseq.bcdbase ← bcdbase; loadinfoseq.cm ← LOOPHOLE[frame]; -- recompute these since bcdbase.firstdummy, bcdbase.nDummies, and bcdbase.nImports -- may have changed ReSizeMaps[loadinfoseq]; mth ← @LOOPHOLE[bcdbase + bcdbase.mtOffset, BcdDefs.Base][FIRST[BcdDefs.MTIndex]]; -- checking IF LOOPHOLE[frame + mth.framesize, CARDINAL] > LOOPHOLE[LoaderPrivate.NextMultipleOfFour[frame + loadinfoseq[0].framesize], CARDINAL] THEN RETURN[frameTooBig]; IF mth.ngfi > loadinfoseq[0].ngfi THEN RETURN[ngfiTooBig]; -- now think of monitor: -- set lock by setting code trap, then call check procedure -- then do replacement, then release lock -- this is to avoid the case where some local frames -- are created after the call on RTOS.CheckForModuleReplacemeent -- but before I swap the code codetrapframe ← frame; savemodellercode ← SDDefs.SD[SDDefs.sSwapTrap]; SDDefs.SD[SDDefs.sSwapTrap] ← ModellerCodeTrap; frame.code.out ← TRUE; -- force code trap IF NOT RTOS.CheckForModuleReplacement[frame] THEN { SDDefs.SD[SDDefs.sSwapTrap] ← savemodellercode; RETURN[checkForMRFailed]; }; PilotLoaderOps.DestroyMap[loadinfoseq.map]; -- old map loadinfoseq.map ← PilotLoaderOps.InitializeMap[bcdbase]; -- the dummy bcd #'s start at 1 FOR i: CARDINAL IN [0 .. bcdbase.firstdummy) DO loadinfoseq.map[i] ← loadinfoseq.dummymapseq[i].ind; ENDLOOP; LoaderPrivate.AssignCodeToFrames[bcdbase, bcdcap, 1, loadinfoseq.map]; -- havinge set the code base, we can now release the lock frame.code.out ← FALSE; BROADCAST waitCodeTrapCV; SDDefs.SD[SDDefs.sSwapTrap] ← savemodellercode; savemodellercode ← NIL; SetLinksToNull[bcdbase, loadinfoseq]; mname ← NSToRope[bcdbase, mth.name]; n ← mth.framesize; gfi ← frame.gfi; window.PutF["%s: gfi = %bB, gfh = %bB, framesize = %d, ", IO.rope[mname], IO.card[gfi], IO.card[LOOPHOLE[frame, CARDINAL]], IO.card[n]]; n ← PilotLoaderOps.LinkSegmentLength[mth, bcdbase]; window.PutF["#%slinks = %d\n", IO.rope[IF frame.codelinks THEN "code" ELSE "frame"], IO.card[n]]; RETURN[ok]; }}; -- old contents are preserved ReSizeMaps: PROC[loadinfoseq: CTLoad.LoadInfoSeq] = { olddummymapseq: CTLoad.DummyMapSeq ← loadinfoseq.dummymapseq; oldimpexpseq: CTLoad.ImpExpSeq ← loadinfoseq.impexpseq; minSize: CARDINAL; loadinfoseq.dummymapseq ← NEW[CTLoad.DummyMapSeqRecord[ loadinfoseq.bcdbase.firstdummy +loadinfoseq.bcdbase.nDummies]]; loadinfoseq.impexpseq ← NEW[ CTLoad.ImpExpSeqRecord[loadinfoseq.bcdbase.nImports]]; minSize ← MIN[olddummymapseq.size, loadinfoseq.dummymapseq.maxsize]; FOR i: CARDINAL IN [0 .. minSize) DO loadinfoseq.dummymapseq[i] ← olddummymapseq[i]; ENDLOOP; loadinfoseq.dummymapseq.size ← minSize; minSize ← MIN[oldimpexpseq.size, loadinfoseq.impexpseq.maxsize]; FOR i: CARDINAL IN [0 .. minSize) DO loadinfoseq.impexpseq[i] ← oldimpexpseq[i]; ENDLOOP; loadinfoseq.impexpseq.size ← minSize; FREE[@olddummymapseq]; FREE[@oldimpexpseq]; }; SetLinksToNull: PROC[bcdbase: BcdOps.BcdBase, loadinfoseq: CTLoad.LoadInfoSeq] = { ForEachModule: PROC[mth: BcdOps.MTHandle, mti: BcdDefs.MTIndex] RETURNS[stop: BOOL] = { -- set all the links to null frame: PrincOps.GlobalFrameHandle; stop ← FALSE; frame ← PrincOpsRuntime.GetFrame[PrincOpsRuntime.GFT[ loadinfoseq.dummymapseq[mth.gfi].ind]]; Runtime.ValidateGlobalFrame[frame]; [] ← OpenLinkSpace[frame, mth, bcdbase]; FOR i: CARDINAL IN [0..PilotLoaderOps.LinkSegmentLength[mth, bcdbase]) DO WriteLink[ offset: i, link: SELECT PilotLoaderOps.IthLink[mth, i, bcdbase].vtag FROM var, type => PrincOps.NullLink, ENDCASE => PrincOps.UnboundLink]; ENDLOOP; CloseLinkSpace[frame]; }; [] ← BcdOps.ProcessModules[bcdbase, ForEachModule]; loadinfoseq.linksresolved ← FALSE; }; BuildInterface: PUBLIC PROC[loadinfoseq: CTLoad.LoadInfoSeq, eth: BcdOps.EXPHandle] RETURNS[interfaceseq: CTLoad.InterfaceSeq] = { clink: PrincOps.ControlLink; cgfi: PrincOps.GFTIndex; -- dummy bcdbase: BcdOps.BcdBase; name: Rope.Text; fth: BcdOps.FTHandle; IF eth.size = 0 THEN RETURN[NIL]; bcdbase ← loadinfoseq.bcdbase; name ← NSToRope[bcdbase, eth.name]; interfaceseq ← AllocateInterfaceSeq[name, eth.size]; interfaceseq.resolved ← TRUE; FOR i: CARDINAL IN [0 .. eth.size) DO SELECT eth.links[i].vtag FROM var => { [link: clink] ← CedarLinkerOps.FindVariableLink[bcd: loadinfoseq.bcdbase, mthLink: eth.links[i], rgfi: loadinfoseq.dummymapseq[eth.links[i].vgfi].ind]; interfaceseq[i] ← [clink: clink, blink: BcdDefs.NullLink] }; proc0, proc1 => { realgfi: PrincOps.GFTIndex; realgfi ← loadinfoseq.dummymapseq[eth.links[i].gfi].ind; [clink, cgfi] ← ConvertDummyLinkToControlLink[eth.links[i], realgfi, bcdbase, loadinfoseq]; interfaceseq[i] ← [clink: clink, blink: [procedure[gfi: cgfi, ep: eth.links[i].ep, tag: eth.links[i].tag]]]; }; type => -- means no checking for exported type mismatches!!! interfaceseq[i] ← [clink: PrincOps.NullLink, blink: BcdDefs.UnboundLink]; ENDCASE => ERROR; IF EmptyLink[interfaceseq[i].clink] THEN interfaceseq.resolved ← FALSE; ENDLOOP; interfaceseq.size ← eth.size; fth ← @LOOPHOLE[bcdbase + bcdbase.ftOffset, BcdDefs.Base][eth.file]; interfaceseq.bcdVers ← fth.version; }; EmptyLink: PROC[link: PrincOps.ControlLink] RETURNS[empty: BOOL] = { RETURN[link = PrincOps.UnboundLink OR link = PrincOps.NullLink]; }; -- can't be used for configs BuildFramePtrInterface: PUBLIC PROC[bcdbase: BcdOps.BcdBase, frame: PrincOps.GlobalFrameHandle] RETURNS[interfaceseq: CTLoad.InterfaceSeq] = { name: Rope.Text; mth: BcdOps.MTHandle; IF bcdbase.nModules ~= 1 THEN ERROR; mth ← @LOOPHOLE[bcdbase + bcdbase.mtOffset, BcdDefs.Base][FIRST[BcdDefs.MTIndex]]; name ← NSToRope[bcdbase, mth.name]; interfaceseq ← AllocateInterfaceSeq[name, 1]; IF mth.file = BcdDefs.FTSelf THEN interfaceseq.bcdVers ← bcdbase.version ELSE { fth: BcdOps.FTHandle; fth ← @LOOPHOLE[bcdbase + bcdbase.ftOffset, BcdDefs.Base][mth.file]; interfaceseq.bcdVers ← fth.version; }; interfaceseq[0] ← [clink: LOOPHOLE[frame], blink: BcdDefs.NullLink]; interfaceseq.size ← 1; }; -- only works for exported BcdDefs.Links in the export table ConvertDummyLinkToControlLink: PROC[bl: BcdDefs.Link, realgfi: CARDINAL, bcdbase: BcdOps.BcdBase, loadinfoseq: CTLoad.LoadInfoSeq] RETURNS[cl: PrincOps.ControlLink, newcgfi: PrincOps.GFTIndex] = { cgfi: PrincOps.GFTIndex; ForEachModule: PROC [mth: BcdOps.MTHandle, mti: BcdDefs.MTIndex] RETURNS [BOOL] = BEGIN mgfi: PrincOps.GFTIndex ← mth.gfi; IF cgfi IN [mth.gfi..mgfi + mth.ngfi) THEN { newcgfi ← newcgfi + (cgfi - mgfi); realgfi ← realgfi + (cgfi - mgfi); RETURN[TRUE]; }; RETURN[FALSE] END; newcgfi ← loadinfoseq.configGfi; IF bl = BcdDefs.UnboundLink THEN RETURN[PrincOps.UnboundLink, newcgfi]; SELECT bl.vtag FROM var => { cgfi ← bl.vgfi; IF BcdOps.ProcessModules[bcdbase, ForEachModule].mth = NIL THEN RETURN[PrincOps.NullLink, newcgfi]; cl ← [procedure[gfi: realgfi, ep: bl.var, tag: FALSE]]; }; proc0, proc1 => { cgfi ← bl.gfi; IF BcdOps.ProcessModules[bcdbase, ForEachModule].mth = NIL THEN RETURN[PrincOps.UnboundLink, newcgfi]; cl ← [procedure[gfi: realgfi, ep: bl.ep, tag: TRUE]]; }; type => cl ← LOOPHOLE[bl.typeID]; ENDCASE; }; NSToRope: PUBLIC PROC[bcdbase: BcdOps.BcdBase, name: BcdDefs.NameRecord] RETURNS[resultstr: Rope.Text] = { namestring: BcdOps.NameString; r: Rope.ROPE ← NIL; namestring ← LOOPHOLE[bcdbase + bcdbase.ssOffset]; FOR i: CARDINAL IN [0 .. namestring.size[name]) DO r ← r.Cat[Rope.FromChar[namestring.string.text[name + i]]]; ENDLOOP; resultstr ← Rope.Flatten[r]; }; AllocateLoadInfoSeq: PUBLIC PROC[bcdbase: BcdOps.BcdBase] RETURNS[loadinfoseq: CTLoad.LoadInfoSeq] = { loadinfoseq ← NEW[CTLoad.LoadInfoSeqRecord[bcdbase.nModules]]; loadinfoseq.dummymapseq ← NEW[ CTLoad.DummyMapSeqRecord[bcdbase.firstdummy +bcdbase.nDummies]]; loadinfoseq.impexpseq ← NEW[ CTLoad.ImpExpSeqRecord[bcdbase.nImports]]; }; FreeLoadInfoSeq: PUBLIC PROC[loadinfoseq: CTLoad.LoadInfoSeq] RETURNS[CTLoad.LoadInfoSeq]= { IF loadinfoseq = NIL THEN RETURN[NIL]; IF loadinfoseq.bcdbase ~= NIL THEN Space.Delete[Space.GetHandle[Space.PageFromLongPointer[loadinfoseq.bcdbase]] ! Space.Error => CONTINUE]; loadinfoseq.impexpseq ← NIL; loadinfoseq.dummymapseq ← NIL; RETURN[NIL]; }; AllocateInterfaceSeq: PUBLIC PROC[name: Rope.Text, size: CARDINAL] RETURNS[interfaceseq: CTLoad.InterfaceSeq] = { interfaceseq ← NEW[CTLoad.InterfaceSeqRecord[size] ← [body: ]]; interfaceseq.name ← name; FOR i: CARDINAL IN [0 .. size) DO interfaceseq[i] ← [PrincOps.NullLink, BcdDefs.NullLink]; ENDLOOP; }; FreeInterfaceSeq: PUBLIC PROC[interfaceseq: CTLoad.InterfaceSeq] RETURNS[CTLoad.InterfaceSeq] = { IF interfaceseq = NIL THEN RETURN[NIL]; interfaceseq.name ← NIL; FREE[@interfaceseq]; RETURN[NIL]; }; InvalidFile: PUBLIC ERROR [File.Capability] = CODE; LoadUpBcd: PROC [file: File.Capability, offset: File.PageCount] RETURNS [bcd: BcdOps.BcdBase, pages: CARDINAL] = BEGIN bcdSpaceBase: File.PageNumber; bcdSpace: Space.Handle; bcdSpaceBase ← offset; bcdSpace ← Space.Create[size: 1, parent: Space.virtualMemory]; Space.Map[space: bcdSpace, window: [file: file, base: bcdSpaceBase]]; bcd ← Space.LongPointer[bcdSpace]; pages ← bcd.nPages; IF bcd.versionIdent # BcdDefs.VersionID OR bcd.definitions OR (NOT bcd.tableCompiled AND ~bcd.spare1) THEN ERROR InvalidFile[file ! UNWIND => Space.Delete[bcdSpace]]; IF pages > 1 THEN BEGIN Space.Delete[bcdSpace]; bcdSpace ← Space.Create[size: pages, parent: Space.virtualMemory]; Space.Map[space: bcdSpace, window: [file: file, base: bcdSpaceBase]]; bcd ← Space.LongPointer[bcdSpace]; END; Space.MakeReadOnly[bcdSpace]; -- make bcd header spaces ReadOnly, as the code is already RETURN END; -- link space operations OpenLinkSpace: PUBLIC PROC [frame: PrincOps.GlobalFrameHandle, mth: BcdOps.MTHandle, bcd: BcdOps.BcdBase ← NIL] RETURNS[LONG POINTER] = BEGIN IF frame.codelinks THEN { long ← TRUE; links ← RuntimeInternal.Codebase[LOOPHOLE[frame]]; IF links = NIL THEN ERROR; } ELSE { long ← FALSE; links ← LOOPHOLE[LONG[frame]] }; links ← links - PilotLoaderOps.LinkSegmentLength[mth, bcd]; writeable ← FALSE; RETURN[links]; END; ReadLink: PUBLIC PROC [offset: CARDINAL] RETURNS [link: PrincOps.ControlLink] = BEGIN RETURN[links[offset]]; END; WriteLink: PUBLIC PROC [offset: CARDINAL, link: PrincOps.ControlLink] = { IF long AND NOT writeable THEN { space: Space.Handle; cap: File.Capability; writeable ← TRUE; space ← LoaderPrivate.FindMappedSpace[Space.GetHandle[ Space.PageFromLongPointer[links]]]; cap ← Space.GetWindow[space].file; -- questionable???? -- IF Inline.BITAND[cap.permissions, File.write] = 0 THEN cap.permissions ← cap.permissions + File.write; Space.MakeWritable[space, cap] }; links[offset] ← link}; CloseLinkSpace: PUBLIC PROC [frame: PrincOps.GlobalFrameHandle] = { IF long AND writeable THEN Space.MakeReadOnly[LoaderPrivate.FindMappedSpace[Space.GetHandle[ Space.PageFromLongPointer[links]]]] }; ConvertLink: PUBLIC PROC [bl: BcdDefs.Link] RETURNS [cl: PrincOps.ControlLink] = BEGIN IF bl = BcdDefs.UnboundLink THEN RETURN[PrincOps.UnboundLink]; SELECT bl.vtag FROM var => cl ← [procedure[gfi: bl.vgfi, ep: bl.var, tag: FALSE]]; proc0, proc1 => cl ← [procedure[gfi: bl.gfi, ep: bl.ep, tag: TRUE]]; type => cl ← LOOPHOLE[bl.typeID]; ENDCASE; RETURN END; }.