Skip to main content
GameDev.net gamedev.net
🔒 Locked 🤖 Godot

Secret hidden compiler options

Started by Excors Jan 17, 2007 at 9:57 AM 2 replies 6.6k views
Original Post
Excors
Excors
This site has a seemingly comprehensive list of compiler options from VC++ 2002, many of which are undocumented by Microsoft. VC2005 had some reorganisation of compiler options, and some of the undocumented ones were removed (or actually deprecated, in the case of /dlp) but at least a couple of interesting ones were added. Since the only place they're found on Google is the one time I mentioned them on this site last year, I just thought I'd point it out again because it could be useful in some cases. Try creating an empty source file, and compile with
cl test.cpp /c /d1 reportAllClassLayout
(/d1 passes the option directly to the C++ compiler frontend (c1xx.dll) - cl.exe doesn't understand reportAllClassLayout.) That should give something like
class _PMD      size(12):
        +---
 0      | mdisp
 4      | pdisp
 8      | vdisp
        +---



class _TypeDescriptor   size(8):
        +---
 0      | pVFTable
 4      | spare
 8      | name
        +---



class _s__CatchableType size(28):
        +---
 0      | properties
 4      | pType
 8      | _PMD thisDisplacement
20      | sizeOrOffset
24      | copyFunction
        +---



class _s__CatchableTypeArray    size(4):
        +---
 0      | nCatchableTypes
 4      | arrayOfCatchableTypes
        +---



class _s__ThrowInfo     size(16):
        +---
 0      | attributes
 4      | pmfnUnwind
 8      | pForwardCompat
12      | pCatchableTypeArray
        +---

...lots more...
showing the contents and sizes of all the classes/structs defined by the program, plus those automatically defined by the compiler. As an aside, that list also includes lots of non-underscore-prefixed names like "default_valueAttribute" and "soap_namespaceAttribute". That wouldn't be very good if they were polluting the global namespace, so they're sensibly in a proper C++ namespace, though peculiarly the namespace is called . Since that's not a valid C++ identifier, you have to use __identifier("") to get at it. That gives "error C4483: syntax error: expected C++ keyword", but fortunately you can disable the error(!). (C4483 is, of course, completely undocumented). You can end up with something like
#include <iostream>

#pragma warning(disable:4483)
using namespace __identifier("<vc_attributes>");
int main() {
	std::cout << sizeof(default_valueAttribute) << "\n";
	std::cout << static_cast<default_valueAttribute*>(NULL)->value << "\n";
};
which successfully compiles (with cl /EHsc test.cpp). If you try to do much else with default_valueAttribute (e.g. default_valueAttribute var; or new default_valueAttribute) then there's an internal compiler error. So, that part is not very useful. (The compiler's frontend (c1xx.dll) just contains a long section of source code (in Managed C++ or C++/CLI - I don't know the difference), which is where and its contents are defined.) Anyway... There's also reportSingleClassLayoutSUBSTRING which does the same but limited to classes whose name contains the given substring. So
cl /EHsc test.cpp /d1 reportSingleClassLayoutbasic_string
(with a suitable test.cpp) gives just
class ?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@   size(28):
        +---
        | +--- (base class ?$_String_val@DV?$allocator@D@std@@)
        | | +--- (base class _String_base)
        | | | +--- (base class _Container_base)
        | | | +---
        | | +---
 0      | | ?$allocator@D _Alval
        | +---
        | <alignment member> (size=3)
 4      | _Bxty _Bx
20      | _Mysize
24      | _Myres
        +---
Something else that seems useful is the /cap, /fastcap, /callcap switches, which are mentioned in that VC2002 reference (so they're not new features, but they're still quite hidden). With /cap, the compiler inserts a "push OFFSET (current function); call __CAP_Profiling@4" instructions at the start of every basic block (I think). The normal documented /Gh option inserts "call __penter" and "call __pexit" at the extreme entry and exit of each function (outside the bits where it fiddles with ebp). /callcap is similar but does "push OFFSET (current function); call __CAP_Enter_Function@4" (and similar for Exit), which may be easier to use since you know where you got called from. /fastcap goes outside function calls instead of inside (so it can handle calls to external libraries): the compiler adds "push OFFSET (function being called); push OFFSET (current function); call __CAP_Start_Profiling@4; call (function being called); add esp, (whatever); push OFFSET (current function); call __CAP_End_Profiling@4". So you can do something like
extern "C" {
    void __stdcall _CAP_Profiling(void* func);
    void __stdcall _CAP_Start_Profiling(void* func, void* called);
    void __stdcall _CAP_End_Profiling(void* func);
    void __stdcall _CAP_Enter_Function(void* func);
    void __stdcall _CAP_Exit_Function(void* func);
}
and implement those functions (being careful not to compile those implementations with the /*cap flags enabled, else you'll get confused by infinite loops...) You can combine all the options, and from code like
int t(int a, int b) {
    return a*b;
}

int main() {
    char buf[16];
    if (1)
        if (1)
            strcpy(buf, "Hello world\n");
    printf(buf);
    return t(42, 56);
}
you can easily get output like
CAP_Enter_Function: entered function 00401030
CAP_Profiling: entered block from function 00401030
CAP_Profiling: entered block from function 00401030
CAP_Profiling: entered block from function 00401030
CAP_Start_Profiling: calling function 00401270 from function 00401030
CAP_End_Profiling: left function, returning to function 00401030
CAP_Profiling: entered block from function 00401030
CAP_Start_Profiling: calling function 004011A8 from function 00401030
Hello world
CAP_End_Profiling: left function, returning to function 00401030
CAP_Start_Profiling: calling function 00401000 from function 00401030
CAP_Enter_Function: entered function 00401000
CAP_Profiling: entered block from function 00401000
CAP_Exit_Function: leaving function 00401000
CAP_End_Profiling: left function, returning to function 00401030
CAP_Exit_Function: leaving function 00401030
and use something like _ReturnAddress() to get more precise details about where you were called from (though I guess you should be careful about preserving any registers you use, the same as with __penter, by writing it in assembly with __declspec(naked)). So it seems that might be useful if you're trying to write a simple profiler, and /Gh doesn't give all the information you need. /fastcap and /callcap are almost documented, in that the Windows CE documentation describes them (though they work fine in the normal Win32 compiler too), but they don't seem to be well known, and /cap doesn't appear to mentioned anywhere at all (or at least Google can't find _CAP_Profiling). I'd assume there are other hidden features that could be useful in a number of cases, but none with obviously exciting names, so I haven't looked at any others in any detail.
Zahlman
Zahlman
How on earth do you find this stuff? O__O
Excors
Excors
Just by looking for interesting strings in the code, then playing around a bit to see what they do [smile]

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.