Talk:Visual C++ name mangling

From Wikiversity
Jump to: navigation, search

Data Type[edit]

It appears that $$B may be a type modifier, but I'm unsure of the specifics, or whether it's actually used by the compiler.

Test Code UNDNAME 0x2000 result Notes
$$BA$CY@@ $CY
$$BB$CY@@ $CY
$$BL$CY@@ $CY
$$BLHello@@ Hello
$$BPAH int * Identical to PAH
$$BP$CGH int volatile % Identical to P$CGH
$$BPCY3HI@I@3H@H int (volatile *)[120][8][4][7] Identical to PCY3HI@I@3H@H
$$BY1HI@I@H int [120][8] Interestingly, Y1HI@I@H by itself isn't a valid type symbol.

When run through UNDNAME (without flags), ?a@@3$$BY1HI@I@HB expands to int (const a)[120][8].

$$B_OA$CY@@ CY[] CY appears to be treated as the type.
$$B_OB$CY@@ CY const[]

I'm not sure, but I think $$B might be a modifier that indicates that the following is to be interpreted as a raw type regardless of anything else, or it might tell the undecorator not to try to expand numbers as abbreviated names. Or it might do something else, I honestly don't know.


[Tested with Visual Studio 2010 UNDNAME. I would test with Studio 2015, but I'm unable to do so directly at the moment, and can't use the online compiler since I don't know how to use __unDName() or __unDNameHelper() in Studio 2015.


It also appears that the code $$F is associated with Managed Extensions for C++ (and possibly C++/CLI), although I'm unsure of its exact meaning.

// cli_ptr_clrold.cpp
// Managed Extensions for C++
// Compile with "/c /clr:oldSyntax" on VS 2013 or earlier.

__gc class GC {
    int i;
  public:
    int getI() { return i; }
};

/* GC member functions:
 *
 * Constructor: ??0GC@@$$FQ$AAM@XZ (public: __clrcall GC::GC(void))
 * getI():      ?getI@GC@@$$FQ$AAMHXZ (public: int __clrcall GC::getI(void))
 */

GC* func() { return new GC; }
/* Two symbols generated for func():
 *
 *   ?func@@$$FYMP$AAVGC@@XZ (class GC ^ __clrcall func(void))
 *   ?func@@YMP$AAVGC@@XZ (class GC ^ __clrcall func(void))
 *
 * One additional MEP symbol generated:
 *   __mep@?func@@$$FYMP$AAVGC@@XZ ([MEP] class GC ^ __clrcall func(void))
 * It binds to the $$F version of func()'s symbol.
 */

GC& cnuf() { return *(new GC); }
/* Two symbols generated for cnuf():
 *
 *   ?cnuf@@$$FYMA$AAVGC@@XZ (class GC % __clrcall cnuf(void))
 *   ?cnuf@@YMA$AAVGC@@XZ (class GC % __clrcall cnuf(void))
 *
 * One additional MEP symbol generated:
 *   __mep@?cnuf@@$$FYMA$AAVGC@@XZ ([MEP] class GC % __clrcall cnuf(void))
 * It, too, binds to the $$F version of the actual function.
 */

Similarly, C++/CLI uses $$F, generating one signature with it and one without.

// cli_ptr_clr.cpp
// C++/CLI
// Compile with "/c /clr" on VS 2005 or later.

ref class REF {
        int i;
    public:
        int getI() { return i; }
};

/* REF member functions:
 *
 * Constructor: ??0REF@@$$FQ$AAM@XZ (public: __clrcall REF::REF(void))
 * getI():      ?getI@REF@@$$FQ$AAMHXZ (public: int __clrcall REF::getI(void))
 */

REF^ funcMP() { return gcnew REF; }
/* Two symbols generated for funcMP():
 *
 *   ?funcMP@@$$FYMP$AAVREF@@XZ (class REF ^ __clrcall funcMP(void))
 *   ?funcMP@@YMP$AAVREF@@XZ (class REF ^ __clrcall funcMP(void))
 *
 * One additional MEP symbol generated:
 *   __mep@?funcMP@@$$FYMP$AAVREF@@XZ ([MEP] class REF ^ __clrcall funcMP(void))
 * It binds to the $$F version of funcMP()'s symbol.
 */

REF% funcMR() { return *(gcnew REF); }
/* Two symbols generated for funcMR():
 *
 *   ?funcMR@@$$FYMA$AAVREF@@XZ (class REF % __clrcall funcMR(void))
 *   ?funcMR@@YMA$AAVREF@@XZ (class REF % __clrcall funcMR(void))
 *
 * One additional MEP symbol generated:
 *   __mep@?funcMR@@$$FYMA$AAVREF@@XZ ([MEP] class REF % __clrcall funcMR(void))
 * It binds to the $$F version of func()'s symbol.
 */

I'm unsure of what exactly this means. I suspect it's part of the "IJW" mechanism, and allows native C++ code to link to the C++/CLI functions, but that's just a guess. 24.222.178.254 (discuss) 16:31, 1 May 2016 (UTC)


Okay, I've found what $$F is for. It indicates that a function symbol is for the managed entry point; the one without $$F is the native entry point (for mixed native C++ and C++/CLI code, and appears to simply pass the call to the __mep version.

To test this, I made a simplified version of the above C++/CLI, that only exports funcMP():

// cli_ptr_funcmp.cpp

ref class REF {
    int i;
  public:
    int getI();
    REF();
};

REF^ funcMP() { return gcnew REF; }

I then compiled it with cl /c /clr cli_ptr_funcmp.cpp /FAs, and obtained the following ASM file:

; Listing generated by Microsoft (R) Optimizing Compiler Version 16.00.40219.01 

; Generated by VC++ for Common Language Runtime
.file "E:\Programs\mangler\cli_ptr_funcmp.cpp"
	.bss
.local	$T6307,0
; Function compile flags: /Odtp
; File e:\programs\mangler\cli_ptr_funcmp.cpp
	.text
.global	?funcMP@@$$FYMP$AAVREF@@XZ			; funcMP
?funcMP@@$$FYMP$AAVREF@@XZ:				; funcMP
;	.proc.def	D:P()

; Function Header:
; max stack depth = 1
; function size = 8 bytes
; local varsig tk = 0x11000001 
; Exception Information:
; 0 handlers, each consisting of filtered handlers

;	.local.i4 0,"$T6306" SIG: class (token:0x34DB71)

;	.proc.beg

; 10   : REF^ funcMP() { return gcnew REF; }

	newobj		??0REF@@$$FQ$AAM@XZ
	stloc.0				; $T6306
	ldloc.0				; $T6306
	ret		
 .end ?funcMP@@$$FYMP$AAVREF@@XZ			; funcMP
;	.proc.end.mptr
_TEXT	ENDS
PUBLIC	__mep@?funcMP@@$$FYMP$AAVREF@@XZ
PUBLIC	?funcMP@@YMP$AAVREF@@XZ				; funcMP
;	COMDAT __mep@?funcMP@@$$FYMP$AAVREF@@XZ
data	SEGMENT
__mep@?funcMP@@$$FYMP$AAVREF@@XZ TOKEN 06000003
; Function compile flags: /Odtp
data	ENDS
;	COMDAT ?funcMP@@YMP$AAVREF@@XZ
_TEXT	SEGMENT
?funcMP@@YMP$AAVREF@@XZ PROC				; funcMP, COMDAT
	jmp	DWORD PTR __mep@?funcMP@@$$FYMP$AAVREF@@XZ
?funcMP@@YMP$AAVREF@@XZ ENDP				; funcMP
_TEXT	ENDS
END

From this, compiling cli_ptr_clr.cpp above with /clr:pure (which only generated $$F symbols, and no __meps), compiling a native function (bool natBool() { return false; }) with /clr (which generated the symbols ?natBool@@$$FYA_NXZ, ?natBool@@YA_NXZ, and __mep@?natBool@@$$FYA_NXZ), and the MSDN "Double Thunking" article, it appears that $$F indicates that a function is managed. It also appears that when managed and native versions of a function are generated, the native one will be a stub that uses the __mep symbol to call the managed one.

That's one symbol down, one to go! 24.222.178.254 (discuss) 18:47, 1 May 2016 (UTC)


Did more testing with UNDNAME, and it appears that the "managed" prefix comes before the "based" prefix.

"?a@@$$F_Y2X@@AXXZ" == "void __cdecl __based(X) a(void)"
"?a@@$$F_Y0AXXZ" == "void __cdecl __based(void) a(void)"
"?a@@_$$FY0AXXZ" == " ?? const volatile ?? ::XZ::& ?? a( ?? ) throw( ?? )"

Updated the main page.

24.222.178.254 (discuss) 19:54, 1 May 2016 (UTC)