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.PutFR["value (%xH) greater than segment size (%xH) for symbol %g in %g", [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 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 Ê9•NewlineDelimiter ™codešœ™K™BK™.—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šœB˜BKšœ>˜>Kšœ>˜>Kšœ>˜>KšœH˜HKšœH˜HKšœ˜—šœœœ˜!KšœL˜NKšœ'˜'šœœ˜šœ$œ,˜Sšœœ(œ˜4šœ œœœ˜ Kšœ7˜7Kšœ˜K˜—š˜Kšœ>˜>—šœ˜KšœA˜A—Kšœ˜—šœ˜šœ œœœ˜ Kšœ8˜8Kšœ˜K˜—š˜Kšœ@˜@—šœ˜KšœB˜B—Kšœ˜—K˜—šœ"œ,˜Qšœ œœœ˜Kšœ6˜6Kšœ ˜ K˜—š˜Kšœ<˜<—šœ˜Kšœ@˜@—Kšœ˜—šœ&˜&šœœœœ˜"Kšœ9˜9Kšœ˜Kšœœœœ˜DKšœ`˜`K˜—Kšœ˜—šœ$œ˜EKšœ>™>šœœœ˜=šœ#œ˜+Kšœ7˜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šœ6˜6K™ š œ œœ&œ œ˜PKšœœœ¡!˜Mšœ œ3œ¡˜`Kšœ˜Kšœ˜—K˜Kšœ˜—Kš œœœœœ˜,Kšœ+˜+KšœG˜GKšœ œœœ˜ Kšœ`˜`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šœ<˜š œžœœœ˜)Jšœœ œ˜/Jšœ˜Jšœ˜Jšœ˜—J˜Jšœ5˜5Kšœœ'˜?š œžœœœ˜*Jšœœ œ˜/Jšœ˜Jšœ˜Jšœ˜—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˜1šœœ˜Kšœœœ˜NK˜K˜—KšœS˜SK˜Kšœ˜šœ˜šœ˜!K˜šœ@œ˜\Kšœœœœ˜cKšœs˜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šœA˜AKšœX˜XKšœ*œ#˜SKšœ˜K˜—Kšœ>˜>Kšœ5œ˜Nšœ˜ Kšœ.˜.Kšœœ˜—šœ$œ3œ˜bKšœG˜GKšœG˜GKšœ˜—Kšœ˜—K˜š œžœœœ ˜šœ˜$šœœ-˜:KšœG˜G—šœ ˜ Kšœœ œi˜‚Kšœœ˜*Kšœ$˜$Kšœœ˜,Kšœ&˜&Kšœ6˜6Kšœ˜—KšœF˜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šœ4˜4Kšœ˜—Kšœ˜—Kšœœœœ˜@Kšœœà˜ˆ—Kšœœœ˜ÀKšœ˜—KšœI˜I—Kšœ#œœ˜Lšœ˜ Kšœœ¬˜ÉKšœ ˜ ——Kšœ˜—Kšœ˜—K˜šžœœ4œ œ œ œ3œœœ˜ÖKšœ œœ>˜QKšœU˜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˜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šœ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šœ œœ(˜KKšœ˜—˜™ Kšœ™——š ž œœœœ œ˜;KšœB˜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šœ2˜2Kšœ1˜1šœ,œ,˜[Kšœ ˜ Kšœ ˜ Kšœ ˜ Kšœ˜Kšœ˜Jšœ˜Kšœ˜J˜J˜Jšœ˜Jšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ ˜ ——K™Kšœ˜K˜—…—±¨÷‰