DIRECTORY
Basics USING[CompareCard, Comparison],
Breakpoint USING[Break, BreakpointProc, ClearBreakpoint, EnumerateBreakpoints, SetBreakpoint, Cant, CantClear, CantSet],
BreakWorldArchitecture USING[Address, AddressRep, WorldAccess, WorldAccessFromBreakWorld, BreakWorld, BreakWorldFromBreakWorldAddress, CreateBreakWorld, NewAddress, nullAddress, PatchAreaRep, Cant, WouldBlock],
CCTypes USING[CCError, CCErrorCase],
CirioBreakAccess USING[],
CirioNubAccess USING[FileEntry, GetFileEntry, GetInstructionSetAndOperatingSystem, Handle, LookupFileEntryByStemName, LookupMatchingSymEntryByName, LookupMatchingSymEntryByValue, LookupSymEntryByName, LookupSymEntryByValue, ModuleType, MonitoredCall, RaFromCi, Read32BitsAsCard, Read4Bytes, RemoteAddress, SymEntry, TextType, Write4Bytes],
CirioTargets,
IO,
MIPSArchitecture USING [MIPSAddress, MIPSContents, MIPSAddressFromTargetAddress, MIPSContentsFromTargetContents, TargetContentsFromMIPSContents, NullMIPSAddress],
RedBlackTree,
Rope USING[Concat, Equal, Length, ROPE, Substr],
SourceFileOps USING [FormatPosition, Position],
SPARCArchitecture USING[SPARCAddress, SPARCAddressFromTargetAddress, SPARCContents, SPARCContentsFromTargetContents, TargetContentsFromSPARCContents, NullSPARCAddress],
SystemInterface USING[ShowReport],
TargetArchitecture USING[Contents];
Breakpoints
maxJump: CARD ~ 2**23;
CirioBreakSet: TYPE = REF CirioBreakSetBody;
CirioBreakSetBody:
PUBLIC
TYPE =
MONITORED
RECORD[
nub: CirioNubAccess.Handle,
recentBreakIndex: CARD,
breakProc: BreakWorldArchitecture.Address,
breakProcDataSegment: BreakWorldArchitecture.Address,
debuggeeBreakWorld: BreakWorldArchitecture.BreakWorld,
afterLastSpacer: CARD,
spacers: RedBlackTree.Table--of Spacer--
];
A CirioBreakSet (or the debuggeeBreakWorld??) also plays the role of a debuggee for Peter
see [Palain-NFS]<rouge>peter>CirioBreaks>FakeCirioImpl.mesa for a sample implementation
Spacer: TYPE ~ REF SpacerPrivate;
SpacerPrivate: TYPE ~ RECORD [start, size: CARD];
CirioBreakClientData: TYPE = REF CirioBreakClientDataBody;
CirioBreakClientDataBody:
TYPE =
RECORD[
index: CARD,
cardAddress: CARD32,
stopAll: BOOLEAN, -- added for two kinds of breaks.
break: Breakpoint.Break,
breakSet: CirioBreakSet,
mesaPosition: SourceFileOps.Position];
QuaBreakSet:
PUBLIC
PROC [ra:
REF
ANY]
RETURNS [is:
BOOL, it: CirioBreakSet] ~ {
WITH ra
SELECT
FROM
x: CirioBreakSet => RETURN [TRUE, x];
ENDCASE => RETURN [FALSE, NIL];
};
breakProcName was llDebugger←P60
CreateCirioBreakSet:
PUBLIC
PROC[nub: CirioNubAccess.Handle, fileNameStem: Rope.
ROPE, breakProcName: Rope.
ROPE]
RETURNS[CirioBreakSet] =
BEGIN
breakProcAddr, breakProcDataSegment: CARD;
breaks: CirioBreakSet;
breakWorld: BreakWorldArchitecture.BreakWorld;
target: CirioTargets.Target ¬ NARROW[nub.target];
instruction set names
SPARC: Rope.ROPE ¬ "SPARC";
RS6000: Rope.ROPE ¬ "RS6000";
MIPSEL: Rope.ROPE ¬ "MIPSEL"; -- MIPS little endian
MIPSEB: Rope.ROPE ¬ "MIPSEB"; -- MIPS big endian
instrSet, opSys: Rope.ROPE;
[instrSet, opSys] ¬ CirioNubAccess.GetInstructionSetAndOperatingSystem[nub];
[breakProcAddr, breakProcDataSegment] ¬ FindNamedProcInNamedFile[nub, fileNameStem, breakProcName];
breaks ¬
NEW[CirioBreakSetBody¬
[nub: nub,
recentBreakIndex: 0,
breakProc: NIL,
breakProcDataSegment: NIL,
debuggeeBreakWorld: NIL,
afterLastSpacer: 0,
spacers: RedBlackTree.Create[SpacerGetKey, SpacerCompare] ]];
SELECT
TRUE
FROM
Rope.Equal[instrSet, SPARC],
Rope.Equal[instrSet,
RS6000] =>
{
breakWorld ¬
BreakWorldArchitecture.CreateBreakWorld[
name: target.instrSet,
peekContents: CirioDebuggeePeekContentsProc,
pokeContents: CirioDebuggeePokeContentsProc,
getProcAddress: CirioDebuggeeGetProcAddressProc,
getProcDataSegment: CirioDebuggeeGetDataSegmentProc,
getPatchArea: CirioDebuggeeGetPatchAreaProc,
monitoredCall: CirioDebuggeeMonitoredCallProc,
worldAccessData: breaks];
note: debuggeeData.breakProc is invalid, however, it won't be needed until after we fill it in below. (Because breaks is not yet publically available.)
};
Rope.Equal[instrSet, MIPSEL],
Rope.Equal[instrSet,
MIPSEB] =>
{
breakWorld ¬
BreakWorldArchitecture.CreateBreakWorld[
name: target.instrSet,
peekContents: MIPSCirioDebuggeePeekContentsProc,
pokeContents: MIPSCirioDebuggeePokeContentsProc,
getProcAddress: MIPSCirioDebuggeeGetProcAddressProc,
getProcDataSegment: MIPSCirioDebuggeeGetDataSegmentProc,
getPatchArea: MIPSCirioDebuggeeGetPatchAreaProc,
monitoredCall: MIPSCirioDebuggeeMonitoredCallProc,
worldAccessData: breaks];
};
ENDCASE => CCTypes.CCError[cirioError, "Unsupported instrSet for CirioBreakSet"];
breaks.breakProc ¬ IF breakProcAddr # 0 THEN BreakWorldArchitecture.NewAddress[breakWorld, LOOPHOLE[breakProcAddr]] ELSE BreakWorldArchitecture.nullAddress;
breaks.debuggeeBreakWorld ¬ breakWorld;
RETURN[breaks];
END;
BreakWorldFromBreakSet:
PUBLIC
PROC[breaks: CirioBreakSet]
RETURNS [BreakWorldArchitecture.BreakWorld] ~ {
breakWorld: BreakWorldArchitecture.BreakWorld ~ breaks.debuggeeBreakWorld;
RETURN [breakWorld];
};
ReportBreakInfo:
PROC[breaks: CirioBreakSet, clientBreak: CirioBreakClientData, clientMessage: Rope.
ROPE] =
{
symEntry: CirioNubAccess.SymEntry ¬ CirioNubAccess.LookupSymEntryByValue[breaks.nub, clientBreak.cardAddress, 0];
procName: Rope.ROPE ¬ IF symEntry = NIL THEN CCTypes.CCError[cirioError, "Bad args to LookupAddr"] ELSE symEntry.name;
SystemInterface.ShowReport[
IO.PutFLR[
"%g%g at %g = 0x%08x (abs), stopAll=%g.\n",
LIST[[rope[clientMessage]],
[cardinal[clientBreak.index]],
[rope[SourceFileOps.FormatPosition[clientBreak.mesaPosition]]],
[cardinal[clientBreak.cardAddress]],
[boolean[clientBreak.stopAll]]] ], $urgent];
};
ClearBreakAtAbsAddr:
PUBLIC
ENTRY
PROC[breaks: CirioBreakSet, cardAddress:
CARD32] =
BEGIN
ENABLE {
UNWIND => NULL;
Breakpoint.Cant => CCTypes.CCError[cirioError, IO.PutFR["Breakpoint.Cant[%g, %g] when trying to clear break at address %g", [atom[code]], [rope[message]], [cardinal[cardAddress]] ]];
Breakpoint.CantClear => CCTypes.CCError[cirioError, IO.PutFR["Breakpoint.CantClear[%g, %g] when trying to clear break at address %g", [atom[code]], [rope[message]], [cardinal[cardAddress]] ]];
};
breakWorld: BreakWorldArchitecture.BreakWorld ¬ breaks.debuggeeBreakWorld;
clientBreak: CirioBreakClientData ¬ NIL;
examineBreaks: Breakpoint.BreakpointProc =
PROCEDURE [clientData: ClientData] RETURNS [quit: BOOLEAN]
BEGIN
tentative: CirioBreakClientData ¬ NARROW[clientData];
IF tentative.cardAddress = cardAddress
THEN
{clientBreak ¬ tentative;
Breakpoint.ClearBreakpoint[clientBreak.break];
ReportBreakInfo[breaks, clientBreak, "Cleared break #"];
RETURN[TRUE]};
RETURN[FALSE];
END;
[] ¬ Breakpoint.EnumerateBreakpoints[breakWorld, examineBreaks];
IF clientBreak = NIL THEN
SystemInterface.ShowReport[IO.PutFR1["sorry, there is no break to clear at %g", IO.card[cardAddress]], $urgent];
IF clientBreak # NIL THEN
BEGIN
Breakpoint.ClearBreakpoint[clientBreak.break];
SystemInterface.ShowReport[IO.PutFR["break with index %g cleared at %g", IO.card[clientBreak.index], IO.card[clientBreak.cardAddress]]];
END
ELSE
SystemInterface.ShowReport[IO.PutFR["sorry, there is no break to clear at %g", IO.card[cardAddress]]];
END;
ClearBreakAtIndex:
PUBLIC
ENTRY
PROC[breaks: CirioBreakSet, index:
CARD] =
BEGIN
ENABLE {
UNWIND => NULL;
Breakpoint.Cant => CCTypes.CCError[cirioError, IO.PutFR["Breakpoint.Cant[%g, %g] when trying to clear break at index %g", [atom[code]], [rope[message]], [cardinal[index]] ]];
Breakpoint.CantClear => CCTypes.CCError[cirioError, IO.PutFR["Breakpoint.CantClear[%g, %g] when trying to clear break at index %g", [atom[code]], [rope[message]], [cardinal[index]] ]];
};
breakWorld: BreakWorldArchitecture.BreakWorld ¬ breaks.debuggeeBreakWorld;
clientBreak: CirioBreakClientData ¬ NIL;
examineBreaks: Breakpoint.BreakpointProc =
PROCEDURE [clientData: ClientData] RETURNS [quit: BOOLEAN]
BEGIN
tentative: CirioBreakClientData ¬ NARROW[clientData];
IF tentative.index = index
THEN
{clientBreak ¬ tentative;
Breakpoint.ClearBreakpoint[clientBreak.break];
ReportBreakInfo[breaks, clientBreak, "Cleared break #"];
RETURN[TRUE]};
RETURN[FALSE];
END;
[] ¬ Breakpoint.EnumerateBreakpoints[breakWorld, examineBreaks];
IF clientBreak = NIL THEN
SystemInterface.ShowReport[IO.PutFR1["sorry, there is no break with index %g", IO.card[index]], $urgent];
IF clientBreak # NIL THEN
BEGIN
Breakpoint.ClearBreakpoint[clientBreak.break];
SystemInterface.ShowReport[IO.PutFR["break with index %g cleared at %g", IO.card[clientBreak.index], IO.card[clientBreak.cardAddress]]];
END
ELSE
SystemInterface.ShowReport[IO.PutFR1["sorry, there is no break with index %g", IO.card[index]]];
END;
ClearAllBreaks:
PUBLIC
ENTRY
PROC[breaks: CirioBreakSet] =
BEGIN
ENABLE UNWIND => NULL;
breakWorld: BreakWorldArchitecture.BreakWorld ¬ breaks.debuggeeBreakWorld;
examineBreaks: Breakpoint.BreakpointProc =
PROCEDURE [clientData: ClientData] RETURNS [quit: BOOLEAN]
BEGIN
clientBreak: CirioBreakClientData ¬ NARROW[clientData];
Breakpoint.ClearBreakpoint[clientBreak.break !
Breakpoint.CantClear => {
SystemInterface.ShowReport[IO.PutFLR["Breakpoint.CantClear[%g, %g] when trying to clear break with index %g at %g", LIST[[atom[code]], [rope[message]], [cardinal[clientBreak.index]], [cardinal[clientBreak.cardAddress]]] ], $normal];
GOTO Givup};
Breakpoint.Cant => {
SystemInterface.ShowReport[IO.PutFLR["Breakpoint.Cant[%g, %g] when trying to clear break with index %g at %g", LIST[[atom[code]], [rope[message]], [cardinal[clientBreak.index]], [cardinal[clientBreak.cardAddress]]] ], $normal];
GOTO Givup}
];
SystemInterface.ShowReport[IO.PutFR["break with index %g cleared at %g", IO.card[clientBreak.index], IO.card[clientBreak.cardAddress]]];
ReportBreakInfo[breaks, clientBreak, "Cleared break #"];
RETURN[FALSE];
EXITS Givup => quit ¬ FALSE
END;
[] ¬ Breakpoint.EnumerateBreakpoints[breakWorld, examineBreaks
!Breakpoint.Cant => CCTypes.CCError[cirioError, "Can't enumerate breakpoints"]];
END;
BreakSetBroken:
PUBLIC
PROC [breaks: CirioBreakSet]
RETURNS [
BOOL] ~ {
RETURN [breaks.breakProc = BreakWorldArchitecture.nullAddress]};
ListBreaks:
PUBLIC
ENTRY
PROC[breaks: CirioBreakSet] =
BEGIN
ENABLE UNWIND => NULL;
breakWorld: BreakWorldArchitecture.BreakWorld ¬ breaks.debuggeeBreakWorld;
examineBreaks: Breakpoint.BreakpointProc =
PROCEDURE [clientData: ClientData] RETURNS [quit: BOOLEAN]
BEGIN
clientBreak: CirioBreakClientData ¬ NARROW[clientData];
ReportBreakInfo[breaks, clientBreak, "Break #"];
RETURN[FALSE];
END;
[] ¬ Breakpoint.EnumerateBreakpoints[breakWorld, examineBreaks
!Breakpoint.Cant => CCTypes.CCError[cirioError, "Can't enumerate breakpoints"]];
END;
SetBreakAtAbsAddr:
PUBLIC
ENTRY
PROC[breaks: CirioBreakSet, cardAddress:
CARD32, mesaPos: SourceFileOps.Position, stopAll:
BOOLEAN] =
BEGIN
ENABLE UNWIND => NULL;
stopAllDummy: CARD32 ¬ IF stopAll THEN 1 ELSE 0;
breakWorld: BreakWorldArchitecture.BreakWorld ¬ breaks.debuggeeBreakWorld;
worldAccess: BreakWorldArchitecture.WorldAccess ¬ BreakWorldArchitecture.WorldAccessFromBreakWorld[breakWorld];
clientData: CirioBreakClientData ¬
NEW[CirioBreakClientDataBody¬[
breaks.recentBreakIndex ¬ breaks.recentBreakIndex+1,
cardAddress,
stopAll,
NIL,
breaks,
mesaPos]];
address: BreakWorldArchitecture.Address ¬ BreakWorldArchitecture.NewAddress[breakWorld, LOOPHOLE[cardAddress]];
clientData.break ¬ Breakpoint.SetBreakpoint[address, clientData, breaks.breakProc, stopAllDummy !
Breakpoint.CantSet => CCTypes.CCError[cirioError, IO.PutFR["Breakpoint.CantSet[%g, %g] at %g", [atom[code]], [rope[message]], [cardinal[cardAddress]] ]];
Breakpoint.Cant => CCTypes.CCError[cirioError, IO.PutFR["Breakpoint.Cant[%g, %g] when trying to set break at %g", [atom[code]], [rope[message]], [cardinal[cardAddress]] ]]
];
ReportBreakInfo[breaks, clientData, "Set break #"];
END;
CirioDebuggeePeekContentsProc:
PROC[address: BreakWorldArchitecture.Address]
RETURNS [TargetArchitecture.Contents] =
BEGIN
ENABLE {
SPARCArchitecture.NullSPARCAddress => CCTypes.CCError[cirioError, "SPARCArchitecture.NullSPARCAddress"];
BreakWorldArchitecture.Cant => CCTypes.CCError[cirioError, IO.PutFR["BreakWorldArchitecture.Cant[%g, %g]", [atom[code]], [rope[message]] ]];
};
worldAccess: BreakWorldArchitecture.WorldAccess ¬ BreakWorldArchitecture.WorldAccessFromBreakWorld[BreakWorldArchitecture.BreakWorldFromBreakWorldAddress[address]];
breaks: CirioBreakSet ¬ NARROW[worldAccess.data];
sparcAddress: SPARCArchitecture.SPARCAddress ¬ SPARCArchitecture.SPARCAddressFromTargetAddress[address.address];
byteAddress: CARD32 ¬ LOOPHOLE[sparcAddress];
remoteAddress: CirioNubAccess.RemoteAddress ¬ [
breaks.nub,
byteAddress,
0,
FALSE,
TRUE];
remoteContents: PACKED ARRAY [0..3] OF BYTE ¬ CirioNubAccess.Read4Bytes[remoteAddress];
sparcContents: SPARCArchitecture.SPARCContents ¬ LOOPHOLE[remoteContents];
RETURN[SPARCArchitecture.TargetContentsFromSPARCContents[sparcContents]];
END;
MIPSCirioDebuggeePeekContentsProc:
PROC[address: BreakWorldArchitecture.Address]
RETURNS [TargetArchitecture.Contents] =
BEGIN
ENABLE {
MIPSArchitecture.NullMIPSAddress => CCTypes.CCError[cirioError, "MIPSArchitecture.NullMIPSAddress"];
BreakWorldArchitecture.Cant => CCTypes.CCError[cirioError, IO.PutFR["BreakWorldArchitecture.Cant[%g, %g]", [atom[code]], [rope[message]] ]];
};
worldAccess: BreakWorldArchitecture.WorldAccess ¬ BreakWorldArchitecture.WorldAccessFromBreakWorld[BreakWorldArchitecture.BreakWorldFromBreakWorldAddress[address]];
breaks: CirioBreakSet ¬ NARROW[worldAccess.data];
mipsAddress: MIPSArchitecture.MIPSAddress ¬ MIPSArchitecture.MIPSAddressFromTargetAddress[address.address];
byteAddress: CARD32 ¬ LOOPHOLE[mipsAddress];
remoteAddress: CirioNubAccess.RemoteAddress ¬ [
breaks.nub,
byteAddress,
0,
FALSE,
TRUE];
remoteContents: PACKED ARRAY [0..3] OF BYTE ¬ LOOPHOLE[CirioNubAccess.Read4Bytes[remoteAddress]];
mipsContents: MIPSArchitecture.MIPSContents ¬ LOOPHOLE[remoteContents];
RETURN[MIPSArchitecture.TargetContentsFromMIPSContents[mipsContents]];
END;
CirioDebuggeePokeContentsProc:
PROC[address: BreakWorldArchitecture.Address, contents: TargetArchitecture.Contents] =
BEGIN
ENABLE {
SPARCArchitecture.NullSPARCAddress => CCTypes.CCError[cirioError, "SPARCArchitecture.NullSPARCAddress"];
BreakWorldArchitecture.Cant => CCTypes.CCError[cirioError, IO.PutFR["BreakWorldArchitecture.Cant[%g, %g]", [atom[code]], [rope[message]] ]];
};
breakWorld: BreakWorldArchitecture.BreakWorld ¬ BreakWorldArchitecture.BreakWorldFromBreakWorldAddress[address];
worldAccess: BreakWorldArchitecture.WorldAccess ¬ BreakWorldArchitecture.WorldAccessFromBreakWorld[breakWorld];
breaks: CirioBreakSet ¬ NARROW[worldAccess.data];
sparcAddress: SPARCArchitecture.SPARCAddress ¬ SPARCArchitecture.SPARCAddressFromTargetAddress[address.address];
byteAddress: CARD32 ¬ LOOPHOLE[sparcAddress];
remoteAddress: CirioNubAccess.RemoteAddress ¬ [
breaks.nub,
byteAddress,
0,
FALSE,
TRUE];
sparcContents: SPARCArchitecture.SPARCContents ¬ SPARCArchitecture.SPARCContentsFromTargetContents[contents];
remoteContents: PACKED ARRAY [0..3] OF BYTE ¬ LOOPHOLE[sparcContents];
CirioNubAccess.Write4Bytes[remoteAddress, remoteContents];
END;
MIPSCirioDebuggeePokeContentsProc: PROC[address: BreakWorldArchitecture.Address, contents: TargetArchitecture.Contents] =
BEGIN
ENABLE {
MIPSArchitecture.NullMIPSAddress => CCTypes.CCError[cirioError, "MIPSArchitecture.NullMIPSAddress"];
BreakWorldArchitecture.Cant => CCTypes.CCError[cirioError, IO.PutFR["BreakWorldArchitecture.Cant[%g, %g]", [atom[code]], [rope[message]] ]];
};
breakWorld: BreakWorldArchitecture.BreakWorld ¬ BreakWorldArchitecture.BreakWorldFromBreakWorldAddress[address];
worldAccess: BreakWorldArchitecture.WorldAccess ¬ BreakWorldArchitecture.WorldAccessFromBreakWorld[breakWorld];
breaks: CirioBreakSet ¬ NARROW[worldAccess.data];
mipsAddress: MIPSArchitecture.MIPSAddress ¬ MIPSArchitecture.MIPSAddressFromTargetAddress[address.address];
byteAddress: CARD32 ¬ LOOPHOLE[mipsAddress];
remoteAddress: CirioNubAccess.RemoteAddress ¬ [
breaks.nub,
byteAddress,
0,
FALSE,
TRUE];
mipsContents: MIPSArchitecture.MIPSContents ¬ MIPSArchitecture.MIPSContentsFromTargetContents[contents];
remoteContents: PACKED ARRAY [0..3] OF BYTE ¬ LOOPHOLE[mipsContents];
CirioNubAccess.Write4Bytes[remoteAddress, remoteContents];
END;
CirioDebuggeeGetProcAddressProc:
PROC[breakWorld: BreakWorldArchitecture.BreakWorld, procName: Rope.
ROPE]
RETURNS [BreakWorldArchitecture.Address] =
BEGIN ENABLE BreakWorldArchitecture.Cant => CCTypes.CCError[cirioError, IO.PutFR["BreakWorldArchitecture.Cant[%g, %g]", [atom[code]], [rope[message]] ]];
worldAccess: BreakWorldArchitecture.WorldAccess ¬ BreakWorldArchitecture.WorldAccessFromBreakWorld[breakWorld];
breaks: CirioBreakSet ¬ NARROW[worldAccess.data];
symEntry: CirioNubAccess.SymEntry ¬ CirioNubAccess.LookupSymEntryByName[breaks.nub, procName, FALSE, FALSE, 0];
IF symEntry = NIL THEN CCTypes.CCError[cirioError, ""] ELSE RETURN[BreakWorldArchitecture.NewAddress[breakWorld, LOOPHOLE[symEntry.value]]];
END;
MIPSCirioDebuggeeGetProcAddressProc:
PROC[breakWorld: BreakWorldArchitecture.BreakWorld, procName: Rope.
ROPE]
RETURNS [BreakWorldArchitecture.Address] =
BEGIN ENABLE BreakWorldArchitecture.Cant => CCTypes.CCError[cirioError, IO.PutFR["BreakWorldArchitecture.Cant[%g, %g]", [atom[code]], [rope[message]] ]];
worldAccess: BreakWorldArchitecture.WorldAccess ¬ BreakWorldArchitecture.WorldAccessFromBreakWorld[breakWorld];
breaks: CirioBreakSet ¬ NARROW[worldAccess.data];
symEntry: CirioNubAccess.SymEntry ¬ CirioNubAccess.LookupSymEntryByName[breaks.nub, procName, FALSE, FALSE, 0];
IF symEntry = NIL THEN CCTypes.CCError[cirioError, ""] ELSE RETURN[BreakWorldArchitecture.NewAddress[breakWorld, LOOPHOLE[symEntry.value]]];
END;
CirioDebuggeeGetDataSegmentProc:
PROC[breakWorld: BreakWorldArchitecture.BreakWorld, address: BreakWorldArchitecture.Address]
RETURNS [BreakWorldArchitecture.Address] =
BEGIN ENABLE BreakWorldArchitecture.Cant => CCTypes.CCError[cirioError, IO.PutFR["BreakWorldArchitecture.Cant[%g, %g]", [atom[code]], [rope[message]] ]];
worldAccess: BreakWorldArchitecture.WorldAccess ¬ BreakWorldArchitecture.WorldAccessFromBreakWorld[breakWorld];
breaks: CirioBreakSet ¬ NARROW[worldAccess.data];
sparcAddress: SPARCArchitecture.SPARCAddress ¬ SPARCArchitecture.SPARCAddressFromTargetAddress[address.address];
symEntry: CirioNubAccess.SymEntry ¬ CirioNubAccess.LookupSymEntryByValue[breaks.nub, LOOPHOLE[sparcAddress], 0];
fileEntry: CirioNubAccess.FileEntry;
tocName: Rope.ROPE;
tocEntry: CirioNubAccess.SymEntry;
tocAddress: CARD;
IF symEntry = NIL THEN CCTypes.CCError[cirioError, ""];
fileEntry ¬ CirioNubAccess.GetFileEntry[breaks.nub, symEntry.fileSeqNum];
IF fileEntry = NIL THEN CCTypes.CCError[cirioError, ""];
tocName ¬ Rope.Substr[symEntry.name, 1, Rope.Length[symEntry.name] - 1];
tocEntry ¬ CirioNubAccess.LookupSymEntryByName[breaks.nub, tocName, FALSE, FALSE, 0];
IF tocEntry = NIL THEN RETURN[BreakWorldArchitecture.NewAddress[breakWorld, LOOPHOLE[fileEntry.dataReloc]]];
tocAddress ¬ CirioNubAccess.RaFromCi[breaks.nub, tocEntry.value, 32].Read32BitsAsCard;
RETURN[BreakWorldArchitecture.NewAddress[breakWorld, LOOPHOLE[tocAddress]]];
END;
MIPSCirioDebuggeeGetDataSegmentProc:
PROC[breakWorld: BreakWorldArchitecture.BreakWorld, address: BreakWorldArchitecture.Address]
RETURNS [BreakWorldArchitecture.Address] =
BEGIN ENABLE BreakWorldArchitecture.Cant => CCTypes.CCError[cirioError, IO.PutFR["BreakWorldArchitecture.Cant[%g, %g]", [atom[code]], [rope[message]] ]];
worldAccess: BreakWorldArchitecture.WorldAccess ¬ BreakWorldArchitecture.WorldAccessFromBreakWorld[breakWorld];
breaks: CirioBreakSet ¬ NARROW[worldAccess.data];
mipsAddress: MIPSArchitecture.MIPSAddress ¬ MIPSArchitecture.MIPSAddressFromTargetAddress[address.address];
symEntry: CirioNubAccess.SymEntry ¬ CirioNubAccess.LookupSymEntryByValue[breaks.nub, LOOPHOLE[mipsAddress], 0];
fileEntry: CirioNubAccess.FileEntry;
tocName: Rope.ROPE;
tocEntry: CirioNubAccess.SymEntry;
tocAddress: CARD;
IF symEntry = NIL THEN CCTypes.CCError[cirioError, ""];
fileEntry ¬ CirioNubAccess.GetFileEntry[breaks.nub, symEntry.fileSeqNum];
IF fileEntry = NIL THEN CCTypes.CCError[cirioError, ""];
tocName ¬ Rope.Substr[symEntry.name, 1, Rope.Length[symEntry.name] - 1];
tocEntry ¬ CirioNubAccess.LookupSymEntryByName[breaks.nub, tocName, FALSE, FALSE, 0];
IF tocEntry = NIL THEN RETURN[BreakWorldArchitecture.NewAddress[breakWorld, LOOPHOLE[fileEntry.dataReloc]]];
tocAddress ¬ CirioNubAccess.RaFromCi[breaks.nub, tocEntry.value, 32].Read32BitsAsCard;
RETURN[BreakWorldArchitecture.NewAddress[breakWorld, LOOPHOLE[tocAddress]]];
END;
CirioDebuggeeGetPatchAreaProc:
PROC[address: BreakWorldArchitecture.Address]
RETURNS [BreakWorldArchitecture.PatchAreaRep] = {
ENABLE {
SPARCArchitecture.NullSPARCAddress => CCTypes.CCError[cirioError, "SPARCArchitecture.NullSPARCAddress"];
BreakWorldArchitecture.Cant => CCTypes.CCError[cirioError, IO.PutFR["BreakWorldArchitecture.Cant[%g, %g]", [atom[code]], [rope[message]] ]];
BreakWorldArchitecture.WouldBlock => CCTypes.CCError[cirioError, IO.PutFR1["BreakWorldArchitecture.WouldBlock[%g]", [rope[message]] ]];
};
breakWorld: BreakWorldArchitecture.BreakWorld ¬ BreakWorldArchitecture.BreakWorldFromBreakWorldAddress[address];
worldAccess: BreakWorldArchitecture.WorldAccess ¬ BreakWorldArchitecture.WorldAccessFromBreakWorld[breakWorld];
breaks: CirioBreakSet ¬ NARROW[worldAccess.data];
sparcAddress: SPARCArchitecture.SPARCAddress ¬ SPARCArchitecture.SPARCAddressFromTargetAddress[address.address];
byteAddress: CARD32 ¬ LOOPHOLE[sparcAddress];
symEntry: CirioNubAccess.SymEntry ¬ CirioNubAccess.LookupSymEntryByValue[breaks.nub, byteAddress, 0];
patchAddress: BreakWorldArchitecture.Address ¬ NIL;
patchAreaRep: BreakWorldArchitecture.PatchAreaRep ¬ [NIL, 0];
key: Spacer;
TrySpacers:
PROC
RETURNS [{no, maybe, yes}] ~ {
raLeft, raAt, raRight: REF ANY;
spLeft, spAt, spRight: Spacer;
leftDist, rightDist: CARD ¬ CARD.LAST;
[raLeft, raAt, raRight] ¬ breaks.spacers.Lookup3[key];
spLeft ¬ NARROW[raLeft];
spAt ¬ NARROW[raAt];
spRight ¬ NARROW[raRight];
SELECT
TRUE
FROM
spAt#NIL, spLeft#NIL AND CARD[byteAddress-spLeft.start] < spLeft.size => CCTypes.CCError[cirioError, IO.PutFR1["trying to set breakpoint in a meadow! (at address 0x%x)", [cardinal[byteAddress]] ]];
spLeft=NIL AND spRight=NIL => RETURN[maybe];
ENDCASE => {
IF spLeft#NIL THEN leftDist ¬ byteAddress-spLeft.start;
IF spRight#NIL THEN rightDist ¬ spRight.start+spRight.size - byteAddress;
SELECT
TRUE
FROM
leftDist<=rightDist
AND NearEnough[byteAddress, spLeft.start]=>{
patchAddress ¬ NEW[BreakWorldArchitecture.AddressRep¬[breakWorld, LOOPHOLE[spLeft.start]]];
patchAreaRep ¬ [patchAddress, spLeft.size];
RETURN[yes]};
rightDist<=leftDist
AND NearEnough[byteAddress, spRight.start+spRight.size] => {
patchAddress ¬ NEW[BreakWorldArchitecture.AddressRep¬[breakWorld, LOOPHOLE[spRight.start]]];
patchAreaRep ¬ [patchAddress, spRight.size];
RETURN[yes]};
leftDist=CARD.LAST OR rightDist=CARD.LAST => RETURN [maybe];
ENDCASE => RETURN [no]};
};
IF symEntry #
NIL
THEN {
fileEntry: CirioNubAccess.FileEntry ¬ CirioNubAccess.GetFileEntry[breaks.nub, symEntry.fileSeqNum];
IF fileEntry.patchSize>0
THEN {
patchAddress ¬ NEW[BreakWorldArchitecture.AddressRep¬[breakWorld, LOOPHOLE[fileEntry.patchReloc]]];
patchAreaRep ¬ [patchAddress, fileEntry.patchSize];
RETURN[patchAreaRep]}};
key ¬ NEW[SpacerPrivate ¬ [byteAddress, 0]];
SELECT TrySpacers[]
FROM
yes => RETURN[patchAreaRep];
maybe => {
UpdateSpacers[breaks];
IF TrySpacers[]=yes THEN RETURN[patchAreaRep];
};
no => NULL;
ENDCASE => ERROR;
IF symEntry #
NIL
THEN {
lo, hi: CARD ¬ symEntry.fileSeqNum;
gohi: BOOL ¬ TRUE;
fileEntry: CirioNubAccess.FileEntry;
WHILE gohi
OR lo>1
DO
IF lo>1
THEN {
lo ¬ lo - 1;
fileEntry ¬ CirioNubAccess.GetFileEntry[breaks.nub, lo];
SELECT
TRUE
FROM
fileEntry.patchSize=0 => NULL;
NearEnough[fileEntry.patchReloc, byteAddress] => {
patchAddress ¬ NEW[BreakWorldArchitecture.AddressRep¬[breakWorld, LOOPHOLE[fileEntry.patchReloc]]];
patchAreaRep ¬ [patchAddress, fileEntry.patchSize];
RETURN[patchAreaRep]};
ENDCASE => lo ¬ 0;
};
IF gohi
THEN {
hi ¬ hi + 1;
fileEntry ¬ CirioNubAccess.GetFileEntry[breaks.nub, hi];
SELECT
TRUE
FROM
fileEntry.seqNum < hi => gohi ¬ FALSE;
fileEntry.patchSize=0 => NULL;
NearEnough[fileEntry.patchReloc+fileEntry.patchSize, byteAddress] => {
patchAddress ¬ NEW[BreakWorldArchitecture.AddressRep¬[breakWorld, LOOPHOLE[fileEntry.patchReloc]]];
patchAreaRep ¬ [patchAddress, fileEntry.patchSize];
RETURN[patchAreaRep]};
ENDCASE => gohi ¬ FALSE;
};
ENDLOOP;
CCTypes.CCError[cirioError, IO.PutFR1["no SymEntry or nearby patch space for address 0x%x", [cardinal[byteAddress]] ]]
};
MIPSCirioDebuggeeGetPatchAreaProc:
PROC[address: BreakWorldArchitecture.Address]
RETURNS [BreakWorldArchitecture.PatchAreaRep] = {
ENABLE {
MIPSArchitecture.NullMIPSAddress => CCTypes.CCError[cirioError, "MIPSArchitecture.NullMIPSAddress"];
BreakWorldArchitecture.Cant => CCTypes.CCError[cirioError, IO.PutFR["BreakWorldArchitecture.Cant[%g, %g]", [atom[code]], [rope[message]] ]];
BreakWorldArchitecture.WouldBlock => CCTypes.CCError[cirioError, IO.PutFR1["BreakWorldArchitecture.WouldBlock[%g]", [rope[message]] ]];
};
breakWorld: BreakWorldArchitecture.BreakWorld ¬ BreakWorldArchitecture.BreakWorldFromBreakWorldAddress[address];
worldAccess: BreakWorldArchitecture.WorldAccess ¬ BreakWorldArchitecture.WorldAccessFromBreakWorld[breakWorld];
breaks: CirioBreakSet ¬ NARROW[worldAccess.data];
mipsAddress: MIPSArchitecture.MIPSAddress ¬ MIPSArchitecture.MIPSAddressFromTargetAddress[address.address];
byteAddress: CARD32 ¬ LOOPHOLE[mipsAddress];
symEntry: CirioNubAccess.SymEntry ¬ CirioNubAccess.LookupSymEntryByValue[breaks.nub, byteAddress, 0];
patchAddress: BreakWorldArchitecture.Address ¬ NIL;
patchAreaRep: BreakWorldArchitecture.PatchAreaRep ¬ [NIL, 0];
key: Spacer;
TrySpacers:
PROC
RETURNS [{no, maybe, yes}] ~ {
raLeft, raAt, raRight: REF ANY;
spLeft, spAt, spRight: Spacer;
leftDist, rightDist: CARD ¬ CARD.LAST;
[raLeft, raAt, raRight] ¬ breaks.spacers.Lookup3[key];
spLeft ¬ NARROW[raLeft];
spAt ¬ NARROW[raAt];
spRight ¬ NARROW[raRight];
SELECT
TRUE
FROM
spAt#NIL, spLeft#NIL AND CARD[byteAddress-spLeft.start] < spLeft.size => CCTypes.CCError[cirioError, IO.PutFR1["trying to set breakpoint in a meadow! (at address 0x%x)", [cardinal[byteAddress]] ]];
spLeft=NIL AND spRight=NIL => RETURN[maybe];
ENDCASE => {
IF spLeft#NIL THEN leftDist ¬ byteAddress-spLeft.start;
IF spRight#NIL THEN rightDist ¬ spRight.start+spRight.size - byteAddress;
SELECT
TRUE
FROM
leftDist<=rightDist
AND NearEnough[byteAddress, spLeft.start]=>{
patchAddress ¬ NEW[BreakWorldArchitecture.AddressRep¬[breakWorld, LOOPHOLE[spLeft.start]]];
patchAreaRep ¬ [patchAddress, spLeft.size];
RETURN[yes]};
rightDist<=leftDist
AND NearEnough[byteAddress, spRight.start+spRight.size] => {
patchAddress ¬ NEW[BreakWorldArchitecture.AddressRep¬[breakWorld, LOOPHOLE[spRight.start]]];
patchAreaRep ¬ [patchAddress, spRight.size];
RETURN[yes]};
leftDist=CARD.LAST OR rightDist=CARD.LAST => RETURN [maybe];
ENDCASE => RETURN [no]};
};
IF symEntry #
NIL
THEN {
fileEntry: CirioNubAccess.FileEntry ¬ CirioNubAccess.GetFileEntry[breaks.nub, symEntry.fileSeqNum];
IF fileEntry.patchSize>0
THEN {
patchAddress ¬ NEW[BreakWorldArchitecture.AddressRep¬[breakWorld, LOOPHOLE[fileEntry.patchReloc]]];
patchAreaRep ¬ [patchAddress, fileEntry.patchSize];
RETURN[patchAreaRep]}};
key ¬ NEW[SpacerPrivate ¬ [byteAddress, 0]];
SELECT TrySpacers[]
FROM
yes => RETURN[patchAreaRep];
maybe => {
UpdateSpacers[breaks];
IF TrySpacers[]=yes THEN RETURN[patchAreaRep];
};
no => NULL;
ENDCASE => ERROR;
IF symEntry #
NIL
THEN {
lo, hi: CARD ¬ symEntry.fileSeqNum;
gohi: BOOL ¬ TRUE;
fileEntry: CirioNubAccess.FileEntry;
WHILE gohi
OR lo>1
DO
IF lo>1
THEN {
lo ¬ lo - 1;
fileEntry ¬ CirioNubAccess.GetFileEntry[breaks.nub, lo];
SELECT
TRUE
FROM
fileEntry.patchSize=0 => NULL;
NearEnough[fileEntry.patchReloc, byteAddress] => {
patchAddress ¬ NEW[BreakWorldArchitecture.AddressRep¬[breakWorld, LOOPHOLE[fileEntry.patchReloc]]];
patchAreaRep ¬ [patchAddress, fileEntry.patchSize];
RETURN[patchAreaRep]};
ENDCASE => lo ¬ 0;
};
IF gohi
THEN {
hi ¬ hi + 1;
fileEntry ¬ CirioNubAccess.GetFileEntry[breaks.nub, hi];
SELECT
TRUE
FROM
fileEntry.seqNum < hi => gohi ¬ FALSE;
fileEntry.patchSize=0 => NULL;
NearEnough[fileEntry.patchReloc+fileEntry.patchSize, byteAddress] => {
patchAddress ¬ NEW[BreakWorldArchitecture.AddressRep¬[breakWorld, LOOPHOLE[fileEntry.patchReloc]]];
patchAreaRep ¬ [patchAddress, fileEntry.patchSize];
RETURN[patchAreaRep]};
ENDCASE => gohi ¬ FALSE;
};
ENDLOOP;
CCTypes.CCError[cirioError, IO.PutFR1["no SymEntry or nearby patch space for address 0x%x", [cardinal[byteAddress]] ]]
};
NearEnough:
PROC [a, b:
CARD]
RETURNS [
BOOL] ~ {
SELECT
TRUE
FROM
a=b => RETURN [TRUE];
a<b => RETURN [b-a < 2**21];
a>b => RETURN [a-b < 2**21];
ENDCASE => ERROR};
UpdateSpacers:
PROC [breaks: CirioBreakSet] ~ {
se: CirioNubAccess.SymEntry;
target: CirioTargets.Target ¬ NARROW[breaks.nub.target];
highest: CARD ¬ 0;
some: BOOL ¬ FALSE;
FOR
se ¬
CirioNubAccess.LookupSymEntryByName[h: breaks.nub, sym: target.CNameToLoaderName[target, "SomePatchSpace"], caseSensitive: FALSE, externOnly: FALSE, numToSkip: 0],
CirioNubAccess.LookupMatchingSymEntryByName[h: breaks.nub, symID: se.symID, pattern: target.CNameToLoaderName[target, "SomePatchSpace"], caseSensitive: FALSE, wantedTypes: CirioNubAccess.TextType, ignoreClasses: 2--externals--, numToSkip: -1]
WHILE se#NIL AND se.value >= breaks.afterLastSpacer
DO
s: Spacer ~ NEW [SpacerPrivate ¬ [se.value, spacerSize]];
breaks.spacers.Insert[s, s];
highest ¬ MAX[highest, se.value];
some ¬ TRUE;
ENDLOOP;
IF some THEN breaks.afterLastSpacer ¬ MAX[breaks.afterLastSpacer, highest+spacerSize];
RETURN};
spacerSize: CARD ~ 8192--that's how much the source reserves--;
SpacerGetKey:
PROC [data:
REF
ANY]
RETURNS [
REF
ANY]
--RedBlackTree.GetKey--
~ {RETURN[data]};
SpacerCompare:
PROC [k, data:
REF
ANY]
RETURNS [Basics.Comparison]
--RedBlackTree.Compare-- ~ {
s1: Spacer ~ NARROW[k];
s2: Spacer ~ NARROW[data];
RETURN Basics.CompareCard[s1.start, s2.start]};
CirioDebuggeeMonitoredCallProc:
PROC[address: BreakWorldArchitecture.Address, proc:
PROCEDURE []
RETURNS []] =
BEGIN
ENABLE {
SPARCArchitecture.NullSPARCAddress => CCTypes.CCError[cirioError, "SPARCArchitecture.NullSPARCAddress"];
BreakWorldArchitecture.Cant => CCTypes.CCError[cirioError, IO.PutFR["BreakWorldArchitecture.Cant[%g, %g]", [atom[code]], [rope[message]] ]];
BreakWorldArchitecture.WouldBlock => CCTypes.CCError[cirioError, IO.PutFR1["BreakWorldArchitecture.WouldBlock[%g]", [rope[message]] ]];
};
breakWorld: BreakWorldArchitecture.BreakWorld ¬ BreakWorldArchitecture.BreakWorldFromBreakWorldAddress[address];
worldAccess: BreakWorldArchitecture.WorldAccess ¬ BreakWorldArchitecture.WorldAccessFromBreakWorld[breakWorld];
breaks: CirioBreakSet ¬ NARROW[worldAccess.data];
sparcAddress: SPARCArchitecture.SPARCAddress ¬ SPARCArchitecture.SPARCAddressFromTargetAddress[address.address];
byteAddress: CARD32 ¬ LOOPHOLE[sparcAddress];
remoteAddress: CirioNubAccess.RemoteAddress ¬ [
breaks.nub,
byteAddress,
0,
FALSE,
TRUE];
CirioNubAccess.MonitoredCall[remoteAddress, proc];
END;
MIPSCirioDebuggeeMonitoredCallProc:
PROC[address: BreakWorldArchitecture.Address, proc:
PROCEDURE []
RETURNS []] =
BEGIN
ENABLE {
MIPSArchitecture.NullMIPSAddress => CCTypes.CCError[cirioError, "MIPSArchitecture.NullMIPSAddress"];
BreakWorldArchitecture.Cant => CCTypes.CCError[cirioError, IO.PutFR["BreakWorldArchitecture.Cant[%g, %g]", [atom[code]], [rope[message]] ]];
BreakWorldArchitecture.WouldBlock => CCTypes.CCError[cirioError, IO.PutFR1["BreakWorldArchitecture.WouldBlock[%g]", [rope[message]] ]];
};
breakWorld: BreakWorldArchitecture.BreakWorld ¬ BreakWorldArchitecture.BreakWorldFromBreakWorldAddress[address];
worldAccess: BreakWorldArchitecture.WorldAccess ¬ BreakWorldArchitecture.WorldAccessFromBreakWorld[breakWorld];
breaks: CirioBreakSet ¬ NARROW[worldAccess.data];
mipsAddress: MIPSArchitecture.MIPSAddress ¬ MIPSArchitecture.MIPSAddressFromTargetAddress[address.address];
byteAddress: CARD32 ¬ LOOPHOLE[mipsAddress];
remoteAddress: CirioNubAccess.RemoteAddress ¬ [
breaks.nub,
byteAddress,
0,
FALSE,
TRUE];
CirioNubAccess.MonitoredCall[remoteAddress, proc];
END;
ModuleType: CARD = CirioNubAccess.ModuleType;
TextType:
CARD = CirioNubAccess.TextType;
These two constants come from /jaune/xrhome/DEVELOPMENT/INCLUDE/xr/IncrementalLoad.h
don't forget that the bottom bit should be ignored, as it is the "external" bit.
This procedure should be in LoadStateAccess!!!
The procedure name is as it would appear in mesa
NOTE: as soon as target worlds get the version 6 nub, then we should always find an appropriate fileEntry. So, we should change the logic of this code.
FindNamedProcInNamedFile:
PROC[nub: CirioNubAccess.Handle, fileStemName: Rope.
ROPE, procName: Rope.
ROPE]
RETURNS[
CARD,
CARD] =
BEGIN
target: CirioTargets.Target ¬ NARROW[nub.target];
loaderName: Rope.ROPE ¬ target.CNameToLoaderName[target, procName];
lookUpName: Rope.ROPE ¬ Rope.Concat[loaderName, "←P"];
lookUpNameSize: CARD ¬ Rope.Length[lookUpName];
moduleEntry: CirioNubAccess.SymEntry ¬ CirioNubAccess.LookupFileEntryByStemName[nub, fileStemName, 0];
fileEntry: CirioNubAccess.FileEntry;
tocName: Rope.ROPE;
tocEntry: CirioNubAccess.SymEntry;
tocAddress: CARD;
IF moduleEntry #
NIL
THEN
BEGIN
previousEntry: CirioNubAccess.SymEntry ¬ moduleEntry;
Blindly look for it first, this is for the interm MIPS version, jas.
fullName: Rope.ROPE ¬ Rope.Concat[loaderName, "←P60"];
entry: CirioNubAccess.SymEntry ¬ CirioNubAccess.LookupMatchingSymEntryByName[nub, 0, fullName, FALSE, TextType, 0, 0];
IF entry #
NIL
THEN
RETURN[entry.value, moduleEntry.value];
End of the interm stuff, jas.
DO
nextEntry: CirioNubAccess.SymEntry ¬ CirioNubAccess.LookupMatchingSymEntryByValue[nub, previousEntry.symID, moduleEntry.value, TextType, 0, 1];
Important remark: my understanding of LookupMatchingSymEntryByValue is that it ignores moduleEntry.value if previousEntry.symID # 0, hence we progress through successively higher valued entries, all greater or equal to the start of the file.
IF nextEntry = NIL THEN RETURN[0, 0]; -- no such proc in the file
IF nextEntry.value >= moduleEntry.value + moduleEntry.size THEN RETURN[0, 0]; -- no such proc in the file
IF Rope.Equal[Rope.Substr[nextEntry.name, 0, lookUpNameSize], lookUpName]
THEN {
fileEntry ¬ CirioNubAccess.GetFileEntry[nub, moduleEntry.fileSeqNum];
IF fileEntry = NIL THEN RETURN [0, 0];
tocName ¬ Rope.Substr[nextEntry.name, 1, Rope.Length[nextEntry.name] - 1];
tocEntry ¬ CirioNubAccess.LookupSymEntryByName[nub, tocName, FALSE, FALSE, 0];
IF tocEntry = NIL THEN RETURN[nextEntry.value, fileEntry.dataReloc];
tocAddress ¬ CirioNubAccess.RaFromCi[nub, tocEntry.value, 32].Read32BitsAsCard;
RETURN[nextEntry.value, tocAddress];
};
we are looking for an entry with a name of the form "←ProcName←Pxx"
previousEntry ¬ nextEntry;
ENDLOOP;
END
ELSE
-- we couldnt find the file, so look up the symbol blindly (for compatibility)
BEGIN
fullName: Rope.ROPE ¬ Rope.Concat[loaderName, "←P60"];
IF NOT Rope.Equal[procName, "CallDebugger"] THEN RETURN[0, 0]
ELSE
BEGIN
entry: CirioNubAccess.SymEntry ¬ CirioNubAccess.LookupMatchingSymEntryByName[nub, 0, fullName, FALSE, TextType, 0, 0];
IF entry = NIL THEN RETURN[0, 0]; -- no such proc
fileEntry ¬ CirioNubAccess.GetFileEntry[nub, entry.fileSeqNum];
IF fileEntry = NIL THEN RETURN [0, 0];
tocName ¬ Rope.Substr[entry.name, 1, Rope.Length[entry.name] - 1];
tocEntry ¬ CirioNubAccess.LookupSymEntryByName[nub, tocName, FALSE, FALSE, 0];
IF tocEntry = NIL THEN RETURN[entry.value, fileEntry.dataReloc];
tocAddress ¬ CirioNubAccess.RaFromCi[nub, tocEntry.value, 32].Read32BitsAsCard;
RETURN[entry.value, tocAddress];
END;
END;
END;