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: BOOLEAN ← TRUE] =
BEGIN
binding: Binding;
BindModule:
PROC[mth: MTHandle, mti: MTIndex]
RETURNS[stop:
BOOLEAN ←
FALSE] =
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:
BOOLEAN ←
FALSE] =
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:
BOOLEAN ←
FALSE] =
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:
BOOLEAN ←
FALSE] =
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: BOOLEAN ← FALSE;
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 . . .