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

Commit 115ff06

Browse files
author
Ernesto Castellotti
committed
Add cppdemangle in druntime
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]>
1 parent 475c85f commit 115ff06

File tree

4 files changed

+167
-0
lines changed

4 files changed

+167
-0
lines changed

mak/COPY

+1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ COPY=\
2525
$(IMPDIR)\core\internal\atomic.d \
2626
$(IMPDIR)\core\internal\attributes.d \
2727
$(IMPDIR)\core\internal\convert.d \
28+
$(IMPDIR)\core\internal\cppdemangle.d \
2829
$(IMPDIR)\core\internal\dassert.d \
2930
$(IMPDIR)\core\internal\destruction.d \
3031
$(IMPDIR)\core\internal\entrypoint.d \

mak/SRCS

+1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ SRCS=\
2525
src\core\internal\atomic.d \
2626
src\core\internal\attributes.d \
2727
src\core\internal\convert.d \
28+
src\core\internal\cppdemangle.d \
2829
src\core\internal\dassert.d \
2930
src\core\internal\destruction.d \
3031
src\core\internal\entrypoint.d \

mak/WINDOWS

+3
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,9 @@ $(IMPDIR)\core\internal\attributes.d : src\core\internal\attributes.d
132132
$(IMPDIR)\core\internal\convert.d : src\core\internal\convert.d
133133
copy $** $@
134134

135+
$(IMPDIR)\core\internal\cppdemangle.d : src\core\internal\cppdemangle.d
136+
copy $** $@
137+
135138
$(IMPDIR)\core\internal\dassert.d : src\core\internal\dassert.d
136139
copy $** $@
137140

src/core/internal/cppdemangle.d

+162
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
/**
2+
* This module provides routines to demangle C++ symbols
3+
*
4+
* For Posix platform the demangling is done by function `__cxa_demangle` presents in Itanium C++ ABI.
5+
* If the function is not found or something fails, the original mangled C++ name will be returned.
6+
* This module currently only supports POSIX.
7+
*
8+
* Copyright: Copyright © 2019, The D Language Foundation
9+
* License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
10+
* Authors: Ernesto Castellotti
11+
* Source: $(DRUNTIMESRC core/internal/_cppdemangle.d)
12+
*/
13+
module core.internal.cppdemangle;
14+
15+
/**
16+
* Demangles C++ mangled names.
17+
*
18+
* If it is not a C++ mangled name or cppdemangle is not supported by your platform, the original mangled C++ name will be returned.
19+
* The optional destination buffer and return will be contains the same string if the demangle is successful.
20+
*
21+
* Params:
22+
* buf = The string to demangle.
23+
* dst = The destination buffer, if the size of the destination buffer is <= the size of cppdemangle output the return string would be incomplete.
24+
* withPrefix = If true, add the prefix "[C++] " before the C++ demangled name, make sure the target buffer is at least large to contain the prefix.
25+
*
26+
* Returns:
27+
* The demangled name or the original string if the name is not a mangled C++ name.
28+
*/
29+
version (iOS)
30+
{
31+
// Not supported (dlopen doesn't work)
32+
// Fix me
33+
}
34+
else version (Posix)
35+
{
36+
version = SupportCppDemangle;
37+
}
38+
39+
char[] cppdemangle(const(char)[] buf, char[] dst, bool withPrefix = false) @safe @nogc nothrow
40+
{
41+
enum prefix = "[C++] ";
42+
auto dstStart = withPrefix ? prefix.length : 0;
43+
assert(dst !is null && dst.length > dstStart, "The destination buffer is null or too small for perform demangle");
44+
45+
version (SupportCppDemangle)
46+
{
47+
auto demangle = _cppdemangle(buf, dst[dstStart..$]);
48+
if (demangle is null) return copyResult(buf, dst);
49+
if (withPrefix) dst[0..dstStart] = prefix;
50+
return dst[0..(demangle.length + dstStart)];
51+
}
52+
else
53+
{
54+
return copyResult(buf, dst);
55+
}
56+
}
57+
58+
private char[] copyResult(const(char)[] input, char[] dst) @safe @nogc nothrow
59+
{
60+
auto len = input.length <= dst.length ? input.length : dst.length;
61+
dst[0..len] = input[0..len];
62+
dst[len..$] = '\0';
63+
return dst[0..len];
64+
}
65+
66+
version (Posix)
67+
{
68+
private char[] _cppdemangle(const(char)[] buf, char[] dst) @trusted @nogc nothrow
69+
{
70+
import core.memory : pureCalloc, pureFree;
71+
import core.stdc.string : strlen;
72+
73+
int status;
74+
auto mangledName = cast(char*) pureCalloc(buf.length + 1, char.sizeof);
75+
scope(exit) pureFree(mangledName);
76+
mangledName[0..buf.length] = buf[];
77+
78+
auto demangle = CXADemangleAPI.instance().__cxa_demangle;
79+
if (demangle is null) return null;
80+
81+
auto result = demangle(mangledName, null, null, &status);
82+
scope(exit) pureFree(result);
83+
84+
if (status != 0) return null;
85+
return copyResult(result[0..strlen(result)], dst);
86+
}
87+
88+
private static struct CXADemangleAPI
89+
{
90+
static struct API
91+
{
92+
@nogc nothrow extern(C):
93+
private __gshared extern(C) char* function(const char* mangled_name, char* dst_buffer, size_t* length, int* status) __cxa_demangle;
94+
}
95+
96+
private __gshared API _api;
97+
private __gshared void* _handle;
98+
99+
static ref API instance() @nogc nothrow
100+
{
101+
if (_api.__cxa_demangle is null) _handle = loadAPI();
102+
return _api;
103+
}
104+
105+
static void* loadAPI() @nogc nothrow
106+
{
107+
static extern(C) void cleanup()
108+
{
109+
if (_handle is null) return;
110+
import core.sys.posix.dlfcn : dlclose;
111+
dlclose(_handle);
112+
_handle = null;
113+
_api.__cxa_demangle = null;
114+
}
115+
116+
static void* setSym(void* handle, void* ptrSym)
117+
{
118+
import core.stdc.stdlib : atexit;
119+
atexit(&cleanup);
120+
_api.__cxa_demangle = cast(typeof(CXADemangleAPI.API.__cxa_demangle)) ptrSym;
121+
return handle;
122+
}
123+
124+
import core.sys.posix.dlfcn : dlsym, dlopen, dlclose, RTLD_LAZY;
125+
126+
auto handle = dlopen(null, RTLD_LAZY);
127+
if (handle is null) return null;
128+
129+
auto p = dlsym(handle, "__cxa_demangle");
130+
if (p !is null) return setSym(handle, p);
131+
132+
dlclose(handle);
133+
134+
version (OSX)
135+
enum names = ["libc++abi.dylib", "libstdc++.dylib"];
136+
else version (Posix)
137+
{
138+
enum names = ["libstdc++.so", "libc++abi.so",
139+
"libc++abi.so.1"];
140+
}
141+
else version (MinGW)
142+
enum names = ["libstdc++.dll", "libstdc++-6.dll"];
143+
144+
foreach (name; names)
145+
{
146+
handle = dlopen(name.ptr, RTLD_LAZY);
147+
if (handle !is null) break;
148+
}
149+
150+
if (handle is null) return null;
151+
p = dlsym(handle, "__cxa_demangle");
152+
153+
if (p is null)
154+
{
155+
dlclose(handle);
156+
return null;
157+
}
158+
159+
return setSym(handle, p);
160+
}
161+
}
162+
}

0 commit comments

Comments
 (0)