DIRECTORY Basics USING [Card16FromH, Card32FromF, Comparison, FWORD, HWORD], BasicTime USING [GMT, Now], CCTypes USING[CCError, CCErrorCase], CirioTypes USING [zeroBA], Commander USING [Register, CommandProc], CommanderOps USING [ArgumentVector, Parse], Convert USING [IntFromRope], IO, List USING[LORA, CompareProc, Sort], ObjectFiles USING[BracketPairKind, CNameOfStab, CreateParsed, DescribeModule, FileSegmentPC, GlobalVarLoc, MakeUnknownVarLoc, ReadInstruction, RopeForStabType, SimpleSeg, Stab, StabBody, StabType, UnreadableObjectFile, unspecdBitSize, VarLoc, VarLocBody, VersionStampInfo], ObjectFilesPrivate USING[AlterFunStabType, BracketConsumer, BracketPair, BracketPairBody, CheckStaticVar, FnConsumer, FunStabSetBody, GetSPOffsetType, GetTypeRefProcType, HeaderBody, InstallStaticVarsType, LineNumToPCMapBody, MemorySegmentInfo, ModuleBody, ModuleFromParsedAndPCProcType, ObjectFileFlavorBody, Parsed, ParsedBody, PCtoLineNumMapBody, ReadHeaderProcType, ReadInitialDataAsRope, ReadStab, RegisterObjectFileFlavor, SLineData, StabList, StabRange, StabSet, TranslationTable, TranslationTableBody, VarLocFromStabProcType], PBasics USING [BITAND, BITSHIFT], PFS USING [PathFromRope, StreamFromOpenFile, OpenFileFromStream], PFSNames USING [PATH], Random USING[ChooseInt, Create, RandomStream], Rope, SunADotOut, SystemInterface USING[CirioFile, CloseFileSet, CreateFileSet, FileSet, GetCirioFile, GetStreamForFile, ReleaseStreamForFile, ShowReport]; SunADotOutFiles: CEDAR MONITOR IMPORTS Basics, BasicTime, CCTypes, Commander, CommanderOps, Convert, IO, List, ObjectFiles, ObjectFilesPrivate, PBasics, PFS, Random, Rope, SystemInterface EXPORTS ObjectFiles, SunADotOut = BEGIN CCError: ERROR[case: CCTypes.CCErrorCase, msg: Rope.ROPE _ NIL] _ CCTypes.CCError; PATH: TYPE ~ PFSNames.PATH; Stab: TYPE = ObjectFiles.Stab; StabList: TYPE = ObjectFilesPrivate.StabList; StabBody: TYPE = ObjectFiles.StabBody; StabType: TYPE = ObjectFiles.StabType; StabRange: TYPE = ObjectFilesPrivate.StabRange; FileSegmentPC: TYPE = ObjectFiles.FileSegmentPC; VersionStampInfo: TYPE = ObjectFiles.VersionStampInfo; LBrac: BYTE = 0c0H; RBrac: BYTE = 0e0H; SLine: BYTE = 044H; Fun: BYTE = 024H; PSym: BYTE = 0a0H; LSym: BYTE = 080H; RSym: BYTE = 040H; STSym: BYTE = 026H; LCSym: BYTE = 028H; GSym: BYTE = 020H; Main: BYTE = 02aH; SO: BYTE = 064H; BIncl: BYTE = 082H; EIncl: BYTE = 0a2H; Excl: BYTE = 0c2H; SOL: BYTE = 084H; Parsed: TYPE = REF ParsedBody; ParsedBody: PUBLIC TYPE = ObjectFilesPrivate.ParsedBody; Module: TYPE = REF ModuleBody; ModuleBody: PUBLIC TYPE = ObjectFilesPrivate.ModuleBody; Header: TYPE = REF HeaderBody; HeaderBody: TYPE = ObjectFilesPrivate.HeaderBody; MemorySegmentInfo: TYPE ~ ObjectFilesPrivate.MemorySegmentInfo; StabSet: TYPE = ObjectFilesPrivate.StabSet; BracketPair: TYPE ~ ObjectFilesPrivate.BracketPair; BracketPairBody: TYPE ~ ObjectFilesPrivate.BracketPairBody; unspecdBitSize: CARD ~ ObjectFiles.unspecdBitSize; MOldsun2: CARD = 0; M68010: CARD = 1; M68020: CARD = 2; MSparc: CARD = 3; OMagic: CARD16 = 0407B; NMagic: CARD16 = 0410B; ZMagic: CARD16 = 0413B; PageSize: CARD32 = 02000H; OldPageSize: CARD32 = 00800H; WireHeader: TYPE = REF WireHeaderBody; WireHeaderBody: TYPE = MACHINE DEPENDENT RECORD[ dynamic: BYTE[0..0], toolversion: BYTE[0..127], machtype: BYTE, magic: Basics.HWORD, textSize: Basics.FWORD, iDataSize: Basics.FWORD, bssSize: Basics.FWORD, symsSize: Basics.FWORD, entryPoint: Basics.FWORD, trSize: Basics.FWORD, drSize: Basics.FWORD]; WireSTEntry: TYPE = REF WireSTEntryBody; WireSTEntryBody: TYPE = MACHINE DEPENDENT RECORD[ stringX(0: 0..31): Basics.FWORD, type(0: 32..39): BYTE, other(0: 40..47): BYTE, desc(0: 48..63): Basics.HWORD, value(0: 64..95): Basics.FWORD]; ReadHeader: ObjectFilesPrivate.ReadHeaderProcType ~ TRUSTED { wHeader: WireHeader _ NEW[WireHeaderBody]; nBytes: INT _ IO.UnsafeGetBlock[stream, [LOOPHOLE[@wHeader^], 0, BYTES[WireHeaderBody]]]; magic: CARD _ Basics.Card16FromH[wHeader.magic]; nBadMagic: BOOLEAN _ (magic # OMagic) AND (magic # NMagic)AND (magic # ZMagic); nPageSize: CARD _ IF wHeader.machtype = MOldsun2 THEN OldPageSize ELSE PageSize; textOffset: CARD _ IF wHeader.machtype = MOldsun2 THEN (IF magic = ZMagic THEN nPageSize ELSE BYTES[WireHeaderBody]) ELSE (IF magic = ZMagic THEN 0 ELSE BYTES[WireHeaderBody]); textSize: CARD _ Basics.Card32FromF[wHeader.textSize]; iDataSeg: MemorySegmentInfo _ [textOffset + textSize, Basics.Card32FromF[wHeader.iDataSize]]; textRelocSeg: MemorySegmentInfo _ [iDataSeg.byteOffset+iDataSeg.byteLength, Basics.Card32FromF[wHeader.trSize]]; dataRelocSeg: MemorySegmentInfo _ [textRelocSeg.byteOffset+textRelocSeg.byteLength, Basics.Card32FromF[wHeader.drSize]]; symsSeg: MemorySegmentInfo _ [dataRelocSeg.byteOffset+dataRelocSeg.byteLength, Basics.Card32FromF[wHeader.symsSize]]; textLoadOffset: CARD32 _ IF magic = ZMagic THEN 02000H ELSE 0; header: Header _ NEW[HeaderBody _ [ dynamic: wHeader.dynamic # 0, toolversion: wHeader.toolversion, machtype: wHeader.machtype, magic: magic, text: [textOffset, textSize], iData: iDataSeg, textReloc: textRelocSeg, dataReloc: dataRelocSeg, syms: symsSeg, debug: [0, 0], linno: [0, 0], textLoadOffset: textLoadOffset, bssSize: Basics.Card32FromF[wHeader.bssSize], entryPoint: Basics.Card32FromF[wHeader.entryPoint], nPageSize: nPageSize, nEntries: symsSeg.byteLength/BYTES[WireSTEntryBody], stringOffset: symsSeg.byteOffset+symsSeg.byteLength ]]; RETURN[header]; }; SunADotOutModuleFromParsedAndPC: ObjectFilesPrivate.ModuleFromParsedAndPCProcType ~ { IF whole = NIL THEN RETURN[NIL] ELSE { stabRange: StabRange _ AlternativeFindModuleStabRange[whole, spc.relPC]; RETURN [ModuleFromParsedInner[whole, stabRange]]; }; }; LineNumList: TYPE = REF LineNumListBody; LineNumListBody: TYPE = RECORD [nLines: CARD, lines: LIST OF REF ANY _ NIL]; CreateModule: PROC[whole: Parsed, stream: IO.STREAM, stabRange: StabRange _ [0, 0]] RETURNS[Module] = BEGIN module: Module _ NEW[ModuleBody]; firstSO: Stab; nStabs: CARD; lineNums: LineNumList; lineNums2: LineNumList; [firstSO, ] _ BasicReadStab[whole, stream, stabRange.first]; nStabs _ stabRange.count; lineNums _ NEW [LineNumListBody _ [0, NIL]]; lineNums2 _ NEW [LineNumListBody _ [0, NIL]]; module.whole _ whole; module.firstStabX _ stabRange.first; module.limitStabX _ stabRange.first+stabRange.count; module.firstPC _ firstSO.value; module.limitPC _ whole.header.text.byteLength; -- (bogus, but it will suffice for reasons given earlier) module.stabs _ NEW[StabSet[nStabs]]; {I: CARD _ 0; WHILE I y _ mid; pc < midStab.value => y _ mid; midStab.value = pc => RETURN[midStab.stabX]; midStab.value < pc => x _ mid; ENDCASE => ERROR; ENDLOOP; RETURN[x]; END; SearchFwdForPCStab: PROC[whole: Parsed, start: CARD, limit: CARD] RETURNS[StabIndexTypeAndValue] = BEGIN dbxLimit: CARD _ 0; -- tentative FOR x: CARD _ start, x+1 WHILE x < limit DO info: StabIndexTypeAndValue _ CheckStab[whole, x]; IF info.stabType = SO OR info.stabType = Fun OR info.stabType = SLine THEN RETURN[info]; IF info.stabType # Unspecified AND info.stabType # Invalid THEN dbxLimit _ x+1; ENDLOOP; IF limit = whole.stabLimit AND dbxLimit > start THEN whole.stabLimit _ dbxLimit; RETURN[[limit, Invalid, 0]]; END; CheckStab: ENTRY PROC[whole: Parsed, stabX: CARD] RETURNS[StabIndexTypeAndValue] = BEGIN ENABLE UNWIND => NULL; stream: IO.STREAM _ SystemInterface.GetStreamForFile[whole.file]; IO.SetIndex[stream, whole.header.syms.byteOffset+stabX*BYTES[WireSTEntryBody]]; TRUSTED{[] _ IO.UnsafeGetBlock[stream, [LOOPHOLE[@WireStabBuffer^], 0, BYTES[WireSTEntryBody]]]}; SystemInterface.ReleaseStreamForFile[whole.file, stream]; RETURN[[stabX, TypeFromData[WireStabBuffer.type], Basics.Card32FromF[WireStabBuffer.value]]]; END; SunADotOutVarLocFromStab: ObjectFilesPrivate.VarLocFromStabProcType ~ { header: Header ~ stab.module.whole.header; MkSegment: PROC [kind: ObjectFiles.SimpleSeg] RETURNS [ObjectFiles.VarLoc] ~ { segRope:Rope.ROPE; segBase: CARD ~ SELECT kind FROM text => 0, data => header.text.byteLength, bss => header.text.byteLength + header.iData.byteLength, ENDCASE => ERROR; IF stab.value < segBase THEN ObjectFiles.UnreadableObjectFile[IO.PutFR["value (%xH) less than segment base (%xH) for symbol %g in %g", [cardinal[stab.value]], [cardinal[segBase]], [rope[ObjectFiles.CNameOfStab[stab]]], [rope[ObjectFiles.DescribeModule[stab.module]]] ]]; SELECT kind FROM text => segRope _ "text"; data => segRope _ "data"; bss => segRope _ "bss"; ENDCASE => ERROR; RETURN [NEW [ObjectFiles.VarLocBody _ [ bitSize: IF stab.size#0 THEN stab.size*8 ELSE unspecdBitSize, where: fSegment[ [0, segRope], (stab.value - segBase)*8, stab.value + header.text.byteOffset - header.textLoadOffset] ]]]}; SELECT stab.stabType FROM Fun => RETURN MkSegment[text]; STSym => RETURN MkSegment[data]; LCSym => RETURN MkSegment[bss]; LSym, PSym => { byteOffset: INT ~ LOOPHOLE[stab.value]; vLB: ObjectFiles.VarLoc; IF stab.stabType=PSym AND stab.size>4 AND stab.size # LAST[CARD] THEN vLB _ NEW[ObjectFiles.VarLocBody _ [ bitSize: stab.size*8, where: indirect[ base: NEW[ObjectFiles.VarLocBody _ [ bitSize: 32, where: frame[bitOffset: 8*byteOffset]]], offset: CirioTypes.zeroBA]]] ELSE vLB _ NEW[ObjectFiles.VarLocBody _ [ bitSize: IF stab.size#0 THEN stab.size*8 ELSE unspecdBitSize, where: frame[bitOffset: 8*byteOffset]]]; RETURN[vLB]}; RSym => { sz: CARD ~ IF stab.size#LAST[CARD] AND stab.size#0 THEN stab.size*8 ELSE unspecdBitSize; vLB: ObjectFiles.VarLoc; IF stab.size>4 AND stab.size # LAST[CARD] THEN vLB _ NEW[ObjectFiles.VarLocBody _ [ bitSize: sz, where: indirect[ base: NEW[ObjectFiles.VarLocBody _ [ bitSize: 32, where: register[regNum: stab.value]]], offset: CirioTypes.zeroBA]]] ELSE vLB _ NEW[ObjectFiles.VarLocBody _ [ bitSize: sz, where: register[regNum: stab.value]]]; RETURN[vLB] }; GSym => RETURN [NEW [ObjectFiles.VarLocBody _ [ bitSize: IF stab.size#0 THEN stab.size*8 ELSE unspecdBitSize, where: namedCommon[Rope.Concat["_", ObjectFiles.CNameOfStab[stab]], 0, 0, FALSE, FALSE] ]]]; ENDCASE => RETURN ObjectFiles.MakeUnknownVarLoc[IO.PutFR["unrecognized stabType (%02xH) for %g", [cardinal[ORD[stab.stabType]]], [rope[ObjectFiles.CNameOfStab[stab]]] ]]; }; PCtoLineNumMap: TYPE = REF PCtoLineNumMapBody; PCtoLineNumMapBody: TYPE = ObjectFilesPrivate.PCtoLineNumMapBody; LineNumToPCMap: TYPE = REF LineNumToPCMapBody; LineNumToPCMapBody: TYPE = ObjectFilesPrivate.LineNumToPCMapBody; SLineData: TYPE = ObjectFilesPrivate.SLineData; InstallPCLineNumMaps: PROC[module: Module, lineNums, lineNums2: LineNumList] = { CompareByCLineNum: PROC[ref1: REF ANY, ref2: REF ANY] RETURNS [Basics.Comparison] = { info1: REF SLineData _ NARROW[ref1]; info2: REF SLineData _ NARROW[ref2]; IF info1.cLineNum < info2.cLineNum THEN RETURN[less]; IF info1.cLineNum > info2.cLineNum THEN RETURN[greater]; IF info1.parsedRelPC.relPC < info2.parsedRelPC.relPC THEN RETURN[less]; IF info1.parsedRelPC.relPC > info2.parsedRelPC.relPC THEN RETURN[greater]; RETURN[equal]; }; CompareByPC: PROC[ref1: REF ANY, ref2: REF ANY] RETURNS [Basics.Comparison] = { info1: REF SLineData _ NARROW[ref1]; info2: REF SLineData _ NARROW[ref2]; IF info1.parsedRelPC.relPC < info2.parsedRelPC.relPC THEN RETURN[less]; IF info1.parsedRelPC.relPC > info2.parsedRelPC.relPC THEN RETURN[greater]; IF info1.cLineNum < info2.cLineNum THEN RETURN[less]; IF info1.cLineNum > info2.cLineNum THEN RETURN[greater]; RETURN[equal]; }; sortedList: LIST OF REF ANY _ NIL; IF module.pcToLineNum # NIL OR module.lineNumToPC # NIL THEN { IF module.pcToLineNum = NIL OR module.lineNumToPC = NIL THEN ERROR; RETURN; }; sortedList _ List.Sort[lineNums.lines, CompareByCLineNum]; module.lineNumToPC _ NEW[LineNumToPCMapBody[lineNums.nLines]]; FOR I: INTEGER IN [0..lineNums.nLines) DO info: REF SLineData _ NARROW[sortedList.first]; module.lineNumToPC[I] _ info^; sortedList _ sortedList.rest; ENDLOOP; sortedList _ List.Sort[lineNums2.lines, CompareByPC]; module.pcToLineNum _ NEW[PCtoLineNumMapBody[lineNums2.nLines]]; FOR I: INTEGER IN [0..lineNums2.nLines) DO info: REF SLineData _ NARROW[sortedList.first]; module.pcToLineNum[I] _ info^; sortedList _ sortedList.rest; ENDLOOP; }; TypeFromData: PROC [data: BYTE] RETURNS [stabType: ObjectFiles.StabType] ~ { val: CARD _ data; stabType _ SELECT val FROM LBrac => LBrac, RBrac => RBrac, SLine => SLine, Fun => Fun, PSym => PSym, LSym => LSym, RSym => RSym, STSym => STSym, LCSym => LCSym, GSym => GSym, Main => Main, SO => SO, BIncl => BIncl, EIncl => EIncl, Excl => Excl, SOL => SOL, ENDCASE => Unspecified }; BasicReadStab: ENTRY PROC[whole: Parsed, stream: IO.STREAM, stabX: CARD, lineNum, lineNum2: LineNumList _ NIL] RETURNS[stab: Stab, numStabsRead: CARD] ~ { ENABLE UNWIND => NULL; stab _ NEW [StabBody]; [stab^, numStabsRead] _ ReadStabBody[whole, stream, stabX, lineNum, lineNum2]; RETURN }; previousStream: IO.STREAM _ NIL; cloneStream: IO.STREAM _ NIL; ReadStabBody: PROC [whole: Parsed, stream: IO.STREAM, stabX: CARD, lineNum, lineNum2: LineNumList _ NIL] RETURNS [stab: StabBody, numStabsRead: CARD] ~ { rope: Rope.ROPE _ "\\"; size, rLen, offset: CARD16 _ 0; value: CARD32; stabType: ObjectFiles.StabType; IO.SetIndex[stream, whole.header.syms.byteOffset+stabX*BYTES[WireSTEntryBody]]; TRUSTED{[] _ IO.UnsafeGetBlock[stream, [LOOPHOLE[@WireStabBuffer^], 0, BYTES[WireSTEntryBody]]]}; stabType _ TypeFromData[WireStabBuffer.type]; size _ Basics.Card16FromH[WireStabBuffer.desc]; value _ Basics.Card32FromF[WireStabBuffer.value]; IF stream#previousStream THEN { cloneStream_PFS.StreamFromOpenFile[openFile~PFS.OpenFileFromStream[stream]]; previousStream _ stream; }; rope _ ReadStringX[cloneStream, whole, Basics.Card32FromF[WireStabBuffer.stringX]]; rLen _ rope.Length[]; numStabsRead _ 1; IF ~rope.IsEmpty[] THEN WHILE rope.Fetch[rLen-1] = '\\ DO offset _ offset + 1; IO.SetIndex[stream, whole.header.syms.byteOffset+(stabX + offset)*BYTES[WireSTEntryBody]]; TRUSTED{[] _ IO.UnsafeGetBlock[stream, [LOOPHOLE[@WireStabBuffer^], 0, BYTES[WireSTEntryBody]]]}; rope _ rope.Substr[len:rLen-1].Concat[ReadStringX[cloneStream, whole, Basics.Card32FromF[WireStabBuffer.stringX]]]; rLen _ rope.Length[]; numStabsRead _ numStabsRead + 1; ENDLOOP; stab _ [ module: NIL, stabX: stabX, stabType: stabType, size: size, value: value, rope: rope ]; IF stab.stabType = SLine AND lineNum # NIL THEN { info: REF SLineData _ NEW[SLineData_[cLineNum: Basics.Card16FromH[WireStabBuffer.desc], parsedRelPC: [[1, ".text"], stab.value]]]; lineNum.lines _ CONS[info, lineNum.lines]; lineNum.nLines _ lineNum.nLines + 1; lineNum2.lines _ CONS[info, lineNum2.lines]; lineNum2.nLines _ lineNum2.nLines + 1; }; RETURN}; WireStabBuffer: WireSTEntry _ NEW[WireSTEntryBody]; ReadStringX: PROC[stream: IO.STREAM, whole: Parsed, stringX: CARD] RETURNS[rope: Rope.ROPE] ~ { IO.SetIndex[stream, whole.header.stringOffset+stringX]; rope _ ReadRope[stream]}; RopeBufferSize: CARD = 100; RopeBuffer: REF PACKED ARRAY [0..RopeBufferSize) OF CHAR _ NEW[PACKED ARRAY [0..RopeBufferSize) OF CHAR]; ReadRope: PROC[s: IO.STREAM] RETURNS[rope: Rope.ROPE] = BEGIN ENABLE UNWIND => NULL; rope: Rope.ROPE _ NIL; WHILE TRUE DO nChars: CARD _ RopeBufferSize; -- tentative rt: Rope.Text; TRUSTED{[] _ IO.UnsafeGetBlock[s, [LOOPHOLE[@RopeBuffer^], 0, RopeBufferSize]]}; FOR I: CARD IN [0..RopeBufferSize) DO IF RopeBuffer[I] = '\000 THEN {nChars _ I; EXIT}; ENDLOOP; rt _ Rope.NewText[nChars]; FOR I: CARD IN [0..nChars) DO rt[I] _ RopeBuffer[I] ENDLOOP; rope _ IF rope = NIL THEN (IF nChars = 0 THEN "" ELSE rt) ELSE (IF nChars = 0 THEN rope ELSE Rope.Concat[rope, rt]); IF nChars < RopeBufferSize THEN EXIT; ENDLOOP; RETURN[rope]; END; RandomTestFindStabRange: Commander.CommandProc = { args: CommanderOps.ArgumentVector _ CommanderOps.Parse[cmd]; path: PATH _ PFS.PathFromRope[args[1]]; fileSet: SystemInterface.FileSet _ SystemInterface.CreateFileSet[]; { ENABLE UNWIND => SystemInterface.CloseFileSet[fileSet]; file: SystemInterface.CirioFile _ SystemInterface.GetCirioFile[fileSet, path]; nextSeed: INT _ IF args.argc < 3 THEN 4466 ELSE Convert.IntFromRope[args[2]]; start: BasicTime.GMT _ BasicTime.Now[]; WHILE TRUE DO rs: Random.RandomStream _ Random.Create[seed: nextSeed]; whole: Parsed _ ObjectFiles.CreateParsed[file, "SunADotOut"]; IO.PutF[cmd.out, "beginning seed = %g at %g\N", IO.card[nextSeed], IO.time[start]]; FOR I: INT IN [0..100) DO pc: CARD _ Random.ChooseInt[rs, 0, whole.header.text.byteLength]; range: StabRange _ AlternativeFindModuleStabRange[whole, pc]; IO.PutF[cmd.out, "\Tpc = %g gives firstX: %g count: %g\N", IO.card[pc], IO.card[range.first], IO.card[range.count]]; ENDLOOP; ENDLOOP; }; SystemInterface.CloseFileSet[fileSet]; }; SADOScanModuleStructure: PROC [module: Module, perFn: ObjectFilesPrivate.FnConsumer] ~ { I: CARD _ 0; WHILE I < module.stabs.nStabs DO stab: Stab ¬ module.stabs[I]; IF stab.module = NIL THEN I _ I+1 ELSE SELECT stab.stabType FROM GSym, LCSym, STSym, LSym => I _ I + 1; Fun => {funStab: Stab ~ stab; firstLocal: Stab _ NIL; WHILE I+1 < module.stabs.nStabs DO I _ I + 1; stab _ module.stabs[I]; IF stab.module = NIL THEN LOOP; SELECT stab.stabType FROM PSym, RSym => NULL; STSym, LSym => IF BracketFollows[module, I] THEN {firstLocal _ stab; EXIT} ELSE {firstLocal _ NIL; EXIT}; LBrac => {firstLocal _ stab; EXIT}; Fun, GSym, LCSym => {firstLocal _ NIL; EXIT}; SLine, Invalid, Unspecified, Main, SO, BIncl, EIncl, Excl, SOL => NULL; ENDCASE => SystemInterface.ShowReport[IO.PutFR["Found %g stab while scanning the arguments for function %g in %g", [rope[ObjectFiles.RopeForStabType[stab.stabType]]], [rope[ObjectFiles.CNameOfStab[funStab]]], [rope[ObjectFiles.DescribeModule[module]]] ], $normal]; ENDLOOP; I _ perFn[funStab, firstLocal, I+module.firstStabX] - module.firstStabX}; SLine, Invalid, Unspecified, Main, SO, BIncl, EIncl, Excl, SOL => I _ I + 1; ENDCASE => { SystemInterface.ShowReport[IO.PutFR["Found %g stab while scanning in the global space of %g", [rope[ObjectFiles.RopeForStabType[stab.stabType]]], [rope[ObjectFiles.DescribeModule[module]]] ], $normal]; I _ I + 1}; ENDLOOP; RETURN}; BracketFollows: PROC [module: Module, J: CARD] RETURNS [BOOL] ~ { FOR J _ J+1, J+1 WHILE J < module.stabs.nStabs DO stab: Stab ¬ module.stabs[J]; IF stab.module = NIL THEN LOOP; SELECT stab.stabType FROM Fun, GSym, LCSym => RETURN [FALSE]; LBrac => RETURN [TRUE]; ENDCASE => NULL; ENDLOOP; RETURN [FALSE]}; SADOScanFnStructure: PROC [module: Module, funStab: Stab, firstLocal: Stab _ NIL, nextX: CARD, perParm: PROC [Stab] _ NIL, perBracket: ObjectFilesPrivate.BracketConsumer _ NIL] RETURNS [limitX, limitPc: CARD] ~ { K: CARD; more: BOOL _ TRUE; IF perParm#NIL THEN PassLocals[module, funStab, funStab.stabX+1, nextX, perParm]; IF firstLocal=NIL THEN limitX _ nextX ELSE limitX _ DoBrack[module, funStab, firstLocal, perBracket]; K _ limitX-module.firstStabX; WHILE K < module.stabs.nStabs DO --seek limitPC, and more bracket pairs. stab: Stab _ module.stabs[K]; IF stab.module = NIL THEN K _ K + 1 ELSE SELECT stab.stabType FROM Fun => RETURN [limitX, stab.value]; LBrac, RSym => IF more THEN {limitX _ DoBrack[module, funStab, stab, perBracket]; K _ limitX-module.firstStabX} ELSE K _ K + 1; STSym, LSym => IF more AND BracketFollows[module, K] THEN {limitX _ DoBrack[module, funStab, stab, perBracket]; K _ limitX-module.firstStabX} ELSE {K _ K + 1; more _ FALSE}; ENDCASE => K _ K + 1; ENDLOOP; RETURN [limitX, module.limitPC]}; DoBrack: PROC [module: Module, funStab, first: Stab, perSubBracket: ObjectFilesPrivate.BracketConsumer] RETURNS [limitX: CARD] ~ { IF perSubBracket#NIL THEN limitX _ perSubBracket[first] ELSE limitX _ SADOScanBktStructure[module, funStab, first, NIL, NIL].limitX; RETURN}; SADOScanBktStructure: PROC [module: Module, funStab, first: Stab, perLocal: PROC [Stab] _ NIL, perSubBracket: ObjectFilesPrivate.BracketConsumer _ NIL] RETURNS [limitX, firstPc, limitPc: CARD] ~ { I: CARD _ first.stabX - module.firstStabX; stab: Stab; FOR I _ I, I+1 WHILE I < module.stabs.nStabs DO stab ¬ module.stabs[I]; IF stab.module = NIL THEN LOOP; SELECT stab.stabType FROM STSym, LSym, RSym => IF perLocal#NIL THEN perLocal[stab]; LBrac => { J: CARD _ I+1; firstPc _ stab.value; WHILE J < module.stabs.nStabs DO stab ¬ module.stabs[J]; IF stab.module = NIL THEN J _ J+1 ELSE SELECT stab.stabType FROM RBrac => RETURN [J+1+module.firstStabX, firstPc, stab.value]; STSym, LSym, RSym, LBrac => J _ DoBrack[module, funStab, stab, perSubBracket] - module.firstStabX; SLine, Invalid, Unspecified, Main, SO, BIncl, EIncl, Excl, SOL => J _ J +1; Fun => {--Since we're debugging C, which has no nested functions, this shouldn't happen; if it does, we assume the bracket parsing has failed and re-synch with top level enumeration of functions SystemInterface.ShowReport[IO.PutFR["Found FUN stab after an LBRAC in function %g in %g", [rope[ObjectFiles.CNameOfStab[funStab]]], [rope[ObjectFiles.DescribeModule[module]]] ], $normal]; RETURN [J+module.firstStabX, firstPc, stab.value]}; ENDCASE => SystemInterface.ShowReport[IO.PutFR["Found %g stab after an LBRAC in function %g in %g", [rope[ObjectFiles.RopeForStabType[stab.stabType]]], [rope[ObjectFiles.CNameOfStab[funStab]]], [rope[ObjectFiles.DescribeModule[module]]] ], $normal]; ENDLOOP; SystemInterface.ShowReport[IO.PutFR["Missed an RBRAC in function %g in %g", [rope[ObjectFiles.CNameOfStab[funStab]]], [rope[ObjectFiles.DescribeModule[module]]] ], $normal]; RETURN[J+module.firstStabX, firstPc, module.limitPC]}; Fun => {--shouldn't happen, but ... SystemInterface.ShowReport[IO.PutFR["Found FUN stab before an LBRAC in function %g in %g", [rope[ObjectFiles.CNameOfStab[funStab]]], [rope[ObjectFiles.DescribeModule[module]]] ], $normal]; RETURN [I+module.firstStabX, stab.value, stab.value]}; SLine, Invalid, Unspecified, Main, SO, BIncl, EIncl, Excl, SOL => NULL; ENDCASE => SystemInterface.ShowReport[IO.PutFR["Found %g stab before an LBRAC in function %g in %g", [rope[ObjectFiles.RopeForStabType[stab.stabType]]], [rope[ObjectFiles.CNameOfStab[funStab]]], [rope[ObjectFiles.DescribeModule[module]]] ], $normal]; ENDLOOP; SystemInterface.ShowReport[IO.PutFR["Missed an LBRAC in function %g in %g", [rope[ObjectFiles.CNameOfStab[funStab]]], [rope[ObjectFiles.DescribeModule[module]]] ], $normal]; RETURN[I+module.firstStabX, module.limitPC, module.limitPC]}; PassLocals: PROC [module: Module, funStab: Stab, firstX, limitX: CARD, to: PROC [Stab]] ~ { FOR I: CARD IN [firstX - module.firstStabX .. limitX-module.firstStabX) DO stab: Stab _ module.stabs[I]; IF stab.module#NIL THEN SELECT stab.stabType FROM PSym, RSym, LSym, STSym => to[stab]; SLine, Invalid, Unspecified, Main, SO, BIncl, EIncl, Excl, SOL => NULL; ENDCASE => SystemInterface.ShowReport[IO.PutFR["Found %g stab in parms/locals of %g in %g", [rope[ObjectFiles.RopeForStabType[stab.stabType]]], [rope[ObjectFiles.CNameOfStab[funStab]]], [rope[ObjectFiles.DescribeModule[module]]] ], $normal]; ENDLOOP; RETURN}; SunADotOutGetTypeRef: ObjectFilesPrivate.GetTypeRefProcType ~ { typeRef: Rope.ROPE; lastChar: CHAR _ IO.PeekChar[sourceStream]; IF IsDigit[lastChar] THEN { typeRef _ IO.GetTokenRope[sourceStream, NumTok].token; RETURN[typeRef]} ELSE IF lastChar='( THEN DO lastChar _ IO.GetChar[sourceStream]; typeRef _ Rope.Concat[typeRef, Rope.FromChar[lastChar]]; IF lastChar = ') THEN RETURN[typeRef] ENDLOOP ELSE CCError[cirioError, IO.PutFR1["malformed type rep (starts with %q)", [character[lastChar]] ]]; }; IsDigit: PROC [c:CHAR] RETURNS [BOOL] = INLINE { RETURN [c IN ['0 .. '9]] }; NumTok: PROC [char: CHAR] RETURNS [IO.CharClass] ~ { SELECT char FROM IN ['0..'9] => RETURN [other]; ENDCASE => RETURN [break]}; ModuleFromParsedAndStabRange: PUBLIC PROC[whole: Parsed, stabRange: StabRange] RETURNS[Module] = { RETURN [ModuleFromParsedInner[whole, stabRange]]; }; ModuleFromParsedInner: PROC[whole: Parsed, stabRange: StabRange] RETURNS[Module] = { firstX, limitX: CARD; stream: IO.STREAM; IF stabRange.count = 0 THEN RETURN[NIL]; firstX _ stabRange.first; limitX _ stabRange.first+stabRange.count; stream _ SystemInterface.GetStreamForFile[whole.file]; { ENABLE UNWIND => { SystemInterface.ReleaseStreamForFile[whole.file, stream]; }; firstSO: Stab; firstPC: CARD; lag: LIST OF Module _ NIL; [firstSO, ] _ BasicReadStab[whole, stream, firstX]; -- rope field not filled firstPC _ firstSO.value; FOR modules: LIST OF Module _ whole.modules, modules.rest WHILE modules # NIL DO IF modules.first.firstPC = firstPC THEN -- we already have it { SystemInterface.ReleaseStreamForFile[whole.file, stream]; RETURN[modules.first] }; IF firstPC < modules.first.firstPC THEN -- we should have found it by now { newModule: Module _ CreateModule[whole, stream, stabRange]; cell: LIST OF Module _ LIST[newModule]; cell.rest _ modules; IF lag # NIL THEN lag.rest _ cell ELSE whole.modules _ cell; SystemInterface.ReleaseStreamForFile[whole.file, stream]; RETURN[newModule]; }; lag _ modules; ENDLOOP; { newModule: Module _ CreateModule[whole, stream, stabRange]; cell: LIST OF Module _ LIST[newModule]; IF lag # NIL THEN lag.rest _ cell ELSE whole.modules _ cell; SystemInterface.ReleaseStreamForFile[whole.file, stream]; RETURN[newModule]; }; }; }; SADOInstallStaticVars: ObjectFilesPrivate.InstallStaticVarsType ~ { IF NOT module.staticVarsInstalled THEN FOR x: CARD IN [module.firstStabX..module.limitStabX) DO stab: Stab _ ObjectFilesPrivate.ReadStab[module, x]; IF stab = NIL THEN LOOP; IF stab.stabType = Fun THEN EXIT; IF stab.stabType = STSym OR stab.stabType = LCSym THEN { IF module.versionStampStab = NIL THEN { gvi: ObjectFiles.GlobalVarLoc _ NIL; gvi _ NARROW[ObjectFilesPrivate.CheckStaticVar[stab, "versionStamp", TRUE]]; IF gvi # NIL THEN { dataRope: Rope.ROPE _ ObjectFilesPrivate.ReadInitialDataAsRope[module, gvi.fileByteOffset]; module.versionStampStab _ stab; module.versionStampInfo _ NEW[ObjectFiles.VersionStampInfo_[gvi, dataRope]]; LOOP; }; }; IF module.globalFrameStab = NIL THEN { gvi: ObjectFiles.GlobalVarLoc _ NIL; gvi _ NARROW[ObjectFilesPrivate.CheckStaticVar[stab, "globalframe", TRUE]]; IF gvi # NIL THEN { module.globalFrameStab _ stab; module.globalFrameGvl _ gvi; LOOP; }; }; }; ENDLOOP; module.staticVarsInstalled _ TRUE; }; SADOAlterFunStab: ObjectFilesPrivate.AlterFunStabType ~ {RETURN [module.funStabs[funStabX].stab]; }; SADOGetSPOffset: ObjectFilesPrivate.GetSPOffsetType ~ { entryRelativePC: CARD _ spc.relPC; inst0: CARD _ ObjectFiles.ReadInstruction[module, [spc.fSeg, spc.relPC]]; inst1: CARD _ ObjectFiles.ReadInstruction[module, [spc.fSeg, spc.relPC+4]]; inst2: CARD _ ObjectFiles.ReadInstruction[module, [spc.fSeg, spc.relPC+8]]; spOffset: INT _ SaveProtocol1[inst0, inst1, inst2]; IF spOffset = 0 THEN spOffset _ SaveProtocol2[inst0]; RETURN[spOffset]; }; SaveProtocol1: PROC[inst0, inst1, inst2: CARD] RETURNS[spOffset: INT] ~ { decoded0: DecodedSparcInstruction _ DecodeSparcInstruction[inst0]; decoded1: DecodedSparcInstruction _ DecodeSparcInstruction[inst1]; decoded2: DecodedSparcInstruction _ DecodeSparcInstruction[inst2]; IF decoded0.op # 0 THEN RETURN[0]; IF decoded0.op2 # 4 THEN RETURN[0]; IF decoded0.rd # 1 THEN RETURN[0]; -- this is %g1 IF decoded1.op # 2 THEN RETURN[0]; IF decoded1.op3 # 0 THEN RETURN[0]; IF decoded1.i # 1 THEN RETURN[0]; IF decoded1.rs1 # 1 THEN RETURN[0]; -- this is %g1 IF decoded1.rd # 1 THEN RETURN[0]; -- this is %g1 IF decoded2.op # 2 THEN RETURN[0]; IF decoded2.op3 # 74B THEN RETURN[0]; IF decoded2.i # 0 THEN RETURN[0]; IF decoded2.rs1 # 16B THEN RETURN[0]; -- this is %sp IF decoded2.rs2 # 1 THEN RETURN[0]; -- this is %g1 IF decoded2.rd # 16B THEN RETURN[0]; -- this is %sp spOffset _ LOOPHOLE[PBasics.BITSHIFT[decoded0.imm22, 10] + decoded1.simm13]; }; SaveProtocol2: PROC[inst0: CARD] RETURNS[spOffset: INT] ~ { decoded0: DecodedSparcInstruction _ DecodeSparcInstruction[inst0]; IF decoded0.op # 2 THEN RETURN[0]; IF decoded0.op3 # 74B THEN RETURN[0]; IF decoded0.i # 0 THEN RETURN[0]; IF decoded0.rs1 # 16B THEN RETURN[0]; -- this is %sp IF decoded0.rd # 16B THEN RETURN[0]; -- this is %sp spOffset _ decoded0.simm13; }; DecodedSparcInstruction: TYPE = RECORD[ op, disp30, rd, a, cond, op2, imm22, disp22, op3, rs1, i, asi, rs2, simm13, opf: CARD]; DecodeSparcInstruction: PROC[inst: CARD] RETURNS[DecodedSparcInstruction] ~ { RETURN[[ op: PBasics.BITAND[3, PBasics.BITSHIFT[inst, -30]], disp30: PBasics.BITAND[7777777777B, inst], rd: PBasics.BITAND[37B, PBasics.BITSHIFT[inst, -25]], a: PBasics.BITAND[1, PBasics.BITSHIFT[inst, -29]], cond: PBasics.BITAND[17B, PBasics.BITSHIFT[inst, -25]], op2: PBasics.BITAND[7, PBasics.BITSHIFT[inst, -22]], imm22: PBasics.BITAND[17777777B, inst], disp22: PBasics.BITAND[17777777B, inst], op3: PBasics.BITAND[77B, PBasics.BITSHIFT[inst, -19]], rs1: PBasics.BITAND[37B, PBasics.BITSHIFT[inst, -14]], i: PBasics.BITAND[1, PBasics.BITSHIFT[inst, -13]], asi: PBasics.BITAND[377B, PBasics.BITSHIFT[inst, -5]], rs2: PBasics.BITAND[37B, inst], simm13: PBasics.BITAND[17777B, inst], opf: PBasics.BITAND[777B, PBasics.BITSHIFT[inst, -5]]]]; }; transTable: ObjectFilesPrivate.TranslationTable _ NEW[ObjectFilesPrivate.TranslationTableBody[3]]; Commander.Register["sunadoRandomTestFindStabRange", RandomTestFindStabRange]; transTable[0] _ [2, 2, 263, "SunADotOut"]; transTable[1] _ [2, 2, 264, "SunADotOut"]; transTable[2] _ [2, 2, 267, "SunADotOut"]; ObjectFilesPrivate.RegisterObjectFileFlavor[NEW[ObjectFilesPrivate.ObjectFileFlavorBody _ [ "SunADotOut", SunADotOut, ReadHeader, SunADotOutModuleFromParsedAndPC, SunADotOutVarLocFromStab, SunADotOutGetTypeRef, NIL, SADOScanModuleStructure, SADOScanFnStructure, SADOScanBktStructure, TRUE, SADOInstallStaticVars, SADOAlterFunStab, SADOGetSPOffset ]], transTable]; END.. n SunADotOutFiles.mesa Copyright Ó 1990, 1991, 1992 by Xerox Corporation. All rights reserved. Modified from DotOAccessImpl.mesa Laurie Horton, June 24, 1992 6:10 pm PDT Philip James, February 10, 1992 1:42 pm PST Last tweaked by Mike Spreitzer January 21, 1993 5:33 pm PST Chauser, March 24, 1992 10:36 am PST Katsuyuki Komatsu April 1, 1992 3:32 pm PST Useful types Following types are defined in ObjectFiles some Stab types Following types are defined in ObjectFilesPrivate Following values are of global interest theContStab: Stab ~ NEW [StabBody _ [module: NIL, stabX: CARD.LAST, stringX: CARD32.LAST, type: BYTE.LAST, other: BYTE.LAST, desc: CARD16.LAST, value: CARD32.LAST, rope: "don't look at me, I'm a continuation of an earlier stab", dx: CARD.LAST]]; This stab is put in module.stabs[i] wherever continuation stabs would appear. The following computations are adapted from /usr/include/sys/exec.h, obtained by: REdit cartiff -f /usr/include/sys/exec.h The following computations are adapted from /usr/include/sun4/a.out.h, obtained by: REdit cartiff -f /usr/include/sun4/a.out.h The following record type was constructed based on page 1339 of the Sun Release 4.0 documentation (A.OUT documentation) PROC[stream: IO.STREAM] RETURNS[Header] = TRUSTED The following computations are adapted from /usr/include/sun4/a.out.h, obtained by: REdit cartiff -f /usr/include/sun4/a.out.h text segment is loaded into memory at nominal 0 + textLoadOffset the 02000H is the largest page size on certain machines, and allows the first page of memory to remain unmapped. Module We assume that there are dbx stabs located in the whole in the symbol table entry range [firstX..limitX). We assume that the first stab is a SO stab whose value is the first pc of the module. We assume that this or the next stab is a SO stab containing the file name. The first and limitX have presumably been obtained by other means. For example, at this writing the CirioNub will provide access to a table of pc ranges located in the target world. (In addition, we provide a rather crude mechanism below to use when the CirioNub approach is not available. (e.g., when examining a file that is not part of a PCedar load state.) We are unable to define a legitimate limitPC. We assume that when, later, an attempt is made to look up a pc in the bracket structure, we have already determined that that pc definitely belongs to this Module. Thus, we can use header.textSize as a (bogus) limitPC. PROC [whole: Parsed, spc: FileSegmentPC, moduleRope: ROPE _ NIL] RETURNS [Module] ~ { prepare the Module for a call on BuildBrackets lets read the all stabs now (WITH their ropes) (I did some timing tests on a Dorado for RopeImpl. It took about 6 seconds to perform this loop, including the allocates. On the other hand, it takes about 4 seconds to spin through the entries without allocating the stab records. So, since at some point I have to traverse all the entries to find the Fun stabs, I might as well allocate them.) (we also relocate the pc value of bracket stabs.) now lets finish up Finding Modules This procedure should only be used when examining a whole which is not in the load state. If we are given a pc in a running PCR, it is far faster to use procedures in LoadStateAccess (if supported!) to obtain the stab range for a Module. Those procedures use a table available from the nub to find the stab range, then produce the Module through a call on CreateModule. foo: IO.STREAM _ NIL; ENABLE PFS.Error => { foo _ PFS.StreamOpen[PFS.PathFromRope["/tmp/PJ.out"], create]; CONTINUE}; foo _ PFS.StreamOpen[PFS.PathFromRope["/tmp/PJ.out"], append]; IO.PutF[foo, "downLooping [%g .. 0]\n", IO.card[nominalX]]; IO.PutF[foo, "(%g)\tstabType:\t%g\n", IO.card[x], IO.rope[ObjectFiles.RopeForStabType[info.stabType]]]; IO.PutF[foo, "Bailing out because baseSO.stabType # SO\n"]; IO.Close[foo]; foo _ NIL; IO.PutF[foo, "upLooping [%g .. %g]\n", IO.card[nominalX], IO.card[whole.stabLimit]]; IO.PutF[foo, "(%g)\tstabType:\t%g\n", IO.card[x], IO.rope[ObjectFiles.RopeForStabType[info.stabType]]]; IO.PutF[foo, "good answer: [%g, %g]\n", IO.card[baseSO.stabX], IO.card[highDbx-baseSO.stabX+1]]; IO.Close[foo]; foo _ NIL; IO.PutF[foo, "bad answer [0, 0] because\n\tbaseSO.value\t%g\n\trelativePC\t%g\n\tmaxPC\t%g\nThese values aren't in descending order.", IO.card[baseSO.value], IO.card[relativePC], IO.card[maxPC]]; IO.Close[foo]; foo _ NIL; if successful, the value returned is somewhere within the range of stabXs for the desired Module. If unsuccessful, the value returned is whole.header.nEntries. We depend upon the fact that all pc containing dbx stabs for a given whole file occur before or after any other dbx stab. We do not require that the pc values be sorted within the block of stabs for a given whole. However, we require that all our pc values are greater than or less than a pc value from some other block of dbx stabs. now, we assert an invariant all SO dbx stabs to the left of or at x have a pc value < pc all SO dbx stabs to the right of or at y have a pc value > pc We start looking at start, and stop just before limit. Hence, if we fail to find a pc stab, then we return [limit, 0, 0] IF info.stabType > 1fH THEN dbxLimit _ x+1; PROC [stab: Stab] RETURNS [ObjectFiles.VarLoc] ~ { C Line-Number to Relative-PC maps sorted by PC sorted by Line Num Stabs MergeContinuationStabs: ENTRY PROC[module: Module, stabX: CARD] ~ { ENABLE UNWIND => NULL; stab: Stab _ module.stabs[stabX]; DO rlen: INT ~ stab.rope.Length[]; IF rlen>0 AND stab.rope.Fetch[rlen-1]='\\ THEN { s2: Stab ~ module.stabs[stabX+stab.dx]; IF s2.stabType=stab.stabType AND s2.value=stab.value AND s2.other=stab.other AND s2.desc=stab.desc THEN { stab.dx _ stab.dx.SUCC; stab.rope _ stab.rope.Substr[len: rlen-1].Concat[s2.rope]; } ELSE EXIT} ELSE EXIT; ENDLOOP; RETURN}; warning: does NOT relocate LBrac or RBrac stabs. Also: the module field is set to NIL; hack to avoid messing up the stream buffering all users should be in the monitor WARNING: the rope reading code causes random repositioning of the stream. That is CRUDE and expensive. Simply for this experiment. I need to do something better. Read in the whole string table? read in the symbols, sort them, then read in string table and pick out the interesting strings? What? symbol ropes we assume that the stream has been positioned at the beginning of a sequence of chars terminated by a '\000 char. usage: RandomTestFindStabRange /dir/.../dir/[sun4/]foo[.c2c].o Stab Structure PROC [sourceStream:IO.STREAM] RETURNS [Rope.ROPE] nest so that we can catch unwinds and release the stream first, see if we already have it PROC[module: Module] = this procedure examines all stabs up to the first Fun stab, looking for certain expected stabs. These are recorded in the whole. Following procedures compute the SP offset from FP by examining the entry protocol to a procedure. SPoffset = 0 means that the instructions do not form the expected protocol. protocol 1 is sethi %hi(Offset),%g1 add %g1,%lo(Offset),%g1 save %sp,%g1,%sp check for sethi %hi(spOffset),%g1 check for add %g1,%lo(spOffset),%g1 check for save %sp,%g1,%sp protocol 2 is save %sp, spOffset,%sp check for save %sp, spOffset, %sp Main code Ê)Ë•NewlineDelimiter ™codešœ™K™HK™!K™(K™+K™;K™$K™+—K˜šÏk ˜ Kšœœ(œœ˜BKšœ œœ˜Kšœœ˜$Kšœ œ ˜Kšœ œ˜(Kšœ œ˜+Kšœœ˜Kšœ˜Kšœœœ˜$Kšœ œ€˜‘Kšœœþ˜–Kšœœœœ˜!Kšœœ8˜AKšœ œœ˜Kšœœ"˜.Kšœ˜K˜ Kšœœt˜‰—K˜K˜K˜šÏnœœ˜Kšœ?œ2œ˜œKšœ˜—Kšœ˜K˜Kšžœœ&œœ˜RK˜™ Kšœœ œ˜—K˜™*K˜Kšœœ˜Kšœ œ˜-Kšœ œ˜&Kšœ œ˜&Kšœ œ ˜/Kšœœ˜0Kšœœ ˜6K˜Kšœ™K™Kšžœœ˜Kšžœœ˜Kšžœœ˜Kšžœœ˜Kšžœœ˜Kšžœœ˜Kšžœœ˜Kšžœœ˜Kšžœœ˜Kšžœœ˜Kšžœœ˜KšÐbkœœ˜Kšžœœ˜Kšžœœ˜Kšžœœ˜KšŸœœ˜K˜—K˜™1K™Kšœœœ ˜Kšœ œœ!˜8K˜Kšœœœ ˜Kšœ œœ!˜8K™Kšœœœ ˜Kšœ œ!˜1K˜Kšœœ(˜?K˜Kšœ œ˜+K˜Kšœ œ"˜3Kšœœ&˜;K˜—™'K™Kšœœ˜2K˜š!œœœ œœ œœœœ œœœœ œœGœœ™õK™M—K˜šœD™DKšœ5™5—K˜Kšžœœ˜Kšœœ˜Kšœœ˜Kšžœœ˜K˜Kšžœœ ˜Kšžœœ ˜Kšžœœ ˜K˜šœF™FKšœ7™7—K˜Kšžœœ ˜Kšž œœ ˜˜Kšœfœ™x—Kšœ œœ˜&š œœœ œœ˜0Kšœ œ˜Kšœ œ ˜Kšœ œ˜Kšœœ˜Kšœœ˜Kšœœ˜Kšœœ˜Kšœœ˜Kšœœ˜Kšœœ˜Kšœœ˜—K˜Kšœ œœ˜(š œœœ œœ˜1Kšœœ˜ Kšœœ˜Kšœœ˜Kšœœ˜Kšœœ˜ —K˜K˜šž œ*œ˜>Kš œ œœœ ™1Kšœœ˜*Kš œœœœœ˜YKšœœ%˜0Kšœ œœœ˜OšœF™FKšœ7™7—Kš œ œœœ œ ˜Pšœ œœ˜6Kš œœœ œœ˜=—šœ˜Kš œœœœœ˜6—Kšœ œ(˜6Kšœ]˜]Kšœp˜pKšœx˜xKšœu˜uš œœœœœ˜>Kšœ@™@Kšœp™p—šœœ˜#K˜K˜!K˜Kšœ ˜ Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜K˜Kšœ-˜-Kšœ3˜3Kšœ˜Kšœœ˜4Kšœ3˜3Kšœ˜—Kšœ ˜Kšœ˜——K˜K™™™K™iK™UK™KK™ëK™ŠK™—šžœ6˜UKšœ1œœœ ™UKš œ œœœœ˜$šœ˜KšœH˜HKšœ+˜1—Kšœ˜—K˜K˜K˜Kšœ œœ˜(Kšœœœ œ œœœœœ˜LK˜š ž œœœœ!œ ˜eKš˜Kšœœ ˜!Kšœ˜Kšœœ˜ Kšœ˜Kšœ˜Kšœ<˜Kšœ˜Kšœ˜Kšœ ˜ Kšœ%˜%Kšœ˜Kšœ˜Kšœ œ˜ K˜ Kšœ œ˜ Kšœœ˜K˜—Kšœ ˜Kšœ˜K™K˜——™K™Kš œœœœœ˜UKšžœ*˜C˜Kšœó™ó—Kšœœœœ™šžœœœœ˜cKš˜Kšœ˜šœœ ™Kšœœ œ&™>Kšœ™ —Kšœœ œ&™>š œ œœœ ˜'Kš˜Kšœ œ"˜0Kšœ;  ˜GKšœœ˜Kšœ œ˜Kšœ"œœ ˜8Kšœ&œ™;š œœ œœ˜*Kšœ2˜2Kšœ$œ œ3™gš œœœœ˜JKšœœ˜—Kšœœœ˜+Kšœœœœ˜DKšœ œ˜#Kšœ˜—šœœœ˜Kšœ9™;Kšœ ™Kšœœ™ Kšœ ˜—Kšœ%œœ™Tšœœœ˜-Kšœ2˜2Kšœ$œ œ3™gšœœ˜4Kšœœ˜—Kšœœœœ˜DKšœ œ˜#Kšœ˜—šœœœ˜š œžœœœ˜)Jšœœ œ˜/Jšœ˜Jšœ˜Jšœ˜—J˜Jšœ5˜5Kšœœ'˜?š œžœœœ˜*Jšœœ œ˜/Jšœ˜Jšœ˜Jšœ˜—Kšœ˜——K™™K™šžœœœœ™CKšœœœ™K™!š™Kšœœ™šœœœ™0Kšœ'™'š œœœœœ™iKšœœ™Kšœ:™:Kšœ™—Kšœœ™ —Kšœœ™ Kšœ™—Kšœ™K™—šž œœœœ%˜LKšœœ˜šœ œœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ ˜ Kšœ ˜ Kšœ ˜ Kšœ ˜ Kšœ˜Kšœ˜Kšœ ˜ Kšœ ˜ Kšœœ˜ Kšœ˜Kšœ˜Kšœ ˜ Kšœœ˜ Kšœ˜—K˜K˜—šž œœœœœ œ#œœœ˜šK˜Kšœ0™0K™%K™Kšœœœ˜Kšœœ ˜KšœN˜NKš˜Kšœ˜K˜—K™-Kšœœœœ˜ Kšœ œœœ˜K˜šž œœœœ œ#œœ œ˜™Kšœ œ˜Kšœœ˜Kšœœ˜Kšœ˜Kšœ5œ˜OKšœœœœ˜aKšœ-˜-K˜/Kšœ1˜1šœœ˜Kšœ œœ˜LK˜K˜—KšœS˜SK˜Kšœ˜šœ˜šœ˜!K˜šœ@œ˜ZKšœœœœ˜aKšœs˜sK˜Kšœ ˜ Kšœ˜———šœ˜Kšœœ˜ Kšœ ˜ Kšœ˜K˜ Kšœ ˜ Kšœ ˜ Kšœ˜—šœœ œœ˜1Kšœœ œi˜‚Kšœœ˜*Kšœ$˜$Kšœœ˜,Kšœ&˜&K˜—Kšœ˜—K˜šžœœ˜3KšÏt"™"—K˜K˜šž œœ œœœœ œ˜_Kšœ5˜7Kšœ˜KšœLœÕ™­—K˜K˜—™ K™Kšžœœ˜Kšž œœœœœœœœœœœ˜iK˜˜Kšœq™q—š žœœœœœ œ˜7Kš˜Kšœœœ˜Kšœ œœ˜šœœ˜ Kšœœ  ˜+K˜K˜Kšœœœ%˜PK˜š œžœœœ˜%Kšœœœ˜1Kšœ˜K˜—K˜Kš œžœœœ œœ˜<š œœœœœ œœ˜9Kšœœ œœ˜:—Kšœœœ˜%Kšœ˜—Kšœ˜ Kšœ˜—K™K™>šžœ˜1Kšœ˜Kšœ<˜˜QKšœ œœ˜%Kšœ;˜?Kšœ˜šœœ '˜HK˜Kšœœœ ˜#šœœ˜Kšœœ˜#šœœ˜šœ6˜:Kšœ˜—Kšœ ˜—šœœœ˜4šœ6˜:Kšœ˜—Kšœœ˜—Kšœ˜—Kšœ˜—Kšœ˜!K˜—šžœœ[œ œ˜‚Kšœœœ˜7Kšœ7œœ ˜LKšœ˜K˜—šžœœ2œ œ6œœœ˜ÄKšžœœ#˜*K˜ šœ œ˜/K˜Kšœœœœ˜šœ˜Kšœœ œœ˜9šœ ˜ Kšžœœ˜K˜šœ˜ K˜Kšœœœ˜!šœœ˜Kšœ œ.˜=Kšœb˜bKšœ#œœ ˜Kšœ º˜ÂKšœœž˜»Kšœ-˜3—KšœœÑ˜ù—Kšœ˜—Kšœœ˜­Kšœ0˜6—šœ ˜#KšœœŸ˜¼Kšœ0˜6—Kšœ#œœœ˜GKšœœÒ˜ú—Kšœ˜—Kšœœ˜­Kšœ7˜=K˜—šž œœ1œœ ˜[š œžœœœ:˜JK˜š œ œœœ˜1Kšœ$˜$Kšœ#œœœ˜GKšœœÉ˜ñ—Kšœ˜—Kšœ˜K˜—šžœ+˜?Kš œœœœœ™1K˜K˜Kšœ œœ˜+šœœ˜Kšœ œ*˜6Kšœ ˜—šœœ œ˜Kšœ œ˜$K˜8Kšœœœ ˜%Kš˜—KšœœH˜cK˜K˜š Ðbnœœœœœ˜%Kšœœœœ˜&K˜—š žœœœœœ˜4šœ˜Kšœ œ ˜Kšœœ ˜——K˜——˜šžœœœ&œ ˜`šœ˜Kšœ+˜1—Kšœ˜——˜K˜šžœœ&œ ˜Sšœ˜Kšœœ˜Kšœœœ˜K˜Kšœœœœ˜(Kšœ˜Kšœ)˜)Kšœ6˜6K˜™8Kšœ˜šœœ˜Kšœ9˜9K˜K˜—Kšœ˜Kšœ œ˜Kšœœœ œ˜Kšœ4 ˜LKšœ˜K™ š œ œœ&œ œ˜Pšœ!œ ˜=šœ˜Kšœ:˜:Kšœ˜Kšœ˜——šœ!œ !˜IKšœ˜Kšœ;˜;Kšœœœ œ ˜'K˜Kšœœœœ˜˜IKšœœ@˜KKšœœ@˜KKšœ œ&˜3Kšœœ!˜5Kšœ ˜Kšœ˜—˜Kšœ!œ œ0™bK™K—™™ K™K™K™——š ž œœœœ œ˜IKšœB˜BKšœB˜BKšœB˜B˜Kšœ#™#—Kšœœœ˜"Kšœœœ˜#KšœœœÐci˜2˜Kšœ%™%—Kšœœœ˜"Kšœœœ˜#Kšœœœ˜!Kšœœœ£˜2Kšœœœ£˜1˜K™—Kšœœœ˜"Kšœœœ˜%Kšœœœ˜!KšœœœÏi£˜4Kšœœœ£˜2Kšœœœ¤£˜3K˜Kšœ œ œ(˜LKšœ˜—˜™ Kšœ™——š ž œœœœ œ˜;KšœB˜B˜Kšœ$™$—Kšœœœ˜"Kšœœœ˜%Kšœœœ˜!Kšœœœ¤£˜4Kšœœœ¤£˜3K˜Kšœ˜Kšœ˜—K˜šœœœ˜'KšœQœ˜XK˜—šžœœœœ˜Mšœ˜Kšœ œ œ ˜3Kšœœ˜*Kšœ œœ ˜5Kšœ œ œ ˜2Kšœœœ ˜7Kšœ œ œ ˜4Kšœœ˜'Kšœœ˜(Kšœ œœ ˜6Kšœ œœ ˜6Kšœ œ œ ˜2Kšœ œœ ˜6Kšœ œ ˜Kšœœ˜%Kšœ œœ˜8—Kšœ˜K˜———K˜K™ ™Kšœ2œ-˜bKšœM˜MK˜*K˜*K˜*šœ,œ,˜[Kšœ ˜ Kšœ ˜ Kšœ ˜ Kšœ ˜ Kšœ˜Jšœ˜Kšœ˜J˜J˜J˜Jšœ˜K˜Kšœ˜K˜Kšœ˜Kšœ ˜ ——K™Kšœ˜—…—~2Äk