Beispiel #1
0
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;
}
int main()
{
    RTTEST hTest;
    int rc = RTTestInitAndCreate("tstRTGetOpt", &hTest);
    if (rc)
        return rc;

    RTGETOPTSTATE GetState;
    RTGETOPTUNION Val;
#define CHECK(expr)  do { if (!(expr)) { RTTestIFailed("error line %d (iNext=%d): %s\n", __LINE__, GetState.iNext, #expr); } } while (0)
#define CHECK2(expr, fmt) \
    do { \
        if (!(expr)) { \
            RTTestIFailed("error line %d (iNext=%d): %s\n", __LINE__, GetState.iNext, #expr); \
            RTTestIFailureDetails fmt; \
         } \
    } while (0)

#define CHECK_pDef(paOpts, i) \
    CHECK2(Val.pDef == &(paOpts)[(i)], ("Got #%d (%p) expected #%d\n", (int)(Val.pDef - &(paOpts)[0]), Val.pDef, i));

#define CHECK_GETOPT(expr, chRet, iInc) \
    do { \
        const int iPrev = GetState.iNext; \
        const int rcGetOpt = (expr); \
        CHECK2(rcGetOpt == (chRet), ("got %d, expected %d\n", rcGetOpt, (chRet))); \
        CHECK2(GetState.iNext == (iInc) + iPrev, ("iNext=%d expected %d\n", GetState.iNext, (iInc) + iPrev)); \
        GetState.iNext = (iInc) + iPrev; \
    } while (0)

#define CHECK_GETOPT_STR(expr, chRet, iInc, str) \
    do { \
        const int iPrev = GetState.iNext; \
        const int rcGetOpt = (expr); \
        CHECK2(rcGetOpt == (chRet), ("got %d, expected %d\n", rcGetOpt, (chRet))); \
        CHECK2(GetState.iNext == (iInc) + iPrev, ("iNext=%d expected %d\n", GetState.iNext, (iInc) + iPrev)); \
        CHECK2(VALID_PTR(Val.psz) && !strcmp(Val.psz, (str)), ("got %s, expected %s\n", Val.psz, (str))); \
        GetState.iNext = (iInc) + iPrev; \
    } while (0)


    /*
     * The basics.
     */
    RTTestSub(hTest, "Basics");
    static const RTGETOPTDEF s_aOpts2[] =
    {
        { "--optwithstring",    's', RTGETOPT_REQ_STRING },
        { "--optwithint",       'i', RTGETOPT_REQ_INT32 },
        { "--verbose",          'v', RTGETOPT_REQ_NOTHING },
        { NULL,                 'q', RTGETOPT_REQ_NOTHING },
        { "--quiet",            384, RTGETOPT_REQ_NOTHING },
        { "-novalue",           385, RTGETOPT_REQ_NOTHING },
        { "-startvm",           386, RTGETOPT_REQ_STRING },
        { "nodash",             387, RTGETOPT_REQ_NOTHING },
        { "nodashval",          388, RTGETOPT_REQ_STRING },
        { "--gateway",          'g', RTGETOPT_REQ_IPV4ADDR },
        { "--mac",              'm', RTGETOPT_REQ_MACADDR },
        { "--strindex",         400, RTGETOPT_REQ_STRING  | RTGETOPT_FLAG_INDEX },
        { "strindex",           400, RTGETOPT_REQ_STRING  | RTGETOPT_FLAG_INDEX },
        { "--intindex",         401, RTGETOPT_REQ_INT32   | RTGETOPT_FLAG_INDEX },
        { "--macindex",         402, RTGETOPT_REQ_MACADDR | RTGETOPT_FLAG_INDEX },
        { "--indexnovalue",     403, RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_INDEX },
        { "--macindexnegative", 404, RTGETOPT_REQ_NOTHING },
        { "--twovalues",        405, RTGETOPT_REQ_STRING },
        { "--twovaluesindex",   406, RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_INDEX },
        { "--threevalues",      407, RTGETOPT_REQ_UINT32 },
        { "--boolean",          408, RTGETOPT_REQ_BOOL_ONOFF },
        { "--booleanindex",     409, RTGETOPT_REQ_BOOL_ONOFF | RTGETOPT_FLAG_INDEX },
    };

    const char *argv2[] =
    {
        "-s",               "string1",
        "-sstring2",
        "-s:string3",
        "-s=string4",
        "-s:",
        "-s=",
        "--optwithstring",  "string5",
        "--optwithstring:string6",
        "--optwithstring=string7",
        "--optwithstring:",
        "--optwithstring=",

        "-i",               "-42",
        "-i:-42",
        "-i=-42",

        "--optwithint",     "42",
        "--optwithint:42",
        "--optwithint=42",

        "-v",
        "--verbose",
        "-q",
        "--quiet",

        "-novalue",
        "-startvm",         "myvm",

        "nodash",
        "nodashval",        "string9",

        "filename1",
        "-q",
        "filename2",

        "-vqi999",

        "-g192.168.1.1",

        "-m08:0:27:00:ab:f3",
        "--mac:1:::::c",

        "--strindex786",    "string10",
        "--strindex786:string11",
        "--strindex786=string12",
        "strindex687",      "string13",
        "strindex687:string14",
        "strindex687=string15",
        "strindex688:",
        "strindex689=",
        "--intindex137",    "1000",
        "--macindex138",    "08:0:27:00:ab:f3",
        "--indexnovalue1",
        "--macindexnegative",

        "--twovalues",       "firstvalue", "secondvalue",
        "--twovalues:firstvalue",          "secondvalue",
        "--twovaluesindex4", "1",          "0xA",
        "--twovaluesindex5=2",             "0xB",
        "--threevalues",     "1",          "0xC",          "thirdvalue",

        /* bool on/off */
        "--boolean",         "on",
        "--boolean",         "off",
        "--boolean",         "invalid",
        "--booleanindex2",   "on",
        "--booleanindex7",   "off",
        "--booleanindex9",   "invalid",

        /* standard options */
        "--help",
        "-help",
        "-?",
        "-h",
        "--version",
        "-version",
        "-V",

        /* done */
        NULL
    };
    int argc2 = (int)RT_ELEMENTS(argv2) - 1;

    CHECK(RT_SUCCESS(RTGetOptInit(&GetState, argc2, (char **)argv2, &s_aOpts2[0], RT_ELEMENTS(s_aOpts2), 0, 0 /* fFlags */)));

    CHECK_GETOPT(RTGetOpt(&GetState, &Val), 's', 2);
    CHECK(VALID_PTR(Val.psz) && !strcmp(Val.psz, "string1"));
    CHECK(GetState.uIndex == UINT32_MAX);
    CHECK_GETOPT(RTGetOpt(&GetState, &Val), 's', 1);
    CHECK(VALID_PTR(Val.psz) && !strcmp(Val.psz, "string2"));
    CHECK(GetState.uIndex == UINT32_MAX);
    CHECK_GETOPT(RTGetOpt(&GetState, &Val), 's', 1);
    CHECK(VALID_PTR(Val.psz) && !strcmp(Val.psz, "string3"));
    CHECK(GetState.uIndex == UINT32_MAX);
    CHECK_GETOPT(RTGetOpt(&GetState, &Val), 's', 1);
    CHECK(VALID_PTR(Val.psz) && !strcmp(Val.psz, "string4"));
    CHECK(GetState.uIndex == UINT32_MAX);
    CHECK_GETOPT(RTGetOpt(&GetState, &Val), 's', 1);
    CHECK(VALID_PTR(Val.psz) && !strcmp(Val.psz, ""));
    CHECK(GetState.uIndex == UINT32_MAX);
    CHECK_GETOPT(RTGetOpt(&GetState, &Val), 's', 1);
    CHECK(VALID_PTR(Val.psz) && !strcmp(Val.psz, ""));
    CHECK(GetState.uIndex == UINT32_MAX);
    CHECK_GETOPT(RTGetOpt(&GetState, &Val), 's', 2);
    CHECK(VALID_PTR(Val.psz) && !strcmp(Val.psz, "string5"));
    CHECK(GetState.uIndex == UINT32_MAX);
    CHECK_GETOPT(RTGetOpt(&GetState, &Val), 's', 1);
    CHECK(VALID_PTR(Val.psz) && !strcmp(Val.psz, "string6"));
    CHECK(GetState.uIndex == UINT32_MAX);
    CHECK_GETOPT(RTGetOpt(&GetState, &Val), 's', 1);
    CHECK(VALID_PTR(Val.psz) && !strcmp(Val.psz, "string7"));
    CHECK(GetState.uIndex == UINT32_MAX);
    CHECK_GETOPT(RTGetOpt(&GetState, &Val), 's', 1);
    CHECK(VALID_PTR(Val.psz) && !strcmp(Val.psz, ""));
    CHECK(GetState.uIndex == UINT32_MAX);
    CHECK_GETOPT(RTGetOpt(&GetState, &Val), 's', 1);
    CHECK(VALID_PTR(Val.psz) && !strcmp(Val.psz, ""));
    CHECK(GetState.uIndex == UINT32_MAX);

    /* -i */
    CHECK_GETOPT(RTGetOpt(&GetState, &Val), 'i', 2);
    CHECK(Val.i32 == -42);
    CHECK_GETOPT(RTGetOpt(&GetState, &Val), 'i', 1);
    CHECK(Val.i32 == -42);
    CHECK_GETOPT(RTGetOpt(&GetState, &Val), 'i', 1);
    CHECK(Val.i32 == -42);

    /* --optwithint */
    CHECK_GETOPT(RTGetOpt(&GetState, &Val), 'i', 2);
    CHECK(Val.i32 == 42);
    CHECK_GETOPT(RTGetOpt(&GetState, &Val), 'i', 1);
    CHECK(Val.i32 == 42);
    CHECK_GETOPT(RTGetOpt(&GetState, &Val), 'i', 1);
    CHECK(Val.i32 == 42);

    CHECK_GETOPT(RTGetOpt(&GetState, &Val), 'v', 1);
    CHECK_pDef(s_aOpts2, 2);
    CHECK_GETOPT(RTGetOpt(&GetState, &Val), 'v', 1);
    CHECK_pDef(s_aOpts2, 2);

    CHECK_GETOPT(RTGetOpt(&GetState, &Val), 'q', 1);
    CHECK_pDef(s_aOpts2, 3);
    CHECK_GETOPT(RTGetOpt(&GetState, &Val), 384, 1);
    CHECK_pDef(s_aOpts2, 4);

    /* -novalue / -startvm (single dash long options) */
    CHECK_GETOPT(RTGetOpt(&GetState, &Val), 385, 1);
    CHECK_pDef(s_aOpts2, 5);
    CHECK_GETOPT(RTGetOpt(&GetState, &Val), 386, 2);
    CHECK(VALID_PTR(Val.psz) && !strcmp(Val.psz, "myvm"));

    /* no-dash options */
    CHECK_GETOPT(RTGetOpt(&GetState, &Val), 387, 1);
    CHECK_pDef(s_aOpts2, 7);
    CHECK_GETOPT(RTGetOpt(&GetState, &Val), 388, 2);
    CHECK(VALID_PTR(Val.psz) && !strcmp(Val.psz, "string9"));

    /* non-option, option, non-option  */
    CHECK_GETOPT(RTGetOpt(&GetState, &Val), VINF_GETOPT_NOT_OPTION, 1);
    CHECK(Val.psz && !strcmp(Val.psz, "filename1"));
    CHECK_GETOPT(RTGetOpt(&GetState, &Val), 'q', 1);
    CHECK_pDef(s_aOpts2, 3);
    CHECK_GETOPT(RTGetOpt(&GetState, &Val), VINF_GETOPT_NOT_OPTION, 1);
    CHECK(Val.psz && !strcmp(Val.psz, "filename2"));

    /* compress short options */
    CHECK_GETOPT(RTGetOpt(&GetState, &Val), 'v', 0);
    CHECK_pDef(s_aOpts2, 2);
    CHECK_GETOPT(RTGetOpt(&GetState, &Val), 'q', 0);
    CHECK_pDef(s_aOpts2, 3);
    CHECK_GETOPT(RTGetOpt(&GetState, &Val), 'i', 1);
    CHECK(Val.i32 == 999);

    /* IPv4 */
    RTTestSub(hTest, "RTGetOpt - IPv4");
    CHECK_GETOPT(RTGetOpt(&GetState, &Val), 'g', 1);
    CHECK(Val.IPv4Addr.u == RT_H2N_U32_C(RT_BSWAP_U32_C(RT_MAKE_U32_FROM_U8(192,168,1,1))));

    /* Ethernet MAC address. */
    RTTestSub(hTest, "RTGetOpt - MAC Address");
    CHECK_GETOPT(RTGetOpt(&GetState, &Val), 'm', 1);
    CHECK(   Val.MacAddr.au8[0] == 0x08
          && Val.MacAddr.au8[1] == 0x00
          && Val.MacAddr.au8[2] == 0x27
          && Val.MacAddr.au8[3] == 0x00
          && Val.MacAddr.au8[4] == 0xab
          && Val.MacAddr.au8[5] == 0xf3);
    CHECK_GETOPT(RTGetOpt(&GetState, &Val), 'm', 1);
    CHECK(   Val.MacAddr.au8[0] == 0x01
          && Val.MacAddr.au8[1] == 0x00
          && Val.MacAddr.au8[2] == 0x00
          && Val.MacAddr.au8[3] == 0x00
          && Val.MacAddr.au8[4] == 0x00
          && Val.MacAddr.au8[5] == 0x0c);

    /* string with indexed argument */
    RTTestSub(hTest, "RTGetOpt - Option w/ Index");
    CHECK_GETOPT(RTGetOpt(&GetState, &Val), 400, 2);
    CHECK(VALID_PTR(Val.psz) && !strcmp(Val.psz, "string10"));
    CHECK(GetState.uIndex == 786);

    CHECK_GETOPT(RTGetOpt(&GetState, &Val), 400, 1);
    CHECK(VALID_PTR(Val.psz) && !strcmp(Val.psz, "string11"));
    CHECK(GetState.uIndex == 786);

    CHECK_GETOPT(RTGetOpt(&GetState, &Val), 400, 1);
    CHECK(VALID_PTR(Val.psz) && !strcmp(Val.psz, "string12"));
    CHECK(GetState.uIndex == 786);

    CHECK_GETOPT(RTGetOpt(&GetState, &Val), 400, 2);
    CHECK(VALID_PTR(Val.psz) && !strcmp(Val.psz, "string13"));
    CHECK(GetState.uIndex == 687);

    CHECK_GETOPT(RTGetOpt(&GetState, &Val), 400, 1);
    CHECK(VALID_PTR(Val.psz) && !strcmp(Val.psz, "string14"));
    CHECK(GetState.uIndex == 687);

    CHECK_GETOPT(RTGetOpt(&GetState, &Val), 400, 1);
    CHECK(VALID_PTR(Val.psz) && !strcmp(Val.psz, "string15"));
    CHECK(GetState.uIndex == 687);

    CHECK_GETOPT(RTGetOpt(&GetState, &Val), 400, 1);
    CHECK(VALID_PTR(Val.psz) && !strcmp(Val.psz, ""));
    CHECK(GetState.uIndex == 688);

    CHECK_GETOPT(RTGetOpt(&GetState, &Val), 400, 1);
    CHECK(VALID_PTR(Val.psz) && !strcmp(Val.psz, ""));
    CHECK(GetState.uIndex == 689);

    CHECK_GETOPT(RTGetOpt(&GetState, &Val), 401, 2);
    CHECK(Val.i32 == 1000);
    CHECK(GetState.uIndex == 137);

    CHECK_GETOPT(RTGetOpt(&GetState, &Val), 402, 2);
    CHECK(   Val.MacAddr.au8[0] == 0x08
          && Val.MacAddr.au8[1] == 0x00
          && Val.MacAddr.au8[2] == 0x27
          && Val.MacAddr.au8[3] == 0x00
          && Val.MacAddr.au8[4] == 0xab
          && Val.MacAddr.au8[5] == 0xf3);
    CHECK(GetState.uIndex == 138);

    CHECK_GETOPT(RTGetOpt(&GetState, &Val), 403, 1);
    CHECK(GetState.uIndex == 1);

    CHECK_GETOPT(RTGetOpt(&GetState, &Val), 404, 1);
    CHECK(GetState.uIndex == UINT32_MAX);

    /* RTGetOptFetchValue tests */
    RTTestSub(hTest, "RTGetOptFetchValue");
    CHECK_GETOPT(RTGetOpt(&GetState, &Val), 405, 2);
    CHECK(VALID_PTR(Val.psz) && !strcmp(Val.psz, "firstvalue"));
    CHECK(GetState.uIndex == UINT32_MAX);
    CHECK_GETOPT(RTGetOptFetchValue(&GetState, &Val, RTGETOPT_REQ_STRING), VINF_SUCCESS, 1);
    CHECK(VALID_PTR(Val.psz) && !strcmp(Val.psz, "secondvalue"));
    CHECK(GetState.uIndex == UINT32_MAX);

    CHECK_GETOPT(RTGetOpt(&GetState, &Val), 405, 1);
    CHECK(VALID_PTR(Val.psz) && !strcmp(Val.psz, "firstvalue"));
    CHECK(GetState.uIndex == UINT32_MAX);
    CHECK_GETOPT(RTGetOptFetchValue(&GetState, &Val, RTGETOPT_REQ_STRING), VINF_SUCCESS, 1);
    CHECK(VALID_PTR(Val.psz) && !strcmp(Val.psz, "secondvalue"));
    CHECK(GetState.uIndex == UINT32_MAX);

    CHECK_GETOPT(RTGetOpt(&GetState, &Val), 406, 2);
    CHECK(Val.u32 == 1);
    CHECK(GetState.uIndex == 4);
    CHECK_GETOPT(RTGetOptFetchValue(&GetState, &Val, RTGETOPT_REQ_UINT32), VINF_SUCCESS, 1);
    CHECK(Val.u32 == 10);
    CHECK(GetState.uIndex == 4);

    CHECK_GETOPT(RTGetOpt(&GetState, &Val), 406, 1);
    CHECK(Val.u32 == 2);
    CHECK(GetState.uIndex == 5);
    CHECK_GETOPT(RTGetOptFetchValue(&GetState, &Val, RTGETOPT_REQ_UINT32), VINF_SUCCESS, 1);
    CHECK(Val.u32 == 11);
    CHECK(GetState.uIndex == 5);

    CHECK_GETOPT(RTGetOpt(&GetState, &Val), 407, 2);
    CHECK(Val.u32 == 1);
    CHECK(GetState.uIndex == UINT32_MAX);
    CHECK_GETOPT(RTGetOptFetchValue(&GetState, &Val, RTGETOPT_REQ_UINT32), VINF_SUCCESS, 1);
    CHECK(Val.u32 == 12);
    CHECK(GetState.uIndex == UINT32_MAX);
    CHECK_GETOPT(RTGetOptFetchValue(&GetState, &Val, RTGETOPT_REQ_STRING), VINF_SUCCESS, 1);
    CHECK(VALID_PTR(Val.psz) && !strcmp(Val.psz, "thirdvalue"));
    CHECK(GetState.uIndex == UINT32_MAX);

    /* bool on/off tests */
    RTTestSub(hTest, "RTGetOpt - bool on/off");
    CHECK_GETOPT(RTGetOpt(&GetState, &Val), 408, 2);
    CHECK(Val.f);
    CHECK_GETOPT(RTGetOpt(&GetState, &Val), 408, 2);
    CHECK(!Val.f);
    CHECK_GETOPT(RTGetOpt(&GetState, &Val), VERR_GETOPT_UNKNOWN_OPTION, 2);
    CHECK(VALID_PTR(Val.psz) && !strcmp(Val.psz, "invalid"));

    /* bool on/off with indexed argument */
    CHECK_GETOPT(RTGetOpt(&GetState, &Val), 409, 2);
    CHECK(Val.f);
    CHECK(GetState.uIndex == 2);
    CHECK_GETOPT(RTGetOpt(&GetState, &Val), 409, 2);
    CHECK(!Val.f);
    CHECK(GetState.uIndex == 7);
    CHECK_GETOPT(RTGetOpt(&GetState, &Val), VERR_GETOPT_UNKNOWN_OPTION, 2);
    CHECK(VALID_PTR(Val.psz) && !strcmp(Val.psz, "invalid"));

    /* standard options. */
    RTTestSub(hTest, "Standard options");
    CHECK_GETOPT(RTGetOpt(&GetState, &Val), 'h', 1);
    CHECK_GETOPT(RTGetOpt(&GetState, &Val), 'h', 1);
    CHECK_GETOPT(RTGetOpt(&GetState, &Val), 'h', 1);
    CHECK_GETOPT(RTGetOpt(&GetState, &Val), 'h', 1);
    CHECK_GETOPT(RTGetOpt(&GetState, &Val), 'V', 1);
    CHECK_GETOPT(RTGetOpt(&GetState, &Val), 'V', 1);
    CHECK_GETOPT(RTGetOpt(&GetState, &Val), 'V', 1);

    /* the end */
    CHECK_GETOPT(RTGetOpt(&GetState, &Val), 0, 0);
    CHECK(Val.pDef == NULL);
    CHECK(argc2 == GetState.iNext);

    /*
     * Options first.
     */
    RTTestSub(hTest, "Options first");
    const char *argv3[] =
    {
        "foo1",
        "-s",               "string1",
        "foo2",
        "--optwithstring",  "string2",
        "foo3",
        "-i",               "-42",
        "foo4",
        "-i:-42",
        "-i=-42",
        "foo5",
        "foo6",
        "foo7",
        "-i:-42",
        "-i=-42",
        "foo8",
        "--twovalues",       "firstvalue", "secondvalue",
        "foo9",
        "--twovalues:firstvalue",          "secondvalue",
        "foo10",
        "--",
        "--optwithstring",
        "foo11",
        "foo12",

        /* done */
        NULL
    };
    int argc3 = (int)RT_ELEMENTS(argv3) - 1;

    CHECK(RT_SUCCESS(RTGetOptInit(&GetState, argc3, (char **)argv3, &s_aOpts2[0], RT_ELEMENTS(s_aOpts2), 0,
                                  RTGETOPTINIT_FLAGS_OPTS_FIRST)));

    /* -s */
    CHECK_GETOPT_STR(RTGetOpt(&GetState, &Val), 's', 2, "string1");
    CHECK(GetState.uIndex == UINT32_MAX);
    CHECK_GETOPT_STR(RTGetOpt(&GetState, &Val), 's', 2, "string2");
    CHECK(GetState.uIndex == UINT32_MAX);

    /* -i */
    CHECK_GETOPT(RTGetOpt(&GetState, &Val), 'i', 2);
    CHECK(Val.i32 == -42);
    CHECK_GETOPT(RTGetOpt(&GetState, &Val), 'i', 1);
    CHECK(Val.i32 == -42);
    CHECK_GETOPT(RTGetOpt(&GetState, &Val), 'i', 1);
    CHECK(Val.i32 == -42);
    CHECK_GETOPT(RTGetOpt(&GetState, &Val), 'i', 1);
    CHECK(Val.i32 == -42);
    CHECK_GETOPT(RTGetOpt(&GetState, &Val), 'i', 1);
    CHECK(Val.i32 == -42);

    /* --twovalues */
    CHECK_GETOPT_STR(RTGetOpt(&GetState, &Val), 405, 2, "firstvalue");
    CHECK(GetState.uIndex == UINT32_MAX);
    CHECK_GETOPT_STR(RTGetOptFetchValue(&GetState, &Val, RTGETOPT_REQ_STRING), VINF_SUCCESS, 1, "secondvalue");
    CHECK(GetState.uIndex == UINT32_MAX);

    CHECK_GETOPT_STR(RTGetOpt(&GetState, &Val), 405, 1, "firstvalue");
    CHECK(GetState.uIndex == UINT32_MAX);
    CHECK_GETOPT_STR(RTGetOptFetchValue(&GetState, &Val, RTGETOPT_REQ_STRING), VINF_SUCCESS, 1, "secondvalue");
    CHECK(GetState.uIndex == UINT32_MAX);

    /* -- */
    CHECK_GETOPT_STR(RTGetOpt(&GetState, &Val), VINF_GETOPT_NOT_OPTION, 2, "foo1");
    CHECK_GETOPT_STR(RTGetOpt(&GetState, &Val), VINF_GETOPT_NOT_OPTION, 1, "foo2");
    CHECK_GETOPT_STR(RTGetOpt(&GetState, &Val), VINF_GETOPT_NOT_OPTION, 1, "foo3");
    CHECK_GETOPT_STR(RTGetOpt(&GetState, &Val), VINF_GETOPT_NOT_OPTION, 1, "foo4");
    CHECK_GETOPT_STR(RTGetOpt(&GetState, &Val), VINF_GETOPT_NOT_OPTION, 1, "foo5");
    CHECK_GETOPT_STR(RTGetOpt(&GetState, &Val), VINF_GETOPT_NOT_OPTION, 1, "foo6");
    CHECK_GETOPT_STR(RTGetOpt(&GetState, &Val), VINF_GETOPT_NOT_OPTION, 1, "foo7");
    CHECK_GETOPT_STR(RTGetOpt(&GetState, &Val), VINF_GETOPT_NOT_OPTION, 1, "foo8");
    CHECK_GETOPT_STR(RTGetOpt(&GetState, &Val), VINF_GETOPT_NOT_OPTION, 1, "foo9");
    CHECK_GETOPT_STR(RTGetOpt(&GetState, &Val), VINF_GETOPT_NOT_OPTION, 1, "foo10");
    CHECK_GETOPT_STR(RTGetOpt(&GetState, &Val), VINF_GETOPT_NOT_OPTION, 1, "--optwithstring");
    CHECK_GETOPT_STR(RTGetOpt(&GetState, &Val), VINF_GETOPT_NOT_OPTION, 1, "foo11");
    CHECK_GETOPT_STR(RTGetOpt(&GetState, &Val), VINF_GETOPT_NOT_OPTION, 1, "foo12");

    /* the end */
    CHECK_GETOPT(RTGetOpt(&GetState, &Val), 0, 0);
    CHECK(Val.pDef == NULL);
    CHECK(argc3 == GetState.iNext);

    /*
     * Options first, part 2: No dash-dash.
     */
    const char *argv4[] =
    {
        "foo1",
        "-s",               "string1",
        "foo2",
        "--optwithstring",  "string2",
        "foo3",
        "-i",               "-42",
        "foo4",
        "-i:-42",
        "-i=-42",
        "foo5",
        "foo6",
        "foo7",
        "-i:-42",
        "-i=-42",
        "foo8",
        "--twovalues",       "firstvalue", "secondvalue",
        "foo9",
        "--twovalues:firstvalue",          "secondvalue",
        "foo10",
        "foo11",
        "foo12",

        /* done */
        NULL
    };
    int argc4 = (int)RT_ELEMENTS(argv4) - 1;

    CHECK(RT_SUCCESS(RTGetOptInit(&GetState, argc4, (char **)argv4, &s_aOpts2[0], RT_ELEMENTS(s_aOpts2), 0,
                                  RTGETOPTINIT_FLAGS_OPTS_FIRST)));

    /* -s */
    CHECK_GETOPT_STR(RTGetOpt(&GetState, &Val), 's', 2, "string1");
    CHECK(GetState.uIndex == UINT32_MAX);
    CHECK_GETOPT_STR(RTGetOpt(&GetState, &Val), 's', 2, "string2");
    CHECK(GetState.uIndex == UINT32_MAX);

    /* -i */
    CHECK_GETOPT(RTGetOpt(&GetState, &Val), 'i', 2);
    CHECK(Val.i32 == -42);
    CHECK_GETOPT(RTGetOpt(&GetState, &Val), 'i', 1);
    CHECK(Val.i32 == -42);
    CHECK_GETOPT(RTGetOpt(&GetState, &Val), 'i', 1);
    CHECK(Val.i32 == -42);
    CHECK_GETOPT(RTGetOpt(&GetState, &Val), 'i', 1);
    CHECK(Val.i32 == -42);
    CHECK_GETOPT(RTGetOpt(&GetState, &Val), 'i', 1);
    CHECK(Val.i32 == -42);

    /* --twovalues */
    CHECK_GETOPT_STR(RTGetOpt(&GetState, &Val), 405, 2, "firstvalue");
    CHECK(GetState.uIndex == UINT32_MAX);
    CHECK_GETOPT_STR(RTGetOptFetchValue(&GetState, &Val, RTGETOPT_REQ_STRING), VINF_SUCCESS, 1, "secondvalue");
    CHECK(GetState.uIndex == UINT32_MAX);

    CHECK_GETOPT_STR(RTGetOpt(&GetState, &Val), 405, 1, "firstvalue");
    CHECK(GetState.uIndex == UINT32_MAX);
    CHECK_GETOPT_STR(RTGetOptFetchValue(&GetState, &Val, RTGETOPT_REQ_STRING), VINF_SUCCESS, 1, "secondvalue");
    CHECK(GetState.uIndex == UINT32_MAX);

    /* -- */
    CHECK_GETOPT_STR(RTGetOpt(&GetState, &Val), VINF_GETOPT_NOT_OPTION, 1, "foo1");
    CHECK_GETOPT_STR(RTGetOpt(&GetState, &Val), VINF_GETOPT_NOT_OPTION, 1, "foo2");
    CHECK_GETOPT_STR(RTGetOpt(&GetState, &Val), VINF_GETOPT_NOT_OPTION, 1, "foo3");
    CHECK_GETOPT_STR(RTGetOpt(&GetState, &Val), VINF_GETOPT_NOT_OPTION, 1, "foo4");
    CHECK_GETOPT_STR(RTGetOpt(&GetState, &Val), VINF_GETOPT_NOT_OPTION, 1, "foo5");
    CHECK_GETOPT_STR(RTGetOpt(&GetState, &Val), VINF_GETOPT_NOT_OPTION, 1, "foo6");
    CHECK_GETOPT_STR(RTGetOpt(&GetState, &Val), VINF_GETOPT_NOT_OPTION, 1, "foo7");
    CHECK_GETOPT_STR(RTGetOpt(&GetState, &Val), VINF_GETOPT_NOT_OPTION, 1, "foo8");
    CHECK_GETOPT_STR(RTGetOpt(&GetState, &Val), VINF_GETOPT_NOT_OPTION, 1, "foo9");
    CHECK_GETOPT_STR(RTGetOpt(&GetState, &Val), VINF_GETOPT_NOT_OPTION, 1, "foo10");
    CHECK_GETOPT_STR(RTGetOpt(&GetState, &Val), VINF_GETOPT_NOT_OPTION, 1, "foo11");
    CHECK_GETOPT_STR(RTGetOpt(&GetState, &Val), VINF_GETOPT_NOT_OPTION, 1, "foo12");

    /* the end */
    CHECK_GETOPT(RTGetOpt(&GetState, &Val), 0, 0);
    CHECK(Val.pDef == NULL);
    CHECK(argc4 == GetState.iNext);



    /*
     * Summary.
     */
    return RTTestSummaryAndDestroy(hTest);
}