DIRECTORY Basics USING [BITAND, BITLSHIFT, BITSHIFT, Card16FromH, Card32FromF, Comparison, FWORD, HWORD, Int32FromF], 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 [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], PFS USING [PathFromRope, RopeFromPath, StreamFromOpenFile, OpenFileFromStream], PFSNames USING [PATH], Random USING[ChooseInt, Create, RandomStream], RMTWBackdoor USING [GetDotO], Rope, SystemInterface USING[CirioFile, CloseFileSet, CreateFileSet, FileSet, GetCirioFile, GetStreamForFile, ReleaseStreamForFile, ShowReport]; SunELFFiles: CEDAR MONITOR IMPORTS Basics, BasicTime, CCTypes, Commander, CommanderOps, Convert, IO, List, ObjectFiles, ObjectFilesPrivate, PFS, Random, RMTWBackdoor, Rope, SystemInterface EXPORTS ObjectFiles = BEGIN CCError: ERROR[case: CCTypes.CCErrorCase, msg: ROPE ¬ NIL] ¬ CCTypes.CCError; ROPE: TYPE ~ Rope.ROPE; 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; ElfMagic: CARD = (Basics.BITLSHIFT[177B, 24] + Basics.BITLSHIFT['E.ORD, 16] + Basics.BITLSHIFT['L.ORD, 8] + 'F.ORD); ElfClassNone: CARD = 0; ElfClass32: CARD = 1; ElfClass64: CARD = 2; ElfDataNone: CARD = 0; ElfData2LSB: CARD = 1; ElfData2MSB: CARD = 2; ETNone: CARD = 0; ETRelocatable: CARD = 1; ETExecutable: CARD = 2; ETDynamicLib: CARD = 3; ETCore: CARD = 4; EMNone: CARD = 0; EMM32: CARD = 1; EMSparc: CARD = 2; EM386: CARD = 3; EM68K: CARD = 4; EM88K: CARD = 5; EM486: CARD = 6; EM860: CARD = 7; EMMips: CARD = 8; EMS370: CARD = 9; SHTNull: CARD = 0; SHTProgBits: CARD = 1; SHTSymTab: CARD = 2; SHTStrTab: CARD = 3; SHTRela: CARD = 4; SHTHash: CARD = 5; SHTDynamic: CARD = 6; SHTNote: CARD = 7; SHTNoBits: CARD = 8; SHTRel: CARD = 9; SHTShLib: CARD = 10; SHTDynSym: CARD = 11; SHFWrite: CARD = 1; SHFAlloc: CARD = 2; SHFExecInstr: CARD = 4; STBLocal: CARD = 0; STBGlobal: CARD = 1; STBWeak: CARD = 2; STTNoType: CARD = 0; STTObject: CARD = 1; STTFunc: CARD = 2; STTSection: CARD = 3; STTFile: CARD = 4; RSparcNone: CARD = 0; RSparc8: CARD = 1; RSparc16: CARD = 2; RSparc32: CARD = 3; RSparcDisk8: CARD = 4; RSparcDisk16: CARD = 5; RSparcDisk32: CARD = 6; RSparcWDisp30: CARD = 7; RSparcWDisp22: CARD = 8; RSparcHi22: CARD = 9; RSparc22: CARD = 10; RSparc13: CARD = 11; RSparcLo10: CARD = 12; RSparcGot10: CARD = 13; RSparcGot13: CARD = 14; RSparcGot22: CARD = 15; RSparcPc10: CARD = 16; RSparcPc22: CARD = 17; RSparcWPlt30: CARD = 18; RSparcCopy: CARD = 19; RSparcGlobDat: CARD = 20; RSparcJmpSlot: CARD = 21; RSparcRelative: CARD = 22; RSparcUa32: CARD = 23; WireHeader: TYPE = REF WireHeaderBody; WireHeaderBody: TYPE = MACHINE DEPENDENT RECORD[ magic: Basics.FWORD, elfClass: BYTE, elfData: BYTE, elfVersion: BYTE, elfPad1: BYTE, elfPad2: PACKED ARRAY [8..15] OF BYTE, type: Basics.HWORD, machine: Basics.HWORD, version: Basics.FWORD, entry: Basics.FWORD, progHdrOffset: Basics.FWORD, sectHdrOffset: Basics.FWORD, flags: Basics.FWORD, elfHdrSize: Basics.HWORD, progHdrSize: Basics.HWORD, progHdrNum: Basics.HWORD, sectHdrSize: Basics.HWORD, sectHdrNum: Basics.HWORD, sectStrTblX: Basics.HWORD]; SectionList: TYPE = REF SectionListBody; SectionListBody: TYPE = RECORD[ list: SEQUENCE length: CARD OF SectionHeader]; SectionHeader: TYPE = REF SectionHeaderBody; SectionHeaderBody: TYPE = RECORD[ name: ROPE ¬ NIL, nameIndex: CARD32, type: CARD32, flags: CARD32, addr: CARD32, offset: CARD32, size: CARD32, link: CARD32, info: CARD32, alignment: CARD32, entrySize: CARD32]; WireSectionHeader: TYPE = REF WireSectionHeaderBody; WireSectionHeaderBody: TYPE = MACHINE DEPENDENT RECORD[ nameIndex: Basics.FWORD, type: Basics.FWORD, flags: Basics.FWORD, addr: Basics.FWORD, offset: Basics.FWORD, size: Basics.FWORD, link: Basics.FWORD, info: Basics.FWORD, alignment: Basics.FWORD, entrySize: Basics.FWORD]; WireStabEntry: TYPE = REF WireStabEntryBody; WireStabEntryBody: 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]; WireSymtabEntry: TYPE = REF WireSymtabEntryBody; WireSymtabEntryBody: TYPE = MACHINE DEPENDENT RECORD[ name(0: 0..31): Basics.FWORD, value(0: 32..63): Basics.FWORD, size(0: 64..95): Basics.FWORD, bind(0: 96..99): [0..15], type(0: 100..103): [0..15], other(0: 104..111): BYTE, sectionIndex(0: 112..127): Basics.HWORD]; SymtabRange: TYPE = RECORD[first, count: CARD]; SymtabBind: TYPE ~ { Local, Global, Weak }; SymtabType: TYPE ~ { NoType, Object, Func, Section, File }; WireRelocEntry: TYPE = REF WireRelocEntryBody; WireRelocEntryBody: TYPE = MACHINE DEPENDENT RECORD[ offset(0: 0..31): Basics.FWORD, sym(0: 32..55): [0..16777215], type(0: 56..63): BYTE, addend(0: 64..95): Basics.FWORD]; BitsOn: PROC [flags: CARD, mask: CARD] RETURNS [on: BOOL] ~ INLINE { RETURN [Basics.BITAND[flags, mask] # 0] }; IsDataSection: PROC [section: SectionHeader] RETURNS [BOOL] ~ INLINE { RETURN [(section.type = SHTProgBits OR section.type = SHTNoBits) AND BitsOn[section.flags, SHFAlloc] AND NOT BitsOn[section.flags, SHFExecInstr]] }; ReadHeader: ObjectFilesPrivate.ReadHeaderProcType ~ TRUSTED { wHeader: WireHeader ¬ NEW[WireHeaderBody]; nBytes: INT ¬ IO.UnsafeGetBlock[stream, [LOOPHOLE[@wHeader­], 0, BYTES[WireHeaderBody]]]; magic: CARD ¬ Basics.Card32FromF[wHeader.magic]; objType: CARD ¬ Basics.Card16FromH[wHeader.type]; machine: CARD ¬ Basics.Card16FromH[wHeader.machine]; nBadMagic: BOOLEAN ¬ (magic # ElfMagic) OR (wHeader.elfClass # ElfClass32) OR (wHeader.elfData # ElfData2MSB) OR ((objType # ETRelocatable) AND (objType # ETExecutable)) OR (machine # EMSparc); nPageSize: CARD ¬ 1000H; textSeg: MemorySegmentInfo ¬ [0, 0]; iDataSeg: MemorySegmentInfo ¬ [0, 0]; bssSeg: MemorySegmentInfo ¬ [0, 0]; symtabSeg: MemorySegmentInfo ¬ [0, 0]; symtabStrSeg: MemorySegmentInfo ¬ [0, 0]; symsSeg: MemorySegmentInfo ¬ [0, 0]; symsStrSeg: MemorySegmentInfo ¬ [0, 0]; textRelocSeg: MemorySegmentInfo ¬ [0, 0]; dataRelocSeg: MemorySegmentInfo ¬ [0, 0]; symsRelocSeg: MemorySegmentInfo ¬ [0, 0]; textLoadOffset: CARD32 ¬ 0; sectHdrOffset: CARD ¬ Basics.Card32FromF[wHeader.sectHdrOffset]; sectHdrNum: CARD ¬ Basics.Card16FromH[wHeader.sectHdrNum]; sectionList: SectionList ¬ NEW[SectionListBody[sectHdrNum]]; sectStrTblX: CARD ¬ Basics.Card16FromH[wHeader.sectStrTblX]; textIndex, dataIndex, bssIndex, symtabIndex, symsIndex: CARD ¬ LAST[CARD]; maxSymsSize: CARD ¬ 0; header: Header; IO.SetIndex[stream, sectHdrOffset]; FOR i: CARD IN [0..sectHdrNum) DO wSectionHeader: WireSectionHeader ¬ NEW[WireSectionHeaderBody]; nBytes: INT ¬ IO.UnsafeGetBlock[stream, [LOOPHOLE[@wSectionHeader­], 0, BYTES[WireSectionHeaderBody]]]; sectionList[i] ¬ NEW[SectionHeaderBody]; sectionList[i].nameIndex ¬ Basics.Card32FromF[wSectionHeader.nameIndex]; sectionList[i].type ¬ Basics.Card32FromF[wSectionHeader.type]; sectionList[i].flags ¬ Basics.Card32FromF[wSectionHeader.flags]; sectionList[i].addr ¬ Basics.Card32FromF[wSectionHeader.addr]; sectionList[i].offset ¬ Basics.Card32FromF[wSectionHeader.offset]; sectionList[i].size ¬ Basics.Card32FromF[wSectionHeader.size]; sectionList[i].link ¬ Basics.Card32FromF[wSectionHeader.link]; sectionList[i].info ¬ Basics.Card32FromF[wSectionHeader.info]; sectionList[i].alignment ¬ Basics.Card32FromF[wSectionHeader.alignment]; sectionList[i].entrySize ¬ Basics.Card32FromF[wSectionHeader.entrySize]; ENDLOOP; FOR i: CARD IN [0..sectHdrNum) DO IO.SetIndex[stream, sectionList[sectStrTblX].offset+sectionList[i].nameIndex]; sectionList[i].name ¬ ReadRope[stream]; SELECT TRUE FROM (sectionList[i].type = SHTProgBits) AND BitsOn[sectionList[i].flags, SHFAlloc] => { IF NOT BitsOn[sectionList[i].flags, SHFWrite] THEN { IF textIndex = LAST[CARD] THEN { textSeg ¬ [sectionList[i].offset, sectionList[i].size]; textIndex ¬ i; } ELSE textSeg.byteLength ¬ textSeg.byteLength + sectionList[i].size; IF objType = ETRelocatable THEN sectionList[i].addr ¬ sectionList[i].offset - textSeg.byteOffset; } ELSE { IF dataIndex = LAST[CARD] THEN { iDataSeg ¬ [sectionList[i].offset, sectionList[i].size]; dataIndex ¬ i; } ELSE iDataSeg.byteLength ¬ iDataSeg.byteLength + sectionList[i].size; IF objType = ETRelocatable THEN sectionList[i].addr ¬ sectionList[i].offset - iDataSeg.byteOffset; }; }; (sectionList[i].type = SHTNoBits) AND BitsOn[sectionList[i].flags, SHFWrite] => { IF bssIndex = LAST[CARD] THEN { bssSeg ¬ [sectionList[i].offset, sectionList[i].size]; bssIndex ¬ i; } ELSE bssSeg.byteLength ¬ bssSeg.byteLength + sectionList[i].size; IF objType = ETRelocatable THEN sectionList[i].addr ¬ sectionList[i].offset - bssSeg.byteOffset; }; (sectionList[i].type = SHTSymTab) => { IF symtabIndex = LAST[CARD] THEN { symtabSeg ¬ [sectionList[i].offset, sectionList[i].size]; symtabIndex ¬ i; IF sectionList[i].entrySize # BYTES[WireSymtabEntryBody] THEN ERROR; symtabStrSeg ¬ [sectionList[sectionList[i].link].offset, sectionList[sectionList[i].link].size]; }; }; (sectionList[i].type = SHTProgBits) AND sectionList[i].flags = 0 => { IF sectionList[i].entrySize = BYTES[WireStabEntryBody] THEN { IF maxSymsSize < sectionList[i].size THEN { symsSeg ¬ [sectionList[i].offset, sectionList[i].size]; symsIndex ¬ i; maxSymsSize ¬ sectionList[i].size; }; }; }; (sectionList[i].type = SHTRela) OR (sectionList[i].type = SHTRel) => { IF sectionList[i].info = textIndex THEN textRelocSeg ¬ [sectionList[i].offset, sectionList[i].size] ELSE IF sectionList[i].info = dataIndex THEN dataRelocSeg ¬ [sectionList[i].offset, sectionList[i].size] ELSE IF sectionList[i].info = symsIndex THEN symsRelocSeg ¬ [sectionList[i].offset, sectionList[i].size]; }; ENDCASE; ENDLOOP; IF symsIndex # 0 THEN { symsStrName: ROPE ¬ Rope.Concat[sectionList[symsIndex].name, "str"]; symsRelocName: ROPE ¬ Rope.Concat[".rela", sectionList[symsIndex].name]; FOR i: CARD IN [0..sectHdrNum) DO IF Rope.Equal[symsStrName, sectionList[i].name] THEN symsStrSeg ¬ [sectionList[i].offset, sectionList[i].size]; IF Rope.Equal[symsRelocName, sectionList[i].name] THEN symsRelocSeg ¬ [sectionList[i].offset, sectionList[i].size]; ENDLOOP; }; header ¬ NEW[HeaderBody ¬ [ dynamic: objType = ETDynamicLib, toolversion: BYTE[0] -- wHeader.version --, machtype: machine, magic: 0 -- wHeader.magic --, text: textSeg, iData: iDataSeg, textReloc: textRelocSeg, dataReloc: dataRelocSeg, symsReloc: symsRelocSeg, syms: symsSeg, extSyms: symtabSeg, textLoadOffset: textLoadOffset, bssSize: bssSeg.byteLength, entryPoint: Basics.Card32FromF[wHeader.entry], nPageSize: nPageSize, nEntries: symsSeg.byteLength/BYTES[WireStabEntryBody], nExtEntries: symtabSeg.byteLength/BYTES[WireSymtabEntryBody], stringOffset: symsStrSeg.byteOffset, stringIndexLimit: symsStrSeg.byteLength, extStringOffset: symtabStrSeg.byteOffset, extStringIndexLimit: symtabStrSeg.byteLength, clientData: sectionList ]]; RETURN[header]; }; SunELFModuleFromParsedAndPC: ObjectFilesPrivate.ModuleFromParsedAndPCProcType ~ { IF whole = NIL THEN RETURN [NIL] ELSE { symtabRange: SymtabRange; minFuncX: CARD; minPC: CARD; dataReloc: CARD; [symtabRange, minFuncX, minPC, dataReloc] ¬ AlternativeFindModuleSymtabRange[whole, spc.relPC]; IF symtabRange = [0, 0] THEN RETURN [NIL]; IF symtabRange = [1, whole.header.nExtEntries-1] THEN { RETURN [ModuleFromParsedInner[whole]]; }; { moduleWhole: Parsed ¬ NIL; sourceRope: Rope.ROPE ¬ NIL; funcRope: Rope.ROPE ¬ NIL; dotOPathRope, dotORope: Rope.ROPE ¬ NIL; dotOId: CARD ¬ 0; lag: LIST OF Module ¬ NIL; newModule: Module ¬ NIL; cell: LIST OF Module ¬ NIL; sourceRope ¬ ReadSymtabRope[whole, symtabRange.first]; FOR modules: LIST OF Module ¬ whole.modules, modules.rest WHILE modules # NIL DO IF minPC < modules.first.firstPC THEN EXIT; -- we should have found it by now IF Rope.Equal[PFS.RopeFromPath[modules.first.fileName], sourceRope] THEN { -- we already have it RETURN[modules.first] }; lag ¬ modules; ENDLOOP; IF whole.targetData = NIL THEN RETURN [NIL]; funcRope ¬ ReadSymtabRope[whole, minFuncX]; [dotOPathRope, dotORope, dotOId] ¬ GetFileNameAndID[whole, sourceRope]; IF dotOId = 0 THEN RETURN [NIL]; moduleWhole ¬ RMTWBackdoor.GetDotO[whole.targetData, dotOPathRope, dotORope, dotOId, GetDotOId]; newModule ¬ ModuleFromParsedInner[moduleWhole, funcRope, minPC, dataReloc]; IF newModule = NIL THEN RETURN [NIL]; cell ¬ LIST[newModule]; IF lag # NIL THEN lag.rest ¬ cell ELSE whole.modules ¬ cell; newModule.whole ¬ whole; newModule.moduleWhole ¬ moduleWhole; RETURN [newModule]; }; }; }; ModuleFromParsedInner: PROC[whole: Parsed, funcRope: Rope.ROPE ¬ NIL, funcPC, dataReloc: CARD ¬ 0] RETURNS[Module] = { IF whole = NIL THEN RETURN [NIL] ELSE { stream: IO.STREAM ¬ SystemInterface.GetStreamForFile[whole.file]; IF whole = NIL THEN RETURN [NIL] ELSE { ENABLE UNWIND => { SystemInterface.ReleaseStreamForFile[whole.file, stream]; }; stabRange: StabRange ¬ [0, whole.header.nEntries]; firstPC: CARD ¬ 0; lag: LIST OF Module ¬ NIL; 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, funcRope, funcPC, dataReloc]; cell: LIST OF Module ¬ LIST[newModule]; IF lag # NIL THEN lag.rest ¬ cell ELSE whole.modules ¬ cell; SystemInterface.ReleaseStreamForFile[whole.file, stream]; RETURN[newModule]; }; }; }; }; GetFileNameAndID: PROC [whole: Parsed, sourceRope: Rope.ROPE] RETURNS [dotOPathRope, dotORope: Rope.ROPE ¬ NIL, dotOId: CARD ¬ 0] = { stream: IO.STREAM ¬ SystemInterface.GetStreamForFile[whole.file]; { ENABLE UNWIND => { SystemInterface.ReleaseStreamForFile[whole.file, stream]; }; nStabs: CARD ¬ whole.header.nEntries; stabX, nextStabX: CARD ¬ 0; strIndex, nextStrIndex: CARD ¬ whole.header.stringOffset; source: Rope.ROPE ¬ NIL; dotOPath, dotO: Rope.ROPE ¬ NIL; ropeStream: IO.STREAM ¬ PFS.StreamFromOpenFile[openFile~PFS.OpenFileFromStream[stream]]; WHILE stabX < nStabs DO IO.SetIndex[stream, whole.header.syms.byteOffset+stabX*BYTES[WireStabEntryBody]]; TRUSTED{[] ¬ IO.UnsafeGetBlock[stream, [LOOPHOLE[@wireStabBuffer­], 0, BYTES[WireStabEntryBody]]]}; SELECT wireStabBuffer.type FROM 00H => { stabX ¬ stabX + 1; nextStabX ¬ stabX + Basics.Card16FromH[wireStabBuffer.desc]; strIndex ¬ nextStrIndex; nextStrIndex ¬ nextStrIndex + Basics.Card32FromF[wireStabBuffer.value]; IO.SetIndex[ropeStream, strIndex+Basics.Card32FromF[wireStabBuffer.stringX]]; source ¬ ReadRope[ropeStream]; dotOPath ¬ NIL; }; 38H => { stabX ¬ stabX + 1; IO.SetIndex[ropeStream, strIndex+Basics.Card32FromF[wireStabBuffer.stringX]]; dotO ¬ ReadRope[ropeStream]; dotOPath ¬ Rope.Concat[dotOPath, dotO]; }; 3cH => { stabX ¬ nextStabX; IF Rope.Equal[source, sourceRope] THEN RETURN [dotOPathRope: dotOPath, dotORope: dotO, dotOId: Basics.Card32FromF[wireStabBuffer.value]]; }; ENDCASE => stabX ¬ stabX + 1; ENDLOOP; }; }; GetDotOId: PROC [whole: Parsed] RETURNS [dotOId: CARD ¬ 0] = { stream: IO.STREAM ¬ SystemInterface.GetStreamForFile[whole.file]; { ENABLE UNWIND => { SystemInterface.ReleaseStreamForFile[whole.file, stream]; }; nStabs: CARD ¬ whole.header.nEntries; stabX, nextStabX: CARD ¬ 0; WHILE stabX < nStabs DO IO.SetIndex[stream, whole.header.syms.byteOffset+stabX*BYTES[WireStabEntryBody]]; TRUSTED{[] ¬ IO.UnsafeGetBlock[stream, [LOOPHOLE[@wireStabBuffer­], 0, BYTES[WireStabEntryBody]]]}; SELECT wireStabBuffer.type FROM 00H => { stabX ¬ stabX + 1; nextStabX ¬ stabX + Basics.Card16FromH[wireStabBuffer.desc]; }; 3cH => { stabX ¬ nextStabX; RETURN [Basics.Card32FromF[wireStabBuffer.value]]; }; ENDCASE => stabX ¬ stabX + 1; ENDLOOP; }; }; 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], funcRope: Rope.ROPE ¬ NIL, funcPC, dataReloc: CARD ¬ 0] RETURNS[Module] = BEGIN module: Module ¬ NEW[ModuleBody]; lineNums: LineNumList ¬ NEW [LineNumListBody ¬ [0, NIL]]; lineNums2: LineNumList ¬ NEW [LineNumListBody ¬ [0, NIL]]; nStabs: CARD ¬ stabRange.count; module.whole ¬ whole; module.firstStabX ¬ stabRange.first; module.limitStabX ¬ stabRange.first+stabRange.count; module.firstPC ¬ 0; module.limitPC ¬ whole.header.text.byteLength; module.dataReloc ¬ dataReloc; module.stabs ¬ NEW[StabSet[nStabs]]; {I: CARD ¬ 0; WHILE I 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, "SunELF"]; 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: SymtabRange ¬ AlternativeFindModuleSymtabRange[whole, pc].symtabRange; 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]; }; SymtabBody: TYPE ~ RECORD[symtabX: CARD, value: CARD, size: CARD, symtabBind: SymtabBind, symtabType: SymtabType, symtabSection: CARD]; nullSymtabBody: SymtabBody ~ [0, 0, 0, Local, NoType, 0]; AlternativeFindModuleSymtabRange: PROC[whole: Parsed, relativePC: CARD] RETURNS[symtabRange: SymtabRange ¬ [0, 0], minFuncX, minPC: CARD ¬ 0, dataReloc: CARD ¬ 0] = { baseFile: SymtabBody ¬ nullSymtabBody; maxPC: CARD ¬ 0; highSymtabX: CARD ¬ 0; foundDataReloc: BOOLEAN ¬ FALSE; FindModuleSymtabRange: PROC [nominalX: CARD] RETURNS [failed: BOOL] = { IF nominalX >= whole.header.nExtEntries THEN RETURN[failed: TRUE]; baseFile ¬ nullSymtabBody; maxPC ¬ 0; highSymtabX ¬ 0; foundDataReloc ¬ FALSE; minPC ¬ LAST[CARD]; FOR x: CARD DECREASING IN [0..nominalX] DO info: SymtabBody ¬ ReadSymtabBody[whole, x]; SELECT info.symtabType FROM Func => IF info.symtabSection # 0 THEN { maxPC ¬ MAX[maxPC, info.value + info.size]; IF info.symtabBind = Local AND info.value < minPC THEN { minFuncX ¬ x; minPC ¬ info.value}}; File => {baseFile ¬ info; EXIT}; NoType => {dataReloc ¬ info.value; foundDataReloc ¬ TRUE}; ENDCASE; highSymtabX ¬ MAX[highSymtabX, info.symtabX]; ENDLOOP; IF baseFile.symtabType # File THEN { RETURN[failed: TRUE]}; FOR x: CARD IN (nominalX..whole.header.nExtEntries) DO info: SymtabBody ¬ ReadSymtabBody[whole, x]; SELECT info.symtabType FROM Func => IF info.symtabSection # 0 THEN { maxPC ¬ MAX[maxPC, info.value + info.size]; IF info.symtabBind = Local AND info.value < minPC THEN { minFuncX ¬ x; minPC ¬ info.value}}; File => EXIT; NoType => IF NOT foundDataReloc THEN { dataReloc ¬ info.value; foundDataReloc ¬ TRUE}; ENDCASE; highSymtabX ¬ MAX[highSymtabX, info.symtabX]; ENDLOOP; RETURN[failed: FALSE]; }; IF whole = NIL THEN RETURN[[0, 0], 0, 0, 0] ELSE { nominalX: CARD ¬ SearchForPCSymtab[whole, relativePC]; DO IF FindModuleSymtabRange[nominalX].failed THEN RETURN[[0, 0], 0, 0, 0]; IF minPC <= relativePC AND relativePC < maxPC THEN { RETURN[[baseFile.symtabX, highSymtabX-baseFile.symtabX+1], minFuncX, minPC, dataReloc]}; IF minPC > relativePC THEN EXIT; nominalX ¬ highSymtabX + 1; ENDLOOP; RETURN[[0, 0], 0, 0, 0]; }; }; SearchForPCSymtab: PROC[whole: Parsed, pc: CARD] RETURNS[CARD] = BEGIN x: CARD ¬ 0; y: CARD ¬ whole.header.nExtEntries; WHILE x+1 < y DO mid: CARD ¬ (x+y)/2; midSymtab: SymtabBody ¬ SearchFwdForPCSymtab[whole, mid, y]; SELECT TRUE FROM midSymtab.symtabX = y => y ¬ mid; pc < midSymtab.value => y ¬ mid; midSymtab.value = pc => RETURN[midSymtab.symtabX]; midSymtab.value < pc => x ¬ mid; ENDCASE => ERROR; ENDLOOP; RETURN[x]; END; SearchFwdForPCSymtab: PROC[whole: Parsed, start: CARD, limit: CARD] RETURNS[SymtabBody] = BEGIN FOR x: CARD ¬ start, x+1 WHILE x < limit DO info: SymtabBody ¬ ReadSymtabBody[whole, x]; IF info.symtabType = Func AND info.symtabBind = Local THEN RETURN[info]; IF info.symtabBind = Global THEN EXIT; ENDLOOP; RETURN[[limit, 0, 0, Local, NoType, 0]]; END; SymtabBindFromData: PROC [data: [0..15]] RETURNS [symtabType: SymtabBind] ~ { val: CARD ¬ data; symtabType ¬ SELECT val FROM STBLocal => Local, STBGlobal => Global, STBWeak => Weak, ENDCASE => ERROR; }; SymtabTypeFromData: PROC [data: [0..15]] RETURNS [symtabType: SymtabType] ~ { val: CARD ¬ data; symtabType ¬ SELECT val FROM STTNoType => NoType, STTObject => Object, STTFunc => Func, STTSection => Section, STTFile => File, ENDCASE => ERROR; }; ReadSymtabBody: ENTRY PROC[whole: Parsed, symtabX: CARD] RETURNS[SymtabBody] = BEGIN ENABLE UNWIND => NULL; stream: IO.STREAM ¬ SystemInterface.GetStreamForFile[whole.file]; IO.SetIndex[stream, whole.header.extSyms.byteOffset+symtabX*BYTES[WireSymtabEntryBody]]; TRUSTED{[] ¬ IO.UnsafeGetBlock[stream, [LOOPHOLE[@wireSymtabBuffer­], 0, BYTES[WireSymtabEntryBody]]]}; SystemInterface.ReleaseStreamForFile[whole.file, stream]; RETURN[[symtabX, Basics.Card32FromF[wireSymtabBuffer.value], Basics.Card32FromF[wireSymtabBuffer.size], SymtabBindFromData[wireSymtabBuffer.bind], SymtabTypeFromData[wireSymtabBuffer.type], Basics.Card16FromH[wireSymtabBuffer.sectionIndex]]]; END; ReadSymtabRope: ENTRY PROC[whole: Parsed, symtabX: CARD] RETURNS[rope: Rope.ROPE ¬ NIL] = BEGIN ENABLE UNWIND => NULL; stream: IO.STREAM ¬ SystemInterface.GetStreamForFile[whole.file]; IO.SetIndex[stream, whole.header.extSyms.byteOffset+symtabX*BYTES[WireSymtabEntryBody]]; TRUSTED{[] ¬ IO.UnsafeGetBlock[stream, [LOOPHOLE[@wireSymtabBuffer­], 0, BYTES[WireSymtabEntryBody]]]}; IO.SetIndex[stream, whole.header.extStringOffset+Basics.Card32FromF[wireSymtabBuffer.name]]; rope ¬ ReadRope[stream]; SystemInterface.ReleaseStreamForFile[whole.file, stream]; END; SunELFVarLocFromStab: ObjectFilesPrivate.VarLocFromStabProcType ~ { header: Header ~ stab.module.whole.header; MkSegment: PROC [kind: ObjectFiles.SimpleSeg] RETURNS [ObjectFiles.VarLoc] ~ { segRope: ROPE; segBase: CARD ~ SELECT kind FROM text => 0, data => header.text.byteLength, bss => header.text.byteLength + header.iData.byteLength, ENDCASE => ERROR; segSize: CARD ~ SELECT kind FROM text => header.text.byteLength, data => header.iData.byteLength, bss => header.bssSize, ENDCASE => ERROR; IF stab.value >= segSize THEN ObjectFiles.UnreadableObjectFile[IO.PutFLR["value (%xH) greater than segment size (%xH) for symbol %g in %g", LIST[[cardinal[stab.value]], [cardinal[segSize]], [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*8, stab.value + segBase + 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]]] ]]; }; SunELFInstallStaticVars: 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 ¬ 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; }; 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] RETURNS[stab: Stab, numStabsRead: CARD] ~ { ENABLE UNWIND => NULL; stab ¬ NEW [StabBody]; [stab­, numStabsRead] ¬ ReadStabBody[whole, stream, stabX]; RETURN }; previousStream: IO.STREAM ¬ NIL; cloneStream: IO.STREAM ¬ NIL; ReadStabBody: PROC [whole: Parsed, stream: IO.STREAM, stabX: CARD] RETURNS [stab: StabBody, numStabsRead: CARD] ~ { rope: ROPE ¬ "\\"; size, rLen, offset: CARD16 ¬ 0; value: CARD32; stabType: ObjectFiles.StabType; IO.SetIndex[stream, whole.header.syms.byteOffset+stabX*BYTES[WireStabEntryBody]]; TRUSTED{[] ¬ IO.UnsafeGetBlock[stream, [LOOPHOLE[@wireStabBuffer­], 0, BYTES[WireStabEntryBody]]]}; 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[WireStabEntryBody]]; TRUSTED{[] ¬ IO.UnsafeGetBlock[stream, [LOOPHOLE[@wireStabBuffer­], 0, BYTES[WireStabEntryBody]]]}; 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 ]; RETURN}; RelocateStabAndLineNum: PROC [whole: Parsed, stream: IO.STREAM, module: Module, nStabs: CARD, funcRope: Rope.ROPE ¬ NIL, funcPC, dataReloc: CARD ¬ 0, lineNum, lineNum2: LineNumList] ~ { sectionList: SectionList ¬ NARROW[whole.header.clientData]; nRelocEntries: INT ¬ whole.header.symsReloc.byteLength / BYTES[WireRelocEntryBody]; symtabStream: IO.STREAM ¬ PFS.StreamFromOpenFile[openFile~PFS.OpenFileFromStream[stream]]; previousSymtabX: CARD ¬ LAST[CARD]; symtabX: CARD; sectionIndex: CARD; baseValue: CARD; value: CARD; stabX: CARD; stabRope: Rope.ROPE ¬ Rope.Concat[funcRope, ":f("]; -- local function FOR I: CARD IN [0..nRelocEntries) DO IO.SetIndex[stream, whole.header.symsReloc.byteOffset+I*BYTES[WireRelocEntryBody]]; TRUSTED{[] ¬ IO.UnsafeGetBlock[stream, [LOOPHOLE[@wireRelocBuffer­], 0, BYTES[WireRelocEntryBody]]]}; symtabX ¬ wireRelocBuffer.sym; IF symtabX # previousSymtabX THEN { IO.SetIndex[symtabStream, whole.header.extSyms.byteOffset+symtabX*BYTES[WireSymtabEntryBody]]; TRUSTED{[] ¬ IO.UnsafeGetBlock[symtabStream, [LOOPHOLE[@wireSymtabBuffer­], 0, BYTES[WireSymtabEntryBody]]]}; sectionIndex ¬ Basics.Card16FromH[wireSymtabBuffer.sectionIndex]; baseValue ¬ Basics.Card32FromF[wireSymtabBuffer.value] + sectionList[sectionIndex].addr; IF IsDataSection[sectionList[sectionIndex]] THEN baseValue ¬ baseValue + dataReloc; previousSymtabX ¬ symtabX; }; value ¬ baseValue + Basics.Int32FromF[wireRelocBuffer.addend]; stabX ¬ Basics.Card32FromF[wireRelocBuffer.offset] / BYTES[WireStabEntryBody]; SELECT wireRelocBuffer.type FROM RSparc32 => module.stabs[stabX].value ¬ value; ENDCASE => ERROR; IF module.stabs[stabX].stabType = Fun AND Rope.IsPrefix[stabRope, module.stabs[stabX].rope] THEN { module.firstPC ¬ module.firstPC + (funcPC - module.stabs[stabX].value); module.limitPC ¬ module.limitPC + (funcPC - module.stabs[stabX].value); }; ENDLOOP; FOR I: CARD IN [0..nStabs) DO SELECT module.stabs[I].stabType FROM Fun => IF Rope.Find[module.stabs[I].rope, ":P("] = -1 THEN value ¬ module.stabs[I].value ¬ module.stabs[I].value + module.firstPC; SLine => { info: REF SLineData ¬ NEW[SLineData¬[cLineNum: module.stabs[I].size, parsedRelPC: [[1, ".text"], module.stabs[I].value + value]]]; lineNum.lines ¬ CONS[info, lineNum.lines]; lineNum.nLines ¬ lineNum.nLines + 1; lineNum2.lines ¬ CONS[info, lineNum2.lines]; lineNum2.nLines ¬ lineNum2.nLines + 1; module.stabs[I].value ¬ module.stabs[I].value + value; }; LBrac, RBrac => module.stabs[I].value ¬ module.stabs[I].value + value; ENDCASE; ENDLOOP; RETURN}; wireStabBuffer: WireStabEntry ¬ NEW[WireStabEntryBody]; wireSymtabBuffer: WireSymtabEntry ¬ NEW[WireSymtabEntryBody]; wireRelocBuffer: WireRelocEntry ¬ NEW[WireRelocEntryBody]; ReadStringX: PROC[stream: IO.STREAM, whole: Parsed, stringX: CARD] RETURNS[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] = BEGIN ENABLE UNWIND => NULL; 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; IsExternalFun: PROC [stab: Stab] RETURNS [BOOL] ~ { RETURN[Rope.Find[stab.rope, ":P", 0, TRUE] # -1]}; SunELFScanModuleStructure: 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 => IF IsExternalFun[stab] THEN I ¬ I + 1 ELSE { 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; LBrac => { SELECT module.stabs[I+1].stabType FROM STSym, LSym, RSym => firstLocal ¬ module.stabs[I+1]; ENDCASE; EXIT}; 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]; REPEAT FINISHED => SystemInterface.ShowReport[IO.PutFR["Missed an LBRAC in function %g in %g", [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}; SunELFScanFnStructure: PROC [module: Module, funStab: Stab, firstLocal: Stab ¬ NIL, nextX: CARD, perParm: PROC [Stab] ¬ NIL, perBracket: ObjectFilesPrivate.BracketConsumer ¬ NIL] RETURNS [limitX, limitPc: CARD] ~ { IF perParm#NIL THEN PassLocals[module, funStab, funStab.stabX+1, nextX, perParm]; limitX ¬ DoBrack[module, funStab, module.stabs[nextX-module.firstStabX], perBracket]; FOR K: CARD ¬ limitX-module.firstStabX, K+1 WHILE K < module.stabs.nStabs DO stab: Stab ¬ module.stabs[K]; IF stab.module = NIL THEN LOOP; SELECT stab.stabType FROM Fun => IF stab.value > funStab.value THEN RETURN [limitX, stab.value]; ENDCASE => NULL; 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 ¬ SunELFScanBktStructure[module, funStab, first, NIL, NIL].limitX; RETURN}; SunELFScanBktStructure: 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 + 1; stab: Stab; IF first.stabType # LBrac THEN ERROR; firstPc ¬ first.value; 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 RBrac => RETURN [I+1+module.firstStabX, firstPc, stab.value]; STSym, LSym, RSym => IF perLocal#NIL THEN perLocal[stab]; LBrac => I ¬ DoBrack[module, funStab, stab, perSubBracket] - 1 - module.firstStabX; Fun => IF NOT IsExternalFun[stab] THEN {--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 RBRAC 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[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}; SunELFGetTypeRef: 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]}; SunELFAlterFunStab: ObjectFilesPrivate.AlterFunStabType ~ {RETURN [module.funStabs[funStabX].stab]; }; SunELFGetSPOffset: 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[Basics.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: Basics.BITAND[3, Basics.BITSHIFT[inst, -30]], disp30: Basics.BITAND[7777777777B, inst], rd: Basics.BITAND[37B, Basics.BITSHIFT[inst, -25]], a: Basics.BITAND[1, Basics.BITSHIFT[inst, -29]], cond: Basics.BITAND[17B, Basics.BITSHIFT[inst, -25]], op2: Basics.BITAND[7, Basics.BITSHIFT[inst, -22]], imm22: Basics.BITAND[17777777B, inst], disp22: Basics.BITAND[17777777B, inst], op3: Basics.BITAND[77B, Basics.BITSHIFT[inst, -19]], rs1: Basics.BITAND[37B, Basics.BITSHIFT[inst, -14]], i: Basics.BITAND[1, Basics.BITSHIFT[inst, -13]], asi: Basics.BITAND[377B, Basics.BITSHIFT[inst, -5]], rs2: Basics.BITAND[37B, inst], simm13: Basics.BITAND[17777B, inst], opf: Basics.BITAND[777B, Basics.BITSHIFT[inst, -5]]]]; }; transTable: ObjectFilesPrivate.TranslationTable ¬ NEW[ObjectFilesPrivate.TranslationTableBody[2]]; Commander.Register["SunELFRandomTestFindSymtabRange", RandomTestFindSymtabRange]; transTable[0] ¬ [0, 4, ElfMagic, "SunELF", first]; transTable[1] ¬ [18, 2, EMSparc, "SunELF", last]; ObjectFilesPrivate.RegisterObjectFileFlavor[NEW[ObjectFilesPrivate.ObjectFileFlavorBody ¬ [ "SunELF", SunADotOut, ReadHeader, SunELFModuleFromParsedAndPC, SunELFVarLocFromStab, SunELFGetTypeRef, NIL, SunELFScanModuleStructure, SunELFScanFnStructure, SunELFScanBktStructure, TRUE, SunELFInstallStaticVars, SunELFAlterFunStab, SunELFGetSPOffset ]], transTable]; END.  SunELFFiles.mesa Copyright Ó 1992, 1993 by Xerox Corporation. All rights reserved. Katsuyuki Komatsu January 25, 1993 4:07 pm PST Willie-s, March 2, 1993 4:20 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 The following computations are adapted from /usr/include/sys/elf.h of the SunOS Release 5.x. The following computations are adapted from /usr/include/sys/elf_SPARC.h of the SunOS Release 5.x. The following record type was constructed based on /usr/include/sys/elf.h of the SunOS Release 5.x. Header PROC[stream: IO.STREAM] RETURNS[Header] = TRUSTED ignore .comment section that is (sectionList[i].entrySize = 0) symsStrSeg _ [sectionList[sectionList[i].link].offset, sectionList[sectionList[i].link].size]; -- Unfotunately, the link field of stab section is not reliable. Module We assume that there are dbx stabs located in the whole in the stab table entry range [firstX..limitX). We assume that the first stab is a special stab whose type is the Undefined (0x00), whose size is the count of stabs and whose value is the size of string table. We also assume that this stab containing the file name. PROC [whole: Parsed, spc: FileSegmentPC, moduleRope: ROPE _ NIL] RETURNS [Module] We are fortunate, single module ! first, see if we already have it nest so that we can catch unwinds and release the stream first, see if we already have it nest so that we can catch unwinds and release the stream nest so that we can catch unwinds and release the stream prepare the Module for a call on BuildBrackets lets read the all stabs now (WITH their ropes) now lets finish up usage: RandomTestFindStabRange /dir/.../dir/[sun4/]foo[.c2c].o Finding Modules by Symtab retry because SearchForPCSymtab may return wrong range Variables PROC [stab: Stab] RETURNS [ObjectFiles.VarLoc] 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. C Line-Number to Relative-PC maps sorted by PC sorted by Line Num Stabs warning: does NOT relocate stabs. Also: the module field is set to NIL; hack to avoid messing up the stream buffering all users should be in the monitor all users should be in the monitor 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. Stab Structure PROC [sourceStream:IO.STREAM] RETURNS [Rope.ROPE] Instruction decoder 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 Ê6ß–"cedarcode" style•NewlineDelimiter ™codešœ™Kšœ Ïeœ7™BK™.K™#—K˜šÏk ˜ Kš œžœžœž œžœ(žœžœ˜kKšœ žœžœ˜Kšœžœ˜$Kšœ žœ ˜Kšœ žœ˜(Kšœ žœ˜+Kšœžœ˜Kšžœ˜Kšœžœ˜Kšœ žœ€˜‘Kšœžœþ˜–KšžœžœF˜OKšœ žœžœ˜Kšœžœ"˜.Kšœ žœ ˜Kšœ˜Kšœžœt˜‰K˜—K˜šÏn œžœž˜Kšžœ?žœ)žœ-˜¡Kšžœ ˜—Kšœž˜K˜KšŸœžœ!žœžœ˜MK˜™ Kšžœžœžœ˜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˜—™1K™Kšœžœžœ ˜Kšœ žœžœ!˜8K˜Kšœžœžœ ˜Kšœ žœžœ!˜8K™Kšœžœžœ ˜Kšœ žœ!˜1K˜Kšœžœ(˜?K˜Kšœ žœ˜+K˜Kšœ žœ"˜3Kšœžœ&˜;K˜—™'K™Kšœžœ˜2K˜Kšœ\™\K˜KšŸœžœ ž œž œžœž œžœ žœ˜tK˜KšŸ œžœ˜KšŸ œžœ˜KšŸ œžœ˜K˜KšŸ œžœ˜KšŸ œžœ˜KšŸ œžœ˜K˜KšŸœžœ˜KšŸ œžœ˜KšŸ œžœ˜KšŸ œžœ˜KšŸœžœ˜K˜KšŸœžœ˜Kš œžœ˜KšŸœžœ˜Kš œžœ˜Kš œžœ˜Kš œžœ˜Kš œžœ˜Kš œžœ˜KšÐbnœžœ˜Kš œžœ˜K˜KšŸœžœ˜KšŸ œžœ˜KšŸ œžœ˜KšŸ œžœ˜KšŸœžœ˜KšŸœžœ˜KšŸ œžœ˜KšŸœžœ˜KšŸ œžœ˜KšŸœžœ˜KšŸœžœ˜KšŸ œžœ˜K˜KšŸœžœ˜KšŸœžœ˜KšŸ œžœ˜K˜KšŸœžœ˜KšŸ œžœ˜KšŸœžœ˜K˜KšŸ œžœ˜KšŸ œžœ˜KšŸœžœ˜KšŸ œžœ˜KšŸœžœ˜K˜Kšœb™bK™KšŸ œžœ˜KšŸœžœ˜KšŸœžœ˜KšŸœžœ˜KšŸ œžœ˜KšŸ œžœ˜KšŸ œžœ˜KšŸ œžœ˜KšŸ œžœ˜KšŸ œžœ˜KšŸœžœ˜KšŸœžœ˜KšŸ œžœ˜KšŸ œžœ˜KšŸ œžœ˜KšŸ œžœ˜KšŸ œžœ˜KšŸ œžœ˜KšŸ œžœ˜KšŸ œžœ˜KšŸ œžœ˜KšŸ œžœ˜KšŸœžœ˜KšŸ œžœ˜K˜Kšœc™cKšœ žœžœ˜&š œžœžœž œžœ˜0Kšœžœ˜Kšœ žœ˜Kšœ žœ˜Kšœ žœ˜Kšœ žœ˜Kš œ žœžœ žœžœ˜&Kšœ žœ˜Kšœžœ˜Kšœžœ˜Kšœžœ˜Kšœžœ˜Kšœžœ˜Kšœžœ˜Kšœžœ˜Kšœžœ˜Kšœžœ˜Kšœžœ˜Kšœžœ˜Kšœžœ˜—K˜Kšœ žœžœ˜(šœžœžœ˜Kšœžœ žœžœ˜.K˜—Kšœžœžœ˜,šœžœžœ˜!Kšœžœžœ˜Kšœ žœ˜Kšœžœ˜ Kšœžœ˜Kšœžœ˜ Kšœžœ˜Kšœžœ˜ Kšœžœ˜ Kšœžœ˜ Kšœ žœ˜Kšœ žœ˜K˜—Kšœžœžœ˜4š œžœžœž œžœ˜7Kšœžœ˜Kšœ žœ˜Kšœžœ˜Kšœ žœ˜Kšœžœ˜Kšœ žœ˜Kšœ žœ˜Kšœ žœ˜Kšœžœ˜Kšœžœ˜K˜—Kšœžœžœ˜,š œžœžœž œžœ˜3Kšœžœ˜ Kšœžœ˜Kšœžœ˜Kšœžœ˜Kšœžœ˜ K˜—Kšœžœžœ˜0š œžœžœž œžœ˜5Kšœžœ˜Kšœžœ˜Kšœžœ˜Kšœ˜Kšœ˜Kšœžœ˜Kšœ"žœ˜)K˜—Kšœ žœžœžœ˜/šœ žœ˜K˜K˜Kšœ˜Kšœ˜K˜—šœ žœ˜K˜K˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜K˜—Kšœžœžœ˜.š œžœžœž œžœ˜4Kšœžœ˜Kšœ˜Kšœžœ˜Kšœžœ˜!——K˜™K˜šŸœžœ žœžœžœžœžœ˜DKšžœ žœ˜*K˜—š Ÿ œžœžœžœžœ˜FKš žœžœžœ!žœžœ(˜”K˜—šŸ œ*žœ˜>Kš žœ žœžœžœ ž™1Kšœžœ˜*Kš œžœžœžœžœ˜YKšœžœ%˜0Kšœ žœ$˜1Kšœ žœ'˜4Kš œ žœžœ!žœ!žœžœžœ˜ÁKšœ žœ ˜K˜$K˜%K˜#K˜&K˜)K˜$K˜'K˜)K˜)K˜)Kšœžœ˜Kšœžœ-˜@Kšœ žœ*˜:Kšœžœ˜K˜@K˜>K˜BK˜>K˜>K˜>K˜HK˜HKšžœ˜—šžœžœžœž˜!KšžœL˜NK˜'šžœžœž˜šœ$žœ,˜Sšžœžœ(žœ˜4šžœ žœžœžœ˜ K˜7K˜K˜—šž˜K˜>—šžœž˜K˜A—Kšœ˜—šžœ˜šžœ žœžœžœ˜ K˜8K˜K˜—šž˜K˜@—šžœž˜K˜B—Kšœ˜—K˜—šœ"žœ,˜Qšžœ žœžœžœ˜K˜6K˜ K˜—šž˜K˜<—šžœž˜K˜@—Kšœ˜—šœ&˜&šžœžœžœžœ˜"K˜9K˜Kšžœžœžœžœ˜DK˜`K˜—Kšœ˜—šœ$žœ˜EKšœ>™>šžœžœžœ˜=šžœ#žœ˜+K˜7K˜KšœŸ™ŸK˜"K˜—Kšœ˜—Kšœ˜—šœ žœ$˜Fšžœ!ž˜'K˜;—šžœžœ!ž˜,K˜;—šžœžœ!ž˜,K˜<—Kšœ˜—Kšžœ˜—Kšžœ˜—šžœžœ˜Kšœ žœ3˜DKšœžœ5˜Hšžœžœžœž˜!šžœ.ž˜4K˜:—šžœ0ž˜6K˜<—Kšžœ˜—K˜—šœ žœ˜Kšœ ˜ Kšœ žœÏcœ˜+Kšœ˜Kšœ ¢œ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜K˜Kšœ˜Kšœ.˜.Kšœ˜Kšœžœ˜6Kšœ"žœ˜=Kšœ$˜$Kšœ(˜(Kšœ)˜)Kšœ-˜-Kšœ˜Kšœ˜—Kšžœ ˜Kšœ˜K˜——K™™™K™gK™¡K™7K™—šŸœ6˜QKšžœ1žœžœžœ ™Qš žœ žœžœžœžœžœ˜'Kšœ˜Kšœ žœ˜Kšœžœ˜ Kšœ žœ˜K˜K˜_Kšžœžœžœžœ˜*šžœ/žœ˜7Kšœ!™!Kšžœ ˜&K˜—˜Kšœžœ˜Kšœžœžœ˜Kšœžœžœ˜Kšœžœžœ˜(Kšœžœ˜Kšœžœžœ žœ˜Kšœžœ˜Kšœžœžœ žœ˜K˜K˜6K™ š žœ žœžœ&žœ žœž˜PKšžœžœžœ¢!˜Mšžœ žœ3žœ¢˜`Kšžœ˜Kšœ˜—K˜Kšžœ˜—Kš žœžœžœžœžœ˜,K˜+K˜GKšžœ žœžœžœ˜ K˜`K˜K˜KKš žœ žœžœžœžœ˜%Kšœžœ ˜Kšžœžœžœžœ˜Kšœžœžœ0˜AK™K™8šœ˜šžœžœ˜Kšœ9˜9K˜K˜—Kšœžœ˜%Kšœžœ˜K˜šžœž˜Kšžœ5žœ˜QKšžœžœžœžœ˜cšžœž˜šœ˜K˜K˜Kšœ˜Kšœ˜Kšœ ˜ Kšœ%˜%Kšœ˜Kšœ˜Kšœ žœ˜ K˜ Kšœ žœ˜ Kšœžœ˜K˜—Kšžœ ˜Kšžœ˜K™—K™>šŸœ˜4K˜š žœŸœžœžœž˜)Kšœžœ žœ˜/K˜K˜Kšžœ˜—K˜K˜5Kšœžœ'˜?š žœŸœžœžœž˜*Kšœžœ žœ˜/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˜—šŸ œžœžœžœžœ žœžœžœ˜tK˜Kšœ!™!K™%K™Kšžœžœžœ˜Kšœžœ ˜K˜;Kšž˜Kšœ˜K˜—K™-Kšœžœžœžœ˜ Kšœ žœžœžœ˜K˜šŸ œžœžœžœ žœžœ žœ˜sKšœžœ˜Kšœžœ˜Kšœžœ˜Kšœ˜Kšžœ5žœ˜QKšžœžœžœžœ˜cK˜-K˜/K˜1šžœžœ˜Kšœžœžœ˜NK˜K˜—K˜SK˜K˜šžœž˜šžœž˜!K˜šžœ@žœ˜\Kšžœžœžœžœ˜cK˜sK˜K˜ Kšžœ˜———˜Kšœžœ˜ Kšœ ˜ Kšœ˜K˜ Kšœ ˜ Kšœ ˜ Kšœ˜—Kšžœ˜K˜—šŸœžœžœžœžœžœžœžœ)˜¹Kšœžœ˜;Kšœžœ'žœ˜SKš œžœžœžœžœ˜ZKšœžœžœžœ˜#Kšœ žœ˜Kšœžœ˜Kšœ žœ˜Kšœžœ˜ Kšœžœ˜ Kšœžœ!¢˜EK˜š žœŸœžœžœž˜$Kšžœ6žœ˜SKšžœžœžœžœ˜eK˜šžœžœ˜#Kšžœ@žœ˜^Kšžœžœžœžœ˜mK˜AK˜XKšžœ*žœ#˜SK˜K˜—K˜>Kšœ5žœ˜Nšžœž˜ K˜.Kšžœžœ˜—šžœ$žœ3žœ˜bK˜GK˜GKšœ˜—Kšžœ˜—K˜š žœŸœžœžœ ž˜šžœž˜$šœžœ-ž˜:K˜G—šœ ˜ Kšœžœ žœi˜‚Kšœžœ˜*K˜$Kšœžœ˜,K˜&K˜6Kšœ˜—K˜FKšžœ˜—Kšžœ˜—Kšžœ˜K˜—šœ žœ˜7KšÏt"™"—šœ$žœ˜=Kš£"™"—šœ"žœ˜:Kš£"™"—K™šŸ œžœ žœžœžœžœžœ˜ZKšžœ5˜7K˜KšžœLžœÕ™­—K˜—K˜™ K™KšŸœžœ˜Kšœ žœžœžœžœžœžœžœžœžœžœ˜iK˜˜Kšœq™q—š Ÿœžœžœžœžœžœ˜2Kšž˜Kšžœžœžœ˜Kšœžœžœ˜šžœžœž˜ Kšœžœ¢ ˜+K˜K˜Kšžœžœžœ%˜PK˜š žœŸœžœžœž˜%Kšžœžœžœ˜1Kšžœ˜K˜—K˜Kš žœŸœžœžœ žœžœ˜<š œžœžœžœžœ žœžœ˜9Kšžœžœ žœžœ˜:—Kšžœžœžœ˜%Kšžœ˜—Kšžœ˜ Kšžœ˜K™——K˜™K˜šŸ œžœžœžœ˜3Kšžœžœ ˜2K˜—šŸœžœ;˜ZKšŸœžœ˜ šžœž˜ K˜Kšžœžœžœ˜!šžœžœž˜K˜&šœžœžœ žœ˜3Kšœ˜Kšœžœ˜šžœž˜"K˜ K˜Kšžœžœžœžœ˜šžœž˜Kšœžœ˜šœ ˜ šžœž˜&K˜4Kšžœ˜—Kšžœ˜—Kšœžœžœžœ˜@Kšžœžœà˜ˆ—Kšžœžœžœ˜ÀKšžœ˜—K˜I—Kšœ#žœžœ˜Lšžœ˜ Kšœžœ¬˜ÉK˜ ——Kšžœ˜—Kšžœ˜—K˜šŸœžœ4žœ žœ žœ žœ3žœžœžœ˜ÖKšžœ žœžœ>˜QK˜Uš žœŸœžœ!žœž˜LK˜Kšžœžœžœžœ˜šžœž˜Kšœžœžœžœ˜FKšžœžœ˜—Kšžœ˜—Kšžœ˜!K˜—šŸœžœ[žœ žœ˜‚Kšžœžœžœ˜7Kšžœ9žœžœ ˜NKšžœ˜K˜—šŸœžœ2žœ žœ6žœžœžœ˜ÆKšŸœžœ'˜.K˜ Kšžœžœžœ˜%K˜šžœ žœž˜/K˜Kšžœžœžœžœ˜šžœž˜Kšœ žœ.˜=Kšœžœ žœžœ˜9K˜Sšœžœžœžœ¢˜CKšœžœŸ˜¼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˜š ¡œžœžœžœžœ˜%Kšœžœžœžœ˜&K˜—š Ÿœžœžœžœžœ˜4šžœž˜Kšžœ žœ ˜Kšžœžœ ˜——K˜—KšŸœ)žœ%˜fK˜—K˜™K˜šŸœ(˜9Kšœžœ ˜"Kšœžœ>˜IKšœžœ@˜KKšœžœ@˜KKšœ žœ&˜3Kšžœžœ!˜5Kšžœ ˜Kšœ˜—˜Kšœ!žœ žœ0™bK™K—™™ K™K™K™——š Ÿ œžœžœžœ žœ˜IK˜BK˜BK˜B˜Kšœ#™#—Kšžœžœžœ˜"Kšžœžœžœ˜#KšžœžœžœÐci˜2˜Kšœ%™%—Kšžœžœžœ˜"Kšžœžœžœ˜#Kšžœžœžœ˜!Kšžœžœžœ¤˜2Kšžœžœžœ¤˜1˜K™—Kšžœžœžœ˜"Kšžœžœžœ˜%Kšžœžœžœ˜!KšžœžœžœÏi¤˜4Kšžœžœžœ¤˜2Kšžœžœžœ¥¤˜3K˜Kšœ žœžœ(˜KKšœ˜—˜™ Kšœ™——š Ÿ œžœžœžœ žœ˜;K˜B˜Kšœ$™$—Kšžœžœžœ˜"Kšžœžœžœ˜%Kšžœžœžœ˜!Kšžœžœžœ¥¤˜4Kšžœžœžœ¥¤˜3K˜K˜Kšœ˜—K˜šœžœžœ˜'KšœQžœ˜XK˜—šŸœžœžœžœ˜Mšžœ˜Kšœ žœ žœ ˜1Kšœžœ˜)Kšœ žœ žœ ˜3Kšœ žœ žœ ˜0Kšœ žœ žœ ˜5Kšœ žœ žœ ˜2Kšœžœ˜&Kšœžœ˜'Kšœ žœ žœ ˜4Kšœ žœ žœ ˜4Kšœ žœ žœ ˜0Kšœ žœžœ ˜4Kšœ žœ ˜Kšœžœ˜$Kšœ žœžœ˜6—Kšœ˜K˜——K˜™ K™Kšœ2žœ-˜bKšœQ˜QK˜2K˜1šœ,žœ,˜[Kšœ ˜ Kšœ ˜ Kšœ ˜ Kšœ˜Kšœ˜Kšœ˜Kšžœ˜K˜K˜Kšœ˜Kšžœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ ˜ ——K™Kšžœ˜K˜—…—±°õ‘