Skip to content
This repository has been archived by the owner on Oct 12, 2022. It is now read-only.

Commit

Permalink
Add cppdemangle in druntime
Browse files Browse the repository at this point in the history
Having a C++ symbol demangler is very useful, with this it will
be possible to implement the C++ symbol demangle in the stacktrace.

The demangling is done in Posix platform by function __cxa_demangle
presents in Itanium C++ ABI (https://itanium-cxx-abi.github.io/cxx-abi/abi.html#demangler).

Itanium C++ ABI is used practically in all modern C++ compilers on Posix,
however old compilers (like GCC < 3.0) used a different name mangling,
this is not a problem because DMD in fact only supports Itanium C++ ABI on Posix.

For Windows instead the implementation it is provided by the UnDecorateSymbolName
function present in the Debug Help Library.
(https://docs.microsoft.com/en-us/windows/win32/api/dbghelp/nf-dbghelp-undecoratesymbolname)

Signed-off-by: Ernesto Castellotti <mail@ernestocastellotti.it>
  • Loading branch information
Ernesto Castellotti committed Apr 1, 2020
1 parent 475c85f commit 0de39b0
Show file tree
Hide file tree
Showing 4 changed files with 273 additions and 0 deletions.
1 change: 1 addition & 0 deletions mak/COPY
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ COPY=\
$(IMPDIR)\core\internal\atomic.d \
$(IMPDIR)\core\internal\attributes.d \
$(IMPDIR)\core\internal\convert.d \
$(IMPDIR)\core\internal\cppdemangle.d \
$(IMPDIR)\core\internal\dassert.d \
$(IMPDIR)\core\internal\destruction.d \
$(IMPDIR)\core\internal\entrypoint.d \
Expand Down
1 change: 1 addition & 0 deletions mak/SRCS
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ SRCS=\
src\core\internal\atomic.d \
src\core\internal\attributes.d \
src\core\internal\convert.d \
src\core\internal\cppdemangle.d \
src\core\internal\dassert.d \
src\core\internal\destruction.d \
src\core\internal\entrypoint.d \
Expand Down
3 changes: 3 additions & 0 deletions mak/WINDOWS
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,9 @@ $(IMPDIR)\core\internal\attributes.d : src\core\internal\attributes.d
$(IMPDIR)\core\internal\convert.d : src\core\internal\convert.d
copy $** $@

$(IMPDIR)\core\internal\cppdemangle.d : src\core\internal\cppdemangle.d
copy $** $@

$(IMPDIR)\core\internal\dassert.d : src\core\internal\dassert.d
copy $** $@

Expand Down
268 changes: 268 additions & 0 deletions src/core/internal/cppdemangle.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,268 @@
/**
* This module provides functions to demangle C++ symbols
*
* For Posix platform the demangling is done by function `__cxa_demangle` presents in Itanium C++ ABI.
* For Windows platform the demangling is done by function `UnDecorateSymbolName` presents in the Debug Help Library
* If the function is not found or something fails, the original mangled C++ name will be returned.
*
* Copyright: Copyright © 2020, The D Language Foundation
* License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
* Authors: Ernesto Castellotti
* Source: $(DRUNTIMESRC core/internal/_cppdemangle.d)
*/
module core.internal.cppdemangle;

version (Windows)
{
version (ANSI) {} else version = Unicode;
}

/**
* Demangles C++ mangled names.
*
* If it is not a C++ mangled name or cppdemangle is not supported by your platform, the original mangled C++ name will be returned.
* The optional destination buffer and return will be contains the same string if the demangle is successful.
*
* Params:
* buf = The string to demangle.
* dst = The destination buffer, if the size of the destination buffer is <= the size of cppdemangle output the return string would be incomplete.
* withPrefix = If true, add the prefix (followed by a space) before the C++ demangled name, make sure the target buffer is at least large to contain the prefix.
* prefix = Specifies the prefix to be added to the beginning of the string containing the demangled name
*
* Returns:
* The demangled name or the original string if the name is not a mangled C++ name.
*/
char[] cppdemangle(const(char)[] buf, char[] dst, bool withPrefix = false, string prefix = "[C++]") @safe
{
CPPDemangle.inizialize();
return CPPDemangle.instance.cppdemangle(buf, dst, withPrefix, prefix);
}

package struct CPPDemangle
{
private __gshared CPPDemangle _instance;
private __gshared bool isInizialized;

version (Posix)
{
@nogc nothrow extern(C)
{
private extern(C) char* function(const char* mangled_name, char* dst_buffer, size_t* length, int* status) __cxa_demangle;
}

private __gshared void* _handle;
}
else version (Windows)
{
import core.sys.windows.dbghelp : UnDecorateSymbolNameFunc;

@nogc nothrow extern(System)
{
private UnDecorateSymbolNameFunc UnDecorateSymbolName;
}
}

version (Posix)
{
static ~this() {
import core.sys.posix.dlfcn : dlclose;

if (isInizialized)
{
dlclose(_handle);
_handle = null;
isInizialized = false;
}
}
}

char[] cppdemangle(const(char)[] buf, char[] dst, bool withPrefix, string prefix) @safe
{
auto dstStart = withPrefix ? prefix.length + 1 : 0; // Add prefix + space
assert(dst !is null && dst.length > dstStart, "The destination buffer is null or too small for perform demangle");

auto demangle = _cppdemangle(buf, dst[dstStart..$]);

if (demangle is null)
{
return copyResult(buf, dst);
}

if (withPrefix)
{
dst[0..dstStart - 1] = prefix;
dst[dstStart - 1] = ' ';
}

return dst[0..(demangle.length + dstStart)];
}

static CPPDemangleStatus inizialize() @trusted
{
if (isInizialized)
{
return CPPDemangleStatus.INIZIALIZED;
}

version (iOS)
{
// Not supported (dlopen doesn't work)
// Fix me
}
else version (Posix)
{
import core.sys.posix.dlfcn : dlsym, dlopen, dlclose, RTLD_LAZY;

auto handle = dlopen(null, RTLD_LAZY);
assert(handle !is null);
auto p = dlsym(handle, "__cxa_demangle");

if (p !is null)
{
_handle = handle;
_instance.__cxa_demangle = cast(typeof(CPPDemangle.__cxa_demangle)) p;
isInizialized = true;
return CPPDemangleStatus.INIZIALIZED;
}

dlclose(handle);

version (OSX)
enum names = ["libc++abi.dylib", "libstdc++.dylib"];
else version (Posix)
{
enum names = ["libstdc++.so", "libstdc++.so.6", "libstdc++.so.5",
"libc++abi.so", "libc++abi.so.1"];
}

foreach (name; names)
{
handle = dlopen(name.ptr, RTLD_LAZY);

if (handle !is null)
{
break;
}
}

if (handle is null)
{
return CPPDemangleStatus.LOAD_ERROR;
}

p = dlsym(handle, "__cxa_demangle");

if (p !is null)
{
_handle = handle;
_instance.__cxa_demangle = cast(typeof(CPPDemangle.__cxa_demangle)) p;
isInizialized = true;
return CPPDemangleStatus.INIZIALIZED;
}
}

version (Windows)
{
import core.sys.windows.dbghelp : DbgHelp;

auto dbgHelp = DbgHelp.get();

if (dbgHelp is null)
{
return CPPDemangleStatus.LOAD_ERROR;
}

auto func = dbgHelp.UnDecorateSymbolName;

if (dbgHelp.UnDecorateSymbolName !is null)
{
_instance.UnDecorateSymbolName = dbgHelp.UnDecorateSymbolName;
isInizialized = true;
return CPPDemangleStatus.INIZIALIZED;
}
}

return CPPDemangleStatus.SYMBOL_ERROR;
}

static CPPDemangle instance() @trusted
{
return _instance;
}

private char[] _cppdemangle(const(char)[] buf, char[] dst) @trusted
{
import core.memory : pureCalloc, pureFree;
import core.stdc.string : strlen;

if (!isInizialized)
{
return null;
}

version (Unicode)
{
import core.internal.utf : toUTF16z, toUTF8;
auto mangledName = toUTF16z(buf);
}
else
{
auto mangledName = cast(char*) pureCalloc(buf.length + 1, char.sizeof);
scope(exit) pureFree(mangledName);
mangledName[0..buf.length] = buf[];
}

version (Posix)
{
int result;
auto demangledNamePtr = _instance.__cxa_demangle(mangledName, null, null, &result);

if (result != 0)
{
return null;
}

auto demangledName = demangledNamePtr[0..strlen(demangledNamePtr)];
scope(exit) pureFree(demangledNamePtr);
}

version (Windows)
{
import core.sys.windows.winnt : TCHAR;

TCHAR[2048] outBuf;
auto result = _instance.UnDecorateSymbolName(mangledName, outBuf.ptr, outBuf.length, 0);

if (result <= 0)
{
return null;
}

version (Unicode)
{
auto demangledName = toUTF8(outBuf[0..result]);
}
else
{
auto demangleName = outBuf[0..result];
}
}

return copyResult(demangledName, dst);
}
}

package enum CPPDemangleStatus
{
INIZIALIZED = 1,
LOAD_ERROR = -1,
SYMBOL_ERROR = -2
}

package char[] copyResult(const(char)[] input, char[] dst) @safe @nogc nothrow
{
auto len = input.length <= dst.length ? input.length : dst.length;
dst[0..len] = input[0..len];
dst[len..$] = '\0';
return dst[0..len];
}

0 comments on commit 0de39b0

Please sign in to comment.