/*
* Copyright (c) 2020, Oracle and/or its affiliates.
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other materials provided
* with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to
* endorse or promote products derived from this software without specific prior written
* permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.oracle.truffle.llvm;
import com.oracle.truffle.llvm.runtime.except.LLVMLinkerException;
import java.util.ArrayList;
import java.util.Base64;
final class CXXDemangler {
private static final String NAMESPACE_PREFIX = "_ZN";
private static final int NAMESPACE_PREFIX_LEN = NAMESPACE_PREFIX.length();
private static final String SULONG_NAMESPACE_PREFIX = ParserDriver.SULONG_RENAME_MARKER;
private static final String SULONG_BASE64_NAMESPACE_SUFFIX = "base64";
private static final String SULONG_BASE64_NAMESPACE = SULONG_NAMESPACE_PREFIX + SULONG_BASE64_NAMESPACE_SUFFIX;
private int idx;
private final String name;
private CXXDemangler(String name) {
if (!name.startsWith(NAMESPACE_PREFIX)) {
throw new LLVMLinkerException("Not a mangled namespace: " + name);
}
this.idx = NAMESPACE_PREFIX_LEN;
this.name = name;
}
private int parseNumber() {
int startIdx = idx;
int libnameLength = 0;
while (idx < name.length()) {
char c = name.charAt(idx);
if (c >= '0' && c <= '9') {
int d = c - '0';
libnameLength = libnameLength * 10 + d;
idx++;
} else {
return libnameLength;
}
}
throw new LLVMLinkerException(String.format("Premature end of name string: %s (%d)", name, startIdx));
}
private ArrayList<String> decode() {
ArrayList<String> namespaces = new ArrayList<>();
while (idx < name.length()) {
int length = parseNumber();
if (length == 0) {
// end of name spaces -- add remaining string to result
namespaces.add(name.substring(idx));
return namespaces;
}
int newIdx = idx + length;
if (newIdx >= name.length()) {
throw new LLVMLinkerException(String.format("Premature end of name string: %s (%d)", name, idx));
}
namespaces.add(name.substring(idx, newIdx));
idx = newIdx;
}
throw new LLVMLinkerException(String.format("Unterminated name %s (%d)", name, idx));
}
Mangled C++ symbols. To encode the symbols, we use a special namespace (`__sulong_import` or
`__sulong_import_base64`) to indicate symbols the are subject to renaming. The library name
that includes the target function is encoded in yet another namespace. If the
`__sulong_import_base64` namespace is used, the library name is encoded as a base64 encoded
string. Example:
namespace __cxxabiv1 {
namespace __sulong_import_base64 { // special __sulong_import namespace
namespace bGliYysrYWJpLnNv { // libname in base64 (libc++abi.so)
// the function declaration that will be aliased
static __cxa_exception* cxa_exception_from_exception_unwind_exception(_Unwind_Exception* unwind_exception);
} // end libc++.so
} // end __sulong_import
...
// usage of the declared function
__cxa_exception *ex = __sulong_import_base64::bGliYysrYWJpLnNv::cxa_exception_from_exception_unwind_exception(unwindHeader);
} // end __cxxabiv1
/**
* Mangled C++ symbols. To encode the symbols, we use a special namespace (`__sulong_import` or
* `__sulong_import_base64`) to indicate symbols the are subject to renaming. The library name
* that includes the target function is encoded in yet another namespace. If the
* `__sulong_import_base64` namespace is used, the library name is encoded as a base64 encoded
* string. Example:
*
* <pre>
namespace __cxxabiv1 {
namespace __sulong_import_base64 { // special __sulong_import namespace
namespace bGliYysrYWJpLnNv { // libname in base64 (libc++abi.so)
// the function declaration that will be aliased
static __cxa_exception* cxa_exception_from_exception_unwind_exception(_Unwind_Exception* unwind_exception);
} // end libc++.so
} // end __sulong_import
...
// usage of the declared function
__cxa_exception *ex = __sulong_import_base64::bGliYysrYWJpLnNv::cxa_exception_from_exception_unwind_exception(unwindHeader);
} // end __cxxabiv1
* </pre>
*/
static boolean isRenamedNamespaceSymbol(String name) {
return name.startsWith("_ZN") && name.contains(CXXDemangler.SULONG_NAMESPACE_PREFIX);
}
Decodes a mangled C++ name into a list of namespaces. The last entry is the (mangled) symbol
name (global or function).
Throws: - LLVMLinkerException – if the given name cannot be decoded.
/**
* Decodes a mangled C++ name into a list of namespaces. The last entry is the (mangled) symbol
* name (global or function).
*
* @throws LLVMLinkerException if the given name cannot be decoded.
*/
static ArrayList<String> decodeNamespace(String name) {
return new CXXDemangler(name).decode();
}
Returns the library name which is encoded as a namespace and removes it as well as the marker
namespace by setting it to null.
/**
* Returns the library name which is encoded as a namespace and removes it as well as the marker
* namespace by setting it to null.
*/
static String getAndRemoveLibraryName(ArrayList<String> namespaces) {
// the last entry is the remaining symbol
int numNamespaces = namespaces.size() - 1;
for (int i = 0; i < numNamespaces; i++) {
String namespace = namespaces.get(i);
if (namespace.startsWith(SULONG_NAMESPACE_PREFIX)) {
int libIdx = i + 1;
if (libIdx >= numNamespaces) {
throw new LLVMLinkerException(String.format("No library name to decode: ", String.join("::", namespaces)));
}
String rawLibname = namespaces.get(libIdx);
// remove marker namespaces
namespaces.set(i, null);
namespaces.set(libIdx, null);
if (namespace.equals(SULONG_NAMESPACE_PREFIX)) {
return rawLibname;
}
if (namespace.equals(SULONG_BASE64_NAMESPACE)) {
return new String(decodeBase64(rawLibname));
}
}
}
return null;
}
static byte[] decodeBase64(CharSequence charSequence) {
byte[] result = new byte[charSequence.length()];
for (int i = 0; i < result.length; i++) {
char ch = charSequence.charAt(i);
assert ch >= 0 && ch <= Byte.MAX_VALUE;
result[i] = (byte) ch;
}
return Base64.getDecoder().decode(result);
}
static String encodeNamespace(ArrayList<String> namespaces) {
StringBuilder sb = new StringBuilder();
sb.append(NAMESPACE_PREFIX);
// the last entry is the remaining symbol
int numNamespaces = namespaces.size() - 1;
for (int i = 0; i < numNamespaces; i++) {
String namespace = namespaces.get(i);
if (namespace != null) {
sb.append(namespace.length());
sb.append(namespace);
}
}
sb.append(namespaces.get(numNamespaces));
return sb.toString();
}
}