/** * Prints a range of lines with a prefix. * * @param pState The diff state. * @param chPrefix The prefix. * @param pStream The stream to get the lines from. * @param iLine The first line. * @param cLines The number of lines. */ static void scmDiffPrintLines(PSCMDIFFSTATE pState, char chPrefix, PSCMSTREAM pStream, size_t iLine, size_t cLines) { while (cLines-- > 0) { SCMEOL enmEol; size_t cchLine; const char *pchLine = ScmStreamGetLineByNo(pStream, iLine, &cchLine, &enmEol); RTStrmPutCh(pState->pDiff, chPrefix); if (pchLine && cchLine) { if (!pState->fSpecialChars) RTStrmWrite(pState->pDiff, pchLine, cchLine); else { size_t offVir = 0; const char *pchStart = pchLine; const char *pchTab = (const char *)memchr(pchLine, '\t', cchLine); while (pchTab) { RTStrmWrite(pState->pDiff, pchStart, pchTab - pchStart); offVir += pchTab - pchStart; size_t cchTab = pState->cchTab - offVir % pState->cchTab; switch (cchTab) { case 1: RTStrmPutStr(pState->pDiff, "."); break; case 2: RTStrmPutStr(pState->pDiff, ".."); break; case 3: RTStrmPutStr(pState->pDiff, "[T]"); break; case 4: RTStrmPutStr(pState->pDiff, "[TA]"); break; case 5: RTStrmPutStr(pState->pDiff, "[TAB]"); break; default: RTStrmPrintf(pState->pDiff, "[TAB%.*s]", cchTab - 5, g_szTabSpaces); break; } offVir += cchTab; /* next */ pchStart = pchTab + 1; pchTab = (const char *)memchr(pchStart, '\t', cchLine - (pchStart - pchLine)); } size_t cchLeft = cchLine - (pchStart - pchLine); if (cchLeft) RTStrmWrite(pState->pDiff, pchStart, cchLeft); } } if (!pState->fSpecialChars) RTStrmPutCh(pState->pDiff, '\n'); else if (enmEol == SCMEOL_LF) RTStrmPutStr(pState->pDiff, "[LF]\n"); else if (enmEol == SCMEOL_CRLF) RTStrmPutStr(pState->pDiff, "[CRLF]\n"); else RTStrmPutStr(pState->pDiff, "[NONE]\n"); iLine++; } }
int main(int argc, char **argv) { int rc = RTR3InitExe(argc, &argv, 0); if (RT_FAILURE(rc)) return RTMsgInitFailure(rc); /* * Create an empty address space that we can load modules and stuff into * as we parse the parameters. */ RTDBGAS hDbgAs; rc = RTDbgAsCreate(&hDbgAs, 0, RTUINTPTR_MAX, ""); if (RT_FAILURE(rc)) return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTDBgAsCreate -> %Rrc", rc); /* * Create a debugging configuration instance to work with so that we can * make use of (i.e. test) path searching and such. */ RTDBGCFG hDbgCfg; rc = RTDbgCfgCreate(&hDbgCfg, "IPRT", true /*fNativePaths*/); if (RT_FAILURE(rc)) return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTDbgCfgCreate -> %Rrc", rc); /* * Parse arguments. */ static const RTGETOPTDEF s_aOptions[] = { { "--input", 'i', RTGETOPT_REQ_STRING }, { "--local-file", 'l', RTGETOPT_REQ_NOTHING }, { "--cache-file", 'c', RTGETOPT_REQ_NOTHING }, { "--pe-image", 'p', RTGETOPT_REQ_NOTHING }, { "--verbose", 'v', RTGETOPT_REQ_NOTHING }, { "--x86", '8', RTGETOPT_REQ_NOTHING }, { "--amd64", '6', RTGETOPT_REQ_NOTHING }, { "--whatever", '*', RTGETOPT_REQ_NOTHING }, }; PRTSTREAM pInput = g_pStdIn; PRTSTREAM pOutput = g_pStdOut; unsigned cVerbosityLevel = 0; enum { kOpenMethod_FromImage, kOpenMethod_FromPeImage } enmOpenMethod = kOpenMethod_FromImage; bool fCacheFile = false; RTLDRARCH enmArch = RTLDRARCH_WHATEVER; RTGETOPTUNION ValueUnion; RTGETOPTSTATE GetState; RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, 0); while ((rc = RTGetOpt(&GetState, &ValueUnion))) { switch (rc) { case 'i': rc = RTStrmOpen(ValueUnion.psz, "r", &pInput); if (RT_FAILURE(rc)) return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to open '%s' for reading: %Rrc", ValueUnion.psz, rc); break; case 'c': fCacheFile = true; break; case 'l': fCacheFile = false; break; case 'p': enmOpenMethod = kOpenMethod_FromPeImage; break; case 'v': cVerbosityLevel++; break; case '8': enmArch = RTLDRARCH_X86_32; break; case '6': enmArch = RTLDRARCH_AMD64; break; case '*': enmArch = RTLDRARCH_WHATEVER; break; case 'h': RTPrintf("Usage: %s [options] <module> <address> [<module> <address> [..]]\n" "\n" "Options:\n" " -i,--input=file\n" " Specify a input file instead of standard input.\n" " --pe-image\n" " Use RTDbgModCreateFromPeImage to open the file." " -v, --verbose\n" " Display the address space before doing the filtering.\n" " --amd64,--x86,--whatever\n" " Selects the desired architecture.\n" " -h, -?, --help\n" " Display this help text and exit successfully.\n" " -V, --version\n" " Display the revision and exit successfully.\n" , RTPathFilename(argv[0])); return RTEXITCODE_SUCCESS; case 'V': RTPrintf("$Revision$\n"); return RTEXITCODE_SUCCESS; case VINF_GETOPT_NOT_OPTION: { /* <module> <address> */ const char *pszModule = ValueUnion.psz; rc = RTGetOptFetchValue(&GetState, &ValueUnion, RTGETOPT_REQ_UINT64 | RTGETOPT_FLAG_HEX); if (RT_FAILURE(rc)) return RTGetOptPrintError(rc, &ValueUnion); uint64_t u64Address = ValueUnion.u64; uint32_t cbImage = 0; uint32_t uTimestamp = 0; if (fCacheFile) { rc = RTGetOptFetchValue(&GetState, &ValueUnion, RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_HEX); if (RT_FAILURE(rc)) return RTGetOptPrintError(rc, &ValueUnion); cbImage = ValueUnion.u32; rc = RTGetOptFetchValue(&GetState, &ValueUnion, RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_HEX); if (RT_FAILURE(rc)) return RTGetOptPrintError(rc, &ValueUnion); uTimestamp = ValueUnion.u32; } RTDBGMOD hMod; if (enmOpenMethod == kOpenMethod_FromImage) rc = RTDbgModCreateFromImage(&hMod, pszModule, NULL, enmArch, hDbgCfg); else rc = RTDbgModCreateFromPeImage(&hMod, pszModule, NULL, NIL_RTLDRMOD, cbImage, uTimestamp, hDbgCfg); if (RT_FAILURE(rc)) return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTDbgModCreateFromImage(,%s,,) -> %Rrc", pszModule, rc); rc = RTDbgAsModuleLink(hDbgAs, hMod, u64Address, 0 /* fFlags */); if (RT_FAILURE(rc)) return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTDbgAsModuleLink(,%s,%llx,) -> %Rrc", pszModule, u64Address, rc); break; } default: return RTGetOptPrintError(rc, &ValueUnion); } } /* * Display the address space. */ if (cVerbosityLevel) { RTPrintf("*** Address Space Dump ***\n"); uint32_t cModules = RTDbgAsModuleCount(hDbgAs); for (uint32_t iModule = 0; iModule < cModules; iModule++) { RTDBGMOD hDbgMod = RTDbgAsModuleByIndex(hDbgAs, iModule); RTPrintf("Module #%u: %s\n", iModule, RTDbgModName(hDbgMod)); RTDBGASMAPINFO aMappings[128]; uint32_t cMappings = RT_ELEMENTS(aMappings); rc = RTDbgAsModuleQueryMapByIndex(hDbgAs, iModule, &aMappings[0], &cMappings, 0 /*fFlags*/); if (RT_SUCCESS(rc)) { for (uint32_t iMapping = 0; iMapping < cMappings; iMapping++) { if (aMappings[iMapping].iSeg == NIL_RTDBGSEGIDX) RTPrintf(" mapping #%u: %RTptr-%RTptr\n", iMapping, aMappings[iMapping].Address, aMappings[iMapping].Address + RTDbgModImageSize(hDbgMod) - 1); else { RTDBGSEGMENT SegInfo; rc = RTDbgModSegmentByIndex(hDbgMod, aMappings[iMapping].iSeg, &SegInfo); if (RT_SUCCESS(rc)) RTPrintf(" mapping #%u: %RTptr-%RTptr (segment #%u - '%s')", iMapping, aMappings[iMapping].Address, aMappings[iMapping].Address + SegInfo.cb, SegInfo.iSeg, SegInfo.szName); else RTPrintf(" mapping #%u: %RTptr-???????? (segment #%u)", iMapping, aMappings[iMapping].Address); } if (cVerbosityLevel > 1) { uint32_t cSymbols = RTDbgModSymbolCount(hDbgMod); RTPrintf(" %u symbols\n", cSymbols); for (uint32_t iSymbol = 0; iSymbol < cSymbols; iSymbol++) { RTDBGSYMBOL SymInfo; rc = RTDbgModSymbolByOrdinal(hDbgMod, iSymbol, &SymInfo); if (RT_SUCCESS(rc)) RTPrintf(" #%04u at %08x:%RTptr %05llx %s\n", SymInfo.iOrdinal, SymInfo.iSeg, SymInfo.offSeg, (uint64_t)SymInfo.cb, SymInfo.szName); } } } } else RTMsgError("RTDbgAsModuleQueryMapByIndex failed: %Rrc", rc); RTDbgModRelease(hDbgMod); } RTPrintf("*** End of Address Space Dump ***\n"); } /* * Read text from standard input and see if there is anything we can translate. */ for (;;) { /* Get a line. */ char szLine[_64K]; rc = RTStrmGetLine(pInput, szLine, sizeof(szLine)); if (rc == VERR_EOF) break; if (RT_FAILURE(rc)) return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTStrmGetLine() -> %Rrc\n", rc); /* * Search the line for potential addresses and replace them with * symbols+offset. */ const char *pszStart = szLine; const char *psz = szLine; char ch; while ((ch = *psz) != '\0') { size_t cchAddress; uint64_t u64Address; if ( ( ch == '0' && (psz[1] == 'x' || psz[1] == 'X') && TryParseAddress(psz, &cchAddress, &u64Address)) || ( RT_C_IS_XDIGIT(ch) && TryParseAddress(psz, &cchAddress, &u64Address)) ) { /* Print. */ psz += cchAddress; if (pszStart != psz) RTStrmWrite(pOutput, pszStart, psz - pszStart); pszStart = psz; /* Try get the module. */ RTUINTPTR uAddr; RTDBGSEGIDX iSeg; RTDBGMOD hDbgMod; rc = RTDbgAsModuleByAddr(hDbgAs, u64Address, &hDbgMod, &uAddr, &iSeg); if (RT_SUCCESS(rc)) { if (iSeg != UINT32_MAX) RTStrmPrintf(pOutput, "=[%s:%u", RTDbgModName(hDbgMod), iSeg); else RTStrmPrintf(pOutput, "=[%s", RTDbgModName(hDbgMod), iSeg); /* * Do we have symbols? */ RTDBGSYMBOL Symbol; RTINTPTR offSym; rc = RTDbgAsSymbolByAddr(hDbgAs, u64Address, RTDBGSYMADDR_FLAGS_LESS_OR_EQUAL, &offSym, &Symbol, NULL); if (RT_SUCCESS(rc)) { if (!offSym) RTStrmPrintf(pOutput, "!%s", Symbol.szName); else if (offSym > 0) RTStrmPrintf(pOutput, "!%s+%#llx", Symbol.szName, offSym); else RTStrmPrintf(pOutput, "!%s-%#llx", Symbol.szName, -offSym); } else RTStrmPrintf(pOutput, "+%#llx", u64Address - uAddr); /* * Do we have line numbers? */ RTDBGLINE Line; RTINTPTR offLine; rc = RTDbgAsLineByAddr(hDbgAs, u64Address, &offLine, &Line, NULL); if (RT_SUCCESS(rc)) RTStrmPrintf(pOutput, " %Rbn(%u)", Line.szFilename, Line.uLineNo); RTStrmPrintf(pOutput, "]"); RTDbgModRelease(hDbgMod); } } else psz++; } if (pszStart != psz) RTStrmWrite(pOutput, pszStart, psz - pszStart); RTStrmPutCh(pOutput, '\n'); } return RTEXITCODE_SUCCESS; }
/** The actual implemenation code for @a createServiceFile. */ bool createServiceFileCore(char **ppachTemplate, struct SERVICEPARAMETERS *pParameters) { /* The size of the template data we have read. */ size_t cchTemplate = 0; /* The size of the buffer we have allocated. */ size_t cbBuffer = 0; /* How much of the template data we have written out. */ size_t cchWritten = 0; int rc = VINF_SUCCESS; /* First of all read in the file. */ while (rc != VINF_EOF) { size_t cchRead; if (cchTemplate == cbBuffer) { cbBuffer += READ_SIZE; *ppachTemplate = (char *)RTMemRealloc((void *)*ppachTemplate, cbBuffer); } if (!*ppachTemplate) { RTStrmPrintf(g_pStdErr, "Out of memory.\n"); return false; } rc = RTStrmReadEx(g_pStdIn, *ppachTemplate + cchTemplate, cbBuffer - cchTemplate, &cchRead); if (RT_FAILURE(rc)) { RTStrmPrintf(g_pStdErr, "Error reading input: %Rrc\n", rc); return false; } if (!cchRead) rc = VINF_EOF; cchTemplate += cchRead; } while (true) { /* Find the next '%' character if any and write out up to there (or the * end if there is no '%'). */ char *pchNext = (char *) memchr((void *)(*ppachTemplate + cchWritten), '%', cchTemplate - cchWritten); size_t cchToWrite = pchNext ? pchNext - *ppachTemplate - cchWritten : cchTemplate - cchWritten; rc = RTStrmWrite(g_pStdOut, *ppachTemplate + cchWritten, cchToWrite); if (RT_FAILURE(rc)) { RTStrmPrintf(g_pStdErr, "Error writing output: %Rrc\n", rc); return false; } cchWritten += cchToWrite; if (!pchNext) break; /* And substitute any of our well-known strings. We favour code * readability over efficiency here. */ if (getSequence(*ppachTemplate, cchTemplate, &cchWritten, COMMAND, sizeof(COMMAND) - 1)) { if (!pParameters->pcszCommand) { RTStrmPrintf(g_pStdErr, "--command not specified.\n"); return false; } if (!writeCommand(pParameters->enmFormat, pParameters->pcszCommand)) return false; } else if (getSequence(*ppachTemplate, cchTemplate, &cchWritten, ARGUMENTS, sizeof(ARGUMENTS) - 1)) { if ( pParameters->pcszArguments && !writeQuoted(pParameters->enmFormat, pParameters->pcszArguments)) return false; } else if (getSequence(*ppachTemplate, cchTemplate, &cchWritten, DESCRIPTION, sizeof(DESCRIPTION) - 1)) { if (!pParameters->pcszDescription) { RTStrmPrintf(g_pStdErr, "--description not specified.\n"); return false; } if (!writePrintableString(pParameters->enmFormat, pParameters->pcszDescription)) return false; } else if (getSequence(*ppachTemplate, cchTemplate, &cchWritten, SERVICE_NAME, sizeof(SERVICE_NAME) - 1)) { if ( !pParameters->pcszCommand && !pParameters->pcszServiceName) { RTStrmPrintf(g_pStdErr, "Neither --command nor --service-name specified.\n"); return false; } if (pParameters->pcszServiceName) { if (!writePrintableString(pParameters->enmFormat, pParameters->pcszServiceName)) return false; } else { const char *pcszFileName = RTPathFilename(pParameters->pcszCommand); const char *pcszSuffix = RTPathSuffix(pParameters->pcszCommand); char *pszName = RTStrDupN(pcszFileName, pcszSuffix ? pcszSuffix - pcszFileName : RTPATH_MAX); bool fRc; if (!pszName) { RTStrmPrintf(g_pStdErr, "Out of memory.\n"); return false; } fRc = writePrintableString(pParameters->enmFormat, pszName); RTStrFree(pszName); if (!fRc) return false; } } else if (getSequence(*ppachTemplate, cchTemplate, &cchWritten, HAVE_ONESHOT, sizeof(HAVE_ONESHOT) - 1)) { if (!pParameters->fOneShot) skipLine(*ppachTemplate, cchTemplate, &cchWritten); } else if (getSequence(*ppachTemplate, cchTemplate, &cchWritten, HAVE_DAEMON, sizeof(HAVE_DAEMON) - 1)) { if (pParameters->fOneShot) skipLine(*ppachTemplate, cchTemplate, &cchWritten); } else if (getSequence(*ppachTemplate, cchTemplate, &cchWritten, STOP_COMMAND, sizeof(STOP_COMMAND) - 1)) { if ( pParameters->pcszStopCommand && !writeCommand(pParameters->enmFormat, pParameters->pcszStopCommand)) return false; } else if (getSequence(*ppachTemplate, cchTemplate, &cchWritten, STOP_ARGUMENTS, sizeof(STOP_ARGUMENTS) - 1)) { if ( pParameters->pcszStopArguments && !writeQuoted(pParameters->enmFormat, pParameters->pcszStopArguments)) return false; } else if (getSequence(*ppachTemplate, cchTemplate, &cchWritten, HAVE_STOP_COMMAND, sizeof(HAVE_STOP_COMMAND) - 1)) { if (!pParameters->pcszStopCommand) skipLine(*ppachTemplate, cchTemplate, &cchWritten); } else if (getSequence(*ppachTemplate, cchTemplate, &cchWritten, NO_STOP_COMMAND, sizeof(NO_STOP_COMMAND) - 1)) { if (pParameters->pcszStopCommand) skipLine(*ppachTemplate, cchTemplate, &cchWritten); } else if (getSequence(*ppachTemplate, cchTemplate, &cchWritten, STATUS_COMMAND, sizeof(STATUS_COMMAND) - 1)) { if ( pParameters->pcszStatusCommand && !writeCommand(pParameters->enmFormat, pParameters->pcszStatusCommand)) return false; } else if (getSequence(*ppachTemplate, cchTemplate, &cchWritten, STATUS_ARGUMENTS, sizeof(STATUS_ARGUMENTS) - 1)) { if ( pParameters->pcszStatusArguments && !writeQuoted(pParameters->enmFormat, pParameters->pcszStatusArguments)) return false; } else if (getSequence(*ppachTemplate, cchTemplate, &cchWritten, HAVE_STATUS_COMMAND, sizeof(HAVE_STATUS_COMMAND) - 1)) { if (!pParameters->pcszStatusCommand) skipLine(*ppachTemplate, cchTemplate, &cchWritten); } else if (getSequence(*ppachTemplate, cchTemplate, &cchWritten, NO_STATUS_COMMAND, sizeof(NO_STATUS_COMMAND) - 1)) { if (pParameters->pcszStatusCommand) skipLine(*ppachTemplate, cchTemplate, &cchWritten); } else if (getSequence(*ppachTemplate, cchTemplate, &cchWritten, "%%", 2)) { rc = RTStrmPutCh(g_pStdOut, '%'); if (RT_FAILURE(rc)) { RTStrmPrintf(g_pStdErr, "Error writing output: %Rrc\n", rc); return false; } } else { RTStrmPrintf(g_pStdErr, "Unknown substitution sequence in input at \"%.*s\"\n", RT_MIN(16, cchTemplate - cchWritten), *ppachTemplate + cchWritten); return false; } } return true; }