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 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.

This implementation obviously only supports Posix, on Windows it is not possible to use it.
You could probably do the same thing on Windows using
https://docs.microsoft.com/en-us/windows/win32/api/dbghelp/nf-dbghelp-undecoratesymbolname

I think that for MinGw it is possible to use the same function, and I have already
sketched a support for Windows with MinGw however for now it is not enabled
because I don't know how this platform works.

Signed-off-by: Ernesto Castellotti <[email protected]>
  • Loading branch information
Ernesto Castellotti committed Mar 26, 2020
1 parent 475c85f commit 36d80f7
Show file tree
Hide file tree
Showing 4 changed files with 172 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
167 changes: 167 additions & 0 deletions src/core/internal/cppdemangle.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
/**
* This module provides routines to demangle C++ symbols
*
* For Posix platform the demangling is done by function `__cxa_demangle` presents in Itanium C++ ABI.
* If the function is not found or something fails, the original mangled C++ name will be returned.
* This module currently only supports POSIX.
*
* Copyright: Copyright © 2019, 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)
*/

/**
* 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.
*/
version (iOS)
{
// Not supported (dlopen doesn't work)
// Fix me
}
else version (Posix)
{
version = SupportCppDemangle;
}

char[] cppdemangle(const(char)[] buf, char[] dst, bool withPrefix = false, string prefix = "[C++]") @safe @nogc nothrow
{
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");

version (SupportCppDemangle)
{
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)];
}
else
{
return copyResult(buf, dst);
}
}

private 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];
}

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

int status;
auto mangledName = cast(char*) pureCalloc(buf.length + 1, char.sizeof);
scope(exit) pureFree(mangledName);
mangledName[0..buf.length] = buf[];

auto demangle = CXADemangleAPI.instance().__cxa_demangle;
if (demangle is null) return null;

auto result = demangle(mangledName, null, null, &status);
scope(exit) pureFree(result);

if (status != 0) return null;
return copyResult(result[0..strlen(result)], dst);
}

private static struct CXADemangleAPI
{
static struct API
{
@nogc nothrow extern(C):
private __gshared extern(C) char* function(const char* mangled_name, char* dst_buffer, size_t* length, int* status) __cxa_demangle;
}

private __gshared API _api;
private __gshared void* _handle;

static ref API instance() @nogc nothrow
{
if (_api.__cxa_demangle is null) _handle = loadAPI();
return _api;
}

static void* loadAPI() @nogc nothrow
{
static extern(C) void cleanup()
{
if (_handle is null) return;
import core.sys.posix.dlfcn : dlclose;
dlclose(_handle);
_handle = null;
_api.__cxa_demangle = null;
}

static void* setSym(void* handle, void* ptrSym)
{
import core.stdc.stdlib : atexit;
atexit(&cleanup);
_api.__cxa_demangle = cast(typeof(CXADemangleAPI.API.__cxa_demangle)) ptrSym;
return handle;
}

import core.sys.posix.dlfcn : dlsym, dlopen, dlclose, RTLD_LAZY;

auto handle = dlopen(null, RTLD_LAZY);
if (handle is null) return null;

auto p = dlsym(handle, "__cxa_demangle");
if (p !is null) return setSym(handle, p);

dlclose(handle);

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

foreach (name; names)
{
handle = dlopen(name.ptr, RTLD_LAZY);
if (handle !is null) break;
}

if (handle is null) return null;
p = dlsym(handle, "__cxa_demangle");

if (p is null)
{
dlclose(handle);
return null;
}

return setSym(handle, p);
}
}
}

0 comments on commit 36d80f7

Please sign in to comment.