CedarLinkerImpl.mesa
fills in the links of a newly loaded bcd.
Last edited by: John Maxwell on: February 28, 1983 1:45 pm
Last edited by: Paul Rovner on: December 8, 1982 9:30 am
DIRECTORY
Atom USING [GetPName, GetProp],
AtomsPrivate USING [UnsafeMakeAtom],
BcdDefs USING [
EPLimit, FTSelf, IMPIndex, Link, MTIndex, MTRecord, UnboundLink, VersionStamp],
BcdOps USING [
BcdBase, IMPHandle, MTHandle, NameString, ProcessImports, ProcessModules],
CedarLinkerOps,
Directory USING [Lookup],
File USING [Capability, write],
Loader USING [Instantiate, Start, Error],
LoaderPrivate USING [FindMappedSpace],
RuntimeInternal USING [Codebase],
PilotLoaderOps USING [],
PilotLoadStateOps USING [
AcquireBcd, BcdUnresolved, ConfigIndex, EnumerateBcds, GetMap,
InputLoadState, Map, MapConfigToReal, ReleaseBcd,
ReleaseLoadState, ReleaseMap],
PrincOps USING [
ControlLink, ControlModule, GFTIndex,
GlobalFrameHandle, NullLink, UnboundLink],
PrincOpsRuntime USING [GetFrame, GFT],
Rope USING [Text],
Space USING [
Deactivate, GetHandle, GetWindow, Handle,
MakeReadOnly, MakeWritable, PageFromLongPointer],
Table USING [Base];
CedarLinkerImpl: PROGRAM
IMPORTS Atom, AtomsPrivate, BcdOps, CedarLinkerOps, Directory, Loader, LoaderPrivate, RuntimeInternal, PilotLoadStateOps, PrincOpsRuntime, Space
EXPORTS CedarLinkerOps, LoaderPrivate, PilotLoaderOps
SHARES File, Rope = BEGIN
OPEN BcdDefs, BcdOps, CedarLinkerOps;
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. 'map' maps the bcd-relative
gfi's to the real world.
********************************************************************
Bind: PUBLIC PROC[bcd: BcdBase, map: PilotLoadStateOps.Map]
RETURNS[resolved: BOOLEANTRUE] =
BEGIN
binding: Binding;
BindModule: PROC[mth: MTHandle, mti: MTIndex] RETURNS[stop: BOOLEANFALSE] =
BEGIN
frame: PrincOps.GlobalFrameHandle;
frame ← PrincOpsRuntime.GetFrame[PrincOpsRuntime.GFT[map[mth.gfi]]];
IF frame = NIL THEN RETURN; -- StartCedarCore disappears
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: BcdDefs.Link;
rgfi: PrincOps.GFTIndex;
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 ← map[bcdLink.gfi];
link ← SELECT bcdLink.vtag FROM
var => FindVariableLink[bcd, bcdLink, rgfi].link,
proc0, proc1 => IF bcdLink = BcdDefs.UnboundLink
THEN PrincOps.UnboundLink -- the binder couldn't bind it.
ELSE [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: BcdDefs.VersionStamp;
link ← GetModuleLink[atom];
IF test OR ~NullLink[link] THEN {WriteLink[i, link]; LOOP};
module hasn't been loaded yet. Put it on the list.
resolved ← FALSE;
name ← Atom.GetPName[atom];
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*EPLimit);
link ← interface[index];
IF test OR ~NullLink[link] THEN {WriteLink[i, link]; LOOP};
entry hasn't been exported yet. Put it on the interface's pending list.
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];
END;
START Bind HERE
binding ← GetBinding[bcd]; -- binds dummy gfi's to IR's
[] ← BcdOps.ProcessModules[bcd, BindModule]; -- fill in the links areas
MakeLinksReadOnly[bcd, map]; -- done all at once for efficiency.
END;
GetModuleLink: PUBLIC PROC[atom: ATOM]
RETURNS[link: PrincOps.ControlLink ← PrincOps.NullLink] =
BEGIN -- whiz through the load state looking for this module
this is an expensive procedure!
rgfi: PrincOps.GFTIndex ← 0;
name: Rope.Text ← Atom.GetPName[atom];
version: VersionStamp ← NARROW[Atom.GetProp[atom, $version], REF VersionStamp]^;
Find: PROC[config: PilotLoadStateOps.ConfigIndex] RETURNS[BOOLEAN] =
BEGIN
bcd: BcdBase ← PilotLoadStateOps.AcquireBcd[config];
ftb: Table.Base = LOOPHOLE[bcd + bcd.ftOffset];
ssb: BcdOps.NameString = LOOPHOLE[bcd + bcd.ssOffset];
FindModule: PROC[mth: MTHandle, mti: MTIndex] RETURNS[stop: BOOLEANFALSE] =
BEGIN
IF mth.file = BcdDefs.FTSelf
THEN IF bcd.version # version THEN RETURN ELSE NULL
ELSE IF ftb[mth.file].version # version THEN RETURN;
IF ssb.size[mth.name] # name.length THEN RETURN;
FOR i: NAT IN [0..ssb.size[mth.name]) DO
IF name.text[i] # ssb.string.text[mth.name+i] THEN RETURN;
ENDLOOP;
get the link
rgfi ← PilotLoadStateOps.MapConfigToReal[mth.gfi, config];
link ← LOOPHOLE[PrincOpsRuntime.GetFrame[PrincOpsRuntime.GFT[rgfi]]];
RETURN[stop: TRUE];
END;
START Find HERE
[] ← BcdOps.ProcessModules[bcd, FindModule];
PilotLoadStateOps.ReleaseBcd[bcd];
RETURN[rgfi # 0];
END;
START GetModuleLink HERE
[] ← PilotLoadStateOps.EnumerateBcds[recentfirst, Find];
END;
MakeLinksReadOnly: PROC[bcd: BcdBase, map: PilotLoadStateOps.Map] =
BEGIN
MakeReadOnly: PROC[mth: MTHandle, mti: MTIndex] RETURNS[stop: BOOLEANFALSE] =
BEGIN
frame: PrincOps.GlobalFrameHandle;
frame ← PrincOpsRuntime.GetFrame[PrincOpsRuntime.GFT[map[mth.gfi]]];
IF frame # NIL AND frame.codelinks THEN {
lp: LONG POINTER ← RuntimeInternal.Codebase[LOOPHOLE[frame]];
space: Space.Handle ← Space.GetHandle[Space.PageFromLongPointer[lp]];
Space.MakeReadOnly[space]};
END;
[] ← BcdOps.ProcessModules[bcd, MakeReadOnly];
END;
********************************************************************
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] =
BEGIN -- create a binding between dummy gfi's and interfaces
maxInterfaceNameLength: NAT = 40;
name: STRING = [maxInterfaceNameLength];
ftb: Table.Base = LOOPHOLE[bcd + bcd.ftOffset];
ssb: BcdOps.NameString = LOOPHOLE[bcd + bcd.ssOffset];
BindImport: PROC[imph: IMPHandle, impi: IMPIndex] RETURNS[stop: BOOLEANFALSE] =
BEGIN
size: NAT;
interface: IR;
atom: ATOM;
get the name of the interface
name.length ← 0;
FOR i: NAT IN [0..ssb.size[imph.name]) DO
m: STRING = "interface name too long";
IF name.length = maxInterfaceNameLength
THEN ERROR Loader.Error[type: invalidBcd, message: LOOPHOLE[LONG[m]]];
name[name.length] ← ssb.string.text[imph.name+i];
name.length ← name.length+1;
ENDLOOP;
size ← IF imph.port = module THEN 0 ELSE imph.ngfi*EPLimit;
atom ← AtomsPrivate.UnsafeMakeAtom[LOOPHOLE[LONG[name]]];
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;
END;
binding ← NEW[BindingSequence[bcd.firstdummy+bcd.nDummies]];
[] ← BcdOps.ProcessImports[bcd, BindImport];
END;
********************************************************************
procedures that manipulate the links area.
********************************************************************
linkSpace: Space.Handle;
long, writeable: BOOLEAN;
links: LONG POINTER TO ARRAY [0..0) OF PrincOps.ControlLink;
OpenLinkSpace: PUBLIC PROC [frame: PrincOps.GlobalFrameHandle, mth: BcdOps.MTHandle, bcd: BcdBase ← NIL] =
{IF frame.codelinks THEN {
long ← TRUE; links ← RuntimeInternal.Codebase[LOOPHOLE[frame]]}
ELSE {long ← FALSE; links ← LOOPHOLE[LONG[frame]]};
links ← links - LinkSegmentLength[mth, bcd];
writeable ← FALSE};
LinkSegmentLength: PUBLIC PROC[mth: BcdOps.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: BcdOps.MTHandle, i: CARDINAL, bcd: BcdBase]
RETURNS[BcdDefs.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 NullLink[oldLink] AND NullLink[link] THEN RETURN;
IF oldLink # link THEN errors ← errors + 1;
RETURN};
IF long AND ~writeable THEN
{cap: File.Capability;
writeable ← TRUE;
linkSpace ← Space.GetHandle[Space.PageFromLongPointer[links]];
cap ← Space.GetWindow[LoaderPrivate.FindMappedSpace[linkSpace]].file;
cap.permissions ← cap.permissions + File.write;
Space.MakeWritable[linkSpace, cap]};
links[offset] ← link};
ReadLink: PUBLIC PROC [offset: CARDINAL] RETURNS [link: PrincOps.ControlLink] =
{RETURN[links[offset]]};
DeactivateLinkSpace: PROC[frame: PrincOps.GlobalFrameHandle] = {
IF long AND writeable THEN Space.Deactivate[linkSpace]};
CloseLinkSpace: PUBLIC PROC [frame: PrincOps.GlobalFrameHandle] = {
IF long AND writeable THEN Space.MakeReadOnly[linkSpace]};
********************************************************************
testing and initialization
********************************************************************
errors: INT;
test, skipWrites: BOOLEANFALSE;
Test: PROC RETURNS[totalErrors: INT] = {RETURN[Enumerate[skip: FALSE]]};
Enumerate: PROC[skip: BOOLEAN] RETURNS[totalErrors: INT] =
BEGIN
OPEN PilotLoadStateOps;
map: Map;
bcd: BcdBase;
nbcds: ConfigIndex ← InputLoadState[];
errors ← 0;
IF skip THEN skipWrites ← TRUE ELSE test ← TRUE;
FOR i: ConfigIndex IN [0..nbcds) DO
IF skip AND ~BcdUnresolved[i] THEN LOOP;
bcd ← AcquireBcd[i];
map ← GetMap[i];
[] ← Bind[bcd, map];
ReleaseBcd[bcd];
ReleaseMap[map];
ENDLOOP;
ReleaseLoadState[];
IF skip THEN skipWrites ← FALSE ELSE test ← FALSE;
RETURN[errors];
END;
TestLoad: PROC[file: LONG STRING] =
BEGIN
cap: File.Capability;
cm: PrincOps.ControlModule;
cap ← Directory.Lookup[file];
[cm, ] ← Loader.Instantiate[file: cap, offset: 1, codeLinks: TRUE];
Loader.Start[cm];
END;
[] ← Enumerate[skip: TRUE]; -- initializes pending items
END . . .