CedarLinkerImpl.mesa - fills in the links of a newly loaded bcd.
Copyright © 1985 by Xerox Corporation. All rights reserved.
Maxwell on: February 28, 1983 1:45 pm
Levin, August 9, 1983 11:46 am
Rovner on: August 15, 1983 11:57 am
MBrown on: September 11, 1983 12:53 pm
Russ Atkinson (RRA) February 4, 1985 4:42:53 pm PST
DIRECTORY
Atom USING [GetPName, GetProp, MakeAtom],
BcdDefs USING [ProcLimit, FTSelf, IMPIndex, Link, MTIndex, MTRecord, UnboundLink, VersionStamp, BcdBase, IMPHandle, MTHandle, NameString],
BcdOps USING [ProcessImports, ProcessModules],
IO USING [STREAM, ROS, PutChar, RopeFromROS],
Loader USING [IRList],
LoaderOps USING [IsNullLink, PendingModule, Binding, IR, Pending, FindVariableLink, GetPendingList, SetPendingList, BindingSequence, GetIR],
LoadState USING [ConfigInfo, ConfigID, ModuleInfo, local, EnumerateConfigs, BuildProcDescUsingModule],
PrincOps USING [ControlLink, GlobalFrameHandle, NullLink, UnboundLink],
PrincOpsUtils USING [Codebase],
Rope USING [Text, Length, InlineFetch],
Table USING [Base],
VM USING [Interval, MakeReadOnly, MakeReadWrite, PageNumberForAddress, Age];
CedarLinkerImpl: PROGRAM
IMPORTS Atom, BcdOps, IO, LoaderOps, LoadState, PrincOpsUtils, Rope, VM
EXPORTS LoaderOps
= { OPEN BcdDefs, Loader, LoaderOps;
pendingCount: INT ← 0;
pendingModules: PUBLIC LIST OF PendingModule;
list of people waiting for a MODULE to be exported
********************************************************************
Bind: Creates a binding between the dummy imports and actual interfaces, and
then fills in the links area of each module in the bcd.
********************************************************************
Bind: PUBLIC PROC[config: LoadState.ConfigID] RETURNS[unboundImports: IRList ← NIL] = {
bcd: BcdBase = LoadState.local.ConfigInfo[config].bcd;
binding: Binding;
BindModule: PROC[mth: MTHandle, mti: MTIndex] RETURNS[stop: BOOLFALSE] = {
frame: PrincOps.GlobalFrameHandle = LoadState.local.ModuleInfo[config, mth.gfi].gfh;
IF frame = NIL THEN RETURN; -- StartCedarCore disappears, for example
open the link space (only done once per module)
OpenLinkSpace[frame, mth, bcd];
import each element of the fragment
FOR i: NAT IN [0..LinkSegmentLength[mth, bcd]) DO
index: NAT;
interface: IR;
atom: ATOM;
name: Rope.Text;
bcdLink: Link;
link: PrincOps.ControlLink;
pending: LIST OF Pending;
bcdLink ← IthLink[mth, i, bcd];
initialize link
IF ~test THEN SELECT bcdLink.vtag FROM
var, type => WriteLink[i, PrincOps.NullLink];
proc0, proc1 => WriteLink[i, PrincOps.UnboundLink];
ENDCASE => ERROR;
is the link imported from within the bcd?
IF bcdLink.gfi < bcd.firstdummy THEN {
rgfi: PrincOps.GFTIndex = LoadState.local.ModuleInfo[config, bcdLink.gfi].gfh.gfi;
link ← SELECT bcdLink.vtag FROM
var => FindVariableLink[config, bcdLink.gfi, bcdLink].link,
proc0, proc1 =>
IF bcdLink = BcdDefs.UnboundLink
THEN PrincOps.UnboundLink -- the binder couldn't bind it.
ELSE LoadState.local.BuildProcDescUsingModule[config, bcdLink.gfi, bcdLink.ep],
WAS [procedure[gfi: rgfi, ep: bcdLink.ep, tag: TRUE]],
type => PrincOps.NullLink,
ENDCASE => ERROR;
WriteLink[i, link];
LOOP};
imports an interface or a module outside of the bcd
IF bcdLink.vtag # proc0 AND bcdLink.vtag # proc1 THEN ERROR;
atom ← binding[bcdLink.gfi].atom;
name ← Atom.GetPName[atom];
pending ← GetPendingList[atom];
interface ← binding[bcdLink.gfi].interface;
IF interface = NIL THEN { -- imported module (rare)
version: VersionStamp;
link ← GetModuleLink[atom];
IF test OR ~IsNullLink[link] THEN {WriteLink[i, link]; LOOP};
module hasn't been loaded yet. Put it on the list.
unboundImports ← CONS[[name, 0], unboundImports];
version ← NARROW[Atom.GetProp[atom, $version], REF VersionStamp]^;
pendingModules ← CONS[[frame, mth, bcd, i, name, version], pendingModules];
LOOP};
interface # NIL
index ← bcdLink.ep + (binding[bcdLink.gfi].whichgfi*ProcLimit);
link ← interface[index];
IF test OR ~IsNullLink[link] THEN {WriteLink[i, link]; LOOP};
entry hasn't been exported yet. Put it on the interface's pending list.
unboundImports ← CONS[[name, index], unboundImports];
--resolved ← FALSE;
pending ← GetPendingList[atom];
pendingCount ← pendingCount + 1;
pending ← CONS[[frame, mth, bcd, i, LOOPHOLE[index]], pending];
SetPendingList[atom, pending];
ENDLOOP;
all done, deactivate links area
DeactivateLinkSpace[frame];
}; -- BindModule
START Bind HERE
binding ← GetBinding[bcd]; -- binds dummy gfi's to IR's
[] ← BcdOps.ProcessModules[bcd, BindModule]; -- fill in the links areas
MakeLinksReadOnly[bcd, config]; -- done all at once for efficiency.
}; -- Bind
GetModuleLink: Whizzes thru the loadstate, looking at each module in each BCD, asking whether its name and version stamp match atom's PName and atom's $version property value (a REF VersionStamp), respectively. ASSUMES that the $version property exists for atom. If the desired module is found, GetModuleLink returns its GFH as a PrincOps.ControlLink. If not, GetModuleLink returns PrincOps.NullLink.
GetModuleLink: PUBLIC PROC[atom: ATOM] RETURNS[link: PrincOps.ControlLink ← PrincOps.NullLink] = {
whiz through the load state looking for this module
this is an expensive procedure!
name: Rope.Text ← Atom.GetPName[atom];
version: VersionStamp ← NARROW[Atom.GetProp[atom, $version], REF VersionStamp]^;
Find: PROC[config: LoadState.ConfigID] RETURNS[stop: BOOL] = {
bcd: BcdBase ← LoadState.local.ConfigInfo[config].bcd;
ftb: Table.Base = LOOPHOLE[bcd + bcd.ftOffset];
ssb: NameString = LOOPHOLE[bcd + bcd.ssOffset];
FindModule: PROC[mth: MTHandle, mti: MTIndex] RETURNS[stop: BOOLFALSE] = {
IF mth.file = FTSelf
THEN IF bcd.version # version THEN RETURN ELSE NULL
ELSE IF ftb[mth.file].version # version THEN RETURN;
Here if this module version matches the one associated as $version with atom
IF ssb.size[mth.name] # Rope.Length[name] THEN RETURN;
FOR i: NAT IN [0..ssb.size[mth.name]) DO
IF Rope.InlineFetch[name, i] # ssb.string.text[mth.name+i] THEN RETURN;
ENDLOOP;
Here if this module's name matches atom's PName:
get the module's GFH, assign it to "link"
link ← LOOPHOLE[LoadState.local.ModuleInfo[config, mth.gfi].gfh];
link is a GFH in this case
RETURN[stop: TRUE];
}; -- end FindModule
START Find HERE
[] ← BcdOps.ProcessModules[bcd, FindModule];
RETURN[link # PrincOps.NullLink];
}; -- end Find
START GetModuleLink HERE
[] ← LoadState.local.EnumerateConfigs[newestFirst, Find];
};
MakeLinksReadOnly: PROC[bcd: BcdBase, config: LoadState.ConfigID] = {
MakeReadOnly: PROC[mth: MTHandle, mti: MTIndex] RETURNS[stop: BOOLFALSE] = {
frame: PrincOps.GlobalFrameHandle = LoadState.local.ModuleInfo[config, mth.gfi].gfh;
IF frame # NIL AND frame.codelinks
THEN VM.MakeReadOnly[GetCodeLinksInterval[frame, mth, bcd].interval];
};
[] ← BcdOps.ProcessModules[bcd, MakeReadOnly];
};
********************************************************************
A Binding is a mapping between the dummy gfi's within the bcd and real interfaces.
The dummy gfi's are a coding trick to simulate interfaces. There are bcd.nDummies
gfi's starting at bcd.firstdummy. There can be more than one dummy gfi for an interface
since there can be at most 16 entries per gfi slot.
********************************************************************
GetBinding: PUBLIC PROC[bcd: BcdBase] RETURNS[binding: Binding] = {
create a binding between dummy gfi's and interfaces
ftb: Table.Base = LOOPHOLE[bcd + bcd.ftOffset];
ssb: NameString = LOOPHOLE[bcd + bcd.ssOffset];
BindImport: PROC[imph: IMPHandle, impi: IMPIndex] RETURNS[stop: BOOLFALSE] = {
size: NAT;
interface: IR;
atom: ATOM;
get the name of the interface
s: IO.STREAM = IO.ROS[];
FOR i: NAT IN [0..ssb.size[imph.name]) DO s.PutChar[ssb.string.text[imph.name+i]] ENDLOOP;
atom ← Atom.MakeAtom[s.RopeFromROS[]];
size ← IF imph.port = module THEN 0 ELSE imph.ngfi*ProcLimit;
interface ← GetIR[atom, ftb[imph.file].version, size].interface;
FOR i: NAT IN [0..imph.ngfi) DO
binding[imph.gfi + i] ← [whichgfi: i, atom: atom, interface: interface];
ENDLOOP;
};
binding ← NEW[BindingSequence[bcd.firstdummy+bcd.nDummies]];
[] ← BcdOps.ProcessImports[bcd, BindImport];
};
********************************************************************
procedures that manipulate the links area.
********************************************************************
linkSpace: VM.Interval; -- used if codelinks. Should enclose links area. set RO by CloseLinkSpace.
codelinks: BOOL;
writeable: BOOL;
links: LinksPtr;
LinksPtr: TYPE = LONG POINTER TO LinksRec;
LinksRec: TYPE = RECORD[SEQUENCE COMPUTED NAT OF PrincOps.ControlLink];
GetCodeLinksInterval: PROC [frame: PrincOps.GlobalFrameHandle, mth: MTHandle, bcd: BcdBase] RETURNS [links: LinksPtr, interval: VM.Interval] = {
linkSegmentLength: CARDINAL = LinkSegmentLength[mth, bcd];
p1, p2: INT;
links ← LOOPHOLE[PrincOpsUtils.Codebase[frame], LinksPtr] - linkSegmentLength;
p1 ← VM.PageNumberForAddress[links];
p2 ← VM.PageNumberForAddress[links + linkSegmentLength - 1];
interval ← [page: p1, count: p2-p1+1];
};
OpenLinkSpace: PUBLIC PROC [frame: PrincOps.GlobalFrameHandle, mth: MTHandle, bcd: BcdBase] = {
linkSegmentLength: CARDINAL = LinkSegmentLength[mth, bcd];
codelinks ← frame.codelinks;
IF codelinks
THEN [links, linkSpace] ← GetCodeLinksInterval[frame, mth, bcd]
ELSE links LOOPHOLE[LONG[frame], LinksPtr] - linkSegmentLength;
writeable ← FALSE; -- current links area stays readOnly as long as possible
};
LinkSegmentLength: PUBLIC PROC[mth: MTHandle, bcd: BcdBase] RETURNS[CARDINAL] = {
WITH mth: mth SELECT FROM
direct => RETURN[mth.length];
indirect => RETURN[LOOPHOLE[bcd + bcd.lfOffset, Table.Base][mth.links].length];
multiple => RETURN[LOOPHOLE[bcd + bcd.lfOffset, Table.Base][mth.links].length];
ENDCASE => ERROR;
};
IthLink: PUBLIC PROC[mth: MTHandle, i: CARDINAL, bcd: BcdBase] RETURNS[Link] = {
WITH mth: mth SELECT FROM
direct => RETURN[mth.frag[i]];
indirect => RETURN[LOOPHOLE[bcd + bcd.lfOffset, Table.Base][mth.links].frag[i]];
multiple => RETURN[LOOPHOLE[bcd + bcd.lfOffset, Table.Base][mth.links].frag[i]];
ENDCASE => ERROR;
};
WriteLink: PUBLIC PROC [offset: CARDINAL, link: PrincOps.ControlLink] = {
IF skipWrites THEN RETURN;
IF test THEN {
oldLink: PrincOps.ControlLink ← ReadLink[offset];
IF IsNullLink[oldLink] AND IsNullLink[link] THEN RETURN;
IF oldLink # link THEN errors ← errors + 1;
RETURN};
IF codelinks AND ~writeable THEN {
make the (code) links area writable (this is the first WriteLink after OpenLinkSpace)
writeable ← TRUE;
VM.MakeReadWrite[linkSpace];
};
links[offset] ← link;
};
ReadLink: PUBLIC PROC [offset: CARDINAL] RETURNS [link: PrincOps.ControlLink] = {
RETURN[links[offset]];
};
DeactivateLinkSpace: PROC[frame: PrincOps.GlobalFrameHandle] = {
IF frame.codelinks AND writeable THEN VM.Age[linkSpace];
};
CloseLinkSpace: PUBLIC PROC [frame: PrincOps.GlobalFrameHandle] = {
IF frame.codelinks AND writeable THEN VM.MakeReadOnly[linkSpace];
};
********************************************************************
testing and initialization
********************************************************************
errors: INT;
test, skipWrites: BOOLFALSE;
Test: PROC RETURNS[totalErrors: INT] = {RETURN[Enumerate[skip: FALSE]]};
Enumerate: PROC[skip: BOOL] RETURNS[totalErrors: INT] = {
proc: PROC [cid: ConfigID] RETURNS [stop: BOOL ← FALSE] = {
IF skip AND ~BcdUnresolved[cid] THEN RETURN;
[] ← Bind[cid];
};
LoadState.local.Acquire[exclusive];
errors ← 0;
IF skip THEN skipWrites ← TRUE ELSE test ← TRUE;
[] ← LoadState.local.EnumerateConfigs[order: oldestFirst, proc: proc];
LoadState.local.Release[];
IF skip THEN skipWrites ← FALSE ELSE test ← FALSE;
RETURN[errors];
};
TestLoad: PROC[file: LONG STRING] =
BEGIN
cap: File.Capability;
cm: PrincOps.ControlModule;
cap ← Directory.Lookup[file];
[cm, ] ← Instantiate[file: cap, offset: 1, codeLinks: TRUE];
Start[cm];
END;
[] ← Enumerate[skip: TRUE]; was used to initialize pending items; 5.0 bootfiles have no imports
} . . .