Cannot get SymGetTypeInfo to work (error 1, Incorrect function)

300 Views Asked by At

TLDR: Could someone explain how to correctly use the SymGetTypeInfo.

My goal is to get the type information for local variables (wherever Rip/Eip happens to be).

According to the documentation, I need to call the SymSetContext with the IMAGEHLP_STACK_FRAME structure filled with appropriate values. I got this part working as when I call SymEnumSymbols (setting the BaseOfDll parameter to 0 as the doc says to do when setting context), I do in fact only get the local variables.

HOWEVER:

When inside the enumerate symbols callback, I try to call SymGetTypeInfo, something like this:

static BOOL s_enum_symbols_callback(
    PSYMBOL_INFO pSymInfo,
    ULONG SymbolSize,
    PVOID UserContext) {
    // ...

    HANDLE hProcess = /*Defined elsewhere*/;
    wchar_t *buffer = (wchar_t *)allocate_symname_memory();

    SymGetTypeInfo(hProcess, pSymInfo->ModBase, pSymInfo->TypeIndex, TI_GET_SYMNAME, &buffer);
}

SymGetTypeInfo fails with error 1 ("Incorrect function").

Ok so now I am troubleshooting to figure out why this might happen. My first theory is that this may have something to do with setting the context, so I remove the SymSetContext and enumerate again. This time (I obviously get all the symbols), and I get the same problem except for a few types, notably: I am able to get the type of __scrt_current_native_startup_state (which is __scrt_native_startup_state).

Ok so maybe... the problem is that I am calling SymGetTypeInfo inside a Sym* callback function?? (doubt it though). Anyway, I put the theory to the test, and enumerate just the symbols which I have defined globally:

wchar_t global_variable = 12;
int global_variable0 = 12;
float global_variable1 = 12.0f;
int global_variable2 = 12;

Once again, not setting the context, I enumerate the symbols and note the TypeIndex value for each so that once outside of the enumerate symbols function, I call SymGetTypeInfo like this:

SymGetTypeInfo(hProcess, process_pdb_base, type_index, TI_GET_SYMNAME, &buffer);

(process_pdb_base is returned from SymModuleLoad64).

And I get the SAME problem.

Maybe I am completely misunderstanding what the TypeIndex really is? (although it is consistent for different variables with the same type), or maybe it's only for user-defined types??

So yeah I am lost and tired, please help internet.

1

There are 1 best solutions below

1
On

Alright guys, I think I figured it out and it's all thanks to this marvelous article written in 2004, by Oleg Starodumov!

And one of my theories wasn't completely wrong - I did indeed misunderstand what the Debug Help (well actually, the entire Win32 API!) library refers to, when using the word "type". As the article wonderfully puts it: "The type of an object defines the set of properties supported by the object, and the set of possible relationships between the object and other objects."

So really, the SymGetTypeInfo functions, is just used to query information about any symbol. Now there are indeed different categories of symbols, which are encapsulated by the SymTagEnum enumerator type which is defined in DbgHelp.h IF you have _NO_CVCONST_H defined with #define (NOTE: You use this if you don't have access to cvconst.h which was my case).

Because this article is so great and I don't want to spoil it for anyone, I will quickly resume what I needed to do to fix my issue, instead of trying to explain querying the C/C++ "type" information (not Win32 "type") of a variable.

The first thing I needed to do, was get the tag of the symbol which represented the C/C++ "type" of the local variable symbol (which is indeed stored in TypeIndex). Next, I needed the code to act accordingly for each different possible tag. In my case, because this was what was called a BaseType, I needed to query what BaseType the type symbol was. With this:

SymGetTypeInfo(hProcess, pSymInfo->ModBase, pSymInfo->TypeIndex, TI_GET_BASETYPE, &type);

Now, each base type has a corresponding enum constant associated with it. The enum looks like this:

enum BasicType
{
    btNoType = 0,
    btVoid = 1,
    btChar = 2,
    btWChar = 3,
    btInt = 6,
    btUInt = 7,
    btFloat = 8,
    btBCD = 9,
    btBool = 10,
    btLong = 13,
    btULong = 14,
    btCurrency = 25,
    btDate = 26,
    btVariant = 27,
    btComplex = 28,
    btBit = 29,
    btBSTR = 30,
    btHresult = 31
};

And lo and behold, the base type did in fact correspond with the types of my local variables!

NOTE I couldn't find this enum in my system (most likely because I didn't have the Debugger Interface Access SDK), so to get it, I simply copied the TypeInfoStructs.h file located in another link provided by Oleg Starodumov.