1/*
2 * Copyright (c) 2023 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16#include "dfx_symbols.h"
17
18#include <algorithm>
19#include <cstdlib>
20#include <cxxabi.h>
21#ifdef RUSTC_DEMANGLE
22#include <dlfcn.h>
23#endif
24
25#include "dfx_define.h"
26#include "dfx_log.h"
27#include "dfx_trace_dlsym.h"
28#include "string_util.h"
29
30namespace OHOS {
31namespace HiviewDFX {
32using RustDemangleFn = char*(*)(const char *);
33namespace {
34#undef LOG_DOMAIN
35#undef LOG_TAG
36#define LOG_DOMAIN 0xD002D11
37#define LOG_TAG "DfxSymbols"
38
39const std::string LINKER_PREFIX = "__dl_";
40const std::string LINKER_PREFIX_NAME = "[linker]";
41
42#ifdef RUSTC_DEMANGLE
43static std::mutex g_mutex;
44static bool g_hasTryLoadRustDemangleLib = false;
45static RustDemangleFn g_rustDemangleFn = nullptr;
46#endif
47}
48
49#ifdef RUSTC_DEMANGLE
50bool DfxSymbols::FindRustDemangleFunction()
51{
52    if (g_hasTryLoadRustDemangleLib) {
53        return (g_rustDemangleFn != nullptr);
54    }
55
56    g_hasTryLoadRustDemangleLib = true;
57    void* rustDemangleLibHandle = dlopen("librustc_demangle.z.so", RTLD_LAZY | RTLD_NODELETE);
58    if (rustDemangleLibHandle == nullptr) {
59        DFXLOGW("Failed to dlopen librustc_demangle, %{public}s", dlerror());
60        return false;
61    }
62
63    g_rustDemangleFn = (RustDemangleFn)dlsym(rustDemangleLibHandle, "rustc_demangle");
64    if (g_rustDemangleFn == nullptr) {
65        DFXLOGW("Failed to dlsym rustc_demangle, %{public}s", dlerror());
66        dlclose(rustDemangleLibHandle);
67        return false;
68    }
69    return true;
70}
71#endif
72
73bool DfxSymbols::ParseSymbols(std::vector<DfxSymbol>& symbols, std::shared_ptr<DfxElf> elf, const std::string& filePath)
74{
75    if (elf == nullptr) {
76        return false;
77    }
78    auto elfSymbols = elf->GetFuncSymbols();
79    std::string symbolsPath = filePath;
80    if (elf->GetBaseOffset() != 0) {
81        symbolsPath += ("!" + elf->GetElfName());
82    }
83    for (auto elfSymbol : elfSymbols) {
84        symbols.emplace_back(elfSymbol.value, elfSymbol.size,
85            elfSymbol.nameStr, Demangle(elfSymbol.nameStr), symbolsPath);
86    }
87    return true;
88}
89
90bool DfxSymbols::AddSymbolsByPlt(std::vector<DfxSymbol>& symbols, std::shared_ptr<DfxElf> elf,
91                                 const std::string& filePath)
92{
93    if (elf == nullptr) {
94        return false;
95    }
96    ShdrInfo shdr;
97    elf->GetSectionInfo(shdr, PLT);
98    symbols.emplace_back(shdr.addr, shdr.size, PLT, filePath);
99    return true;
100}
101
102bool DfxSymbols::GetFuncNameAndOffsetByPc(uint64_t relPc, std::shared_ptr<DfxElf> elf,
103    std::string& funcName, uint64_t& funcOffset)
104{
105#if defined(__arm__)
106    relPc = relPc | 1;
107#endif
108    ElfSymbol elfSymbol;
109    if ((elf != nullptr) && elf->GetFuncInfo(relPc, elfSymbol)) {
110        DFXLOGU("nameStr: %{public}s", elfSymbol.nameStr.c_str());
111        funcName = Demangle(elfSymbol.nameStr);
112        funcOffset = relPc - elfSymbol.value;
113#if defined(__arm__)
114        funcOffset &= ~1;
115#endif
116        DFXLOGU("Symbol relPc: %{public}" PRIx64 ", funcName: %{public}s, funcOffset: %{public}" PRIx64 "",
117            relPc, funcName.c_str(), funcOffset);
118        return true;
119    }
120    return false;
121}
122
123std::string DfxSymbols::Demangle(const std::string& buf)
124{
125    DFX_TRACE_SCOPED_DLSYM("Demangle");
126    if ((buf.length() < 2) || (buf[0] != '_')) { // 2 : min buf length
127        return buf;
128    }
129
130    std::string funcName;
131    const char *bufStr = buf.c_str();
132    if (StartsWith(buf, LINKER_PREFIX)) {
133        bufStr += LINKER_PREFIX.size();
134        funcName += LINKER_PREFIX_NAME;
135    }
136
137    int status = 0;
138    char* demangledStr = nullptr;
139    if (buf[1] == 'Z') {
140        demangledStr = abi::__cxa_demangle(bufStr, nullptr, nullptr, &status);
141    }
142#ifdef RUSTC_DEMANGLE
143    if (buf[1] == 'R') {
144        std::lock_guard<std::mutex> lck(g_mutex);
145        if (FindRustDemangleFunction()) {
146            demangledStr = g_rustDemangleFn(bufStr);
147        }
148    }
149#endif
150    std::string demangleName;
151    if (demangledStr != nullptr) {
152        demangleName = std::string(demangledStr);
153        std::free(demangledStr);
154    } else {
155        demangleName = std::string(bufStr);
156    }
157    funcName += demangleName;
158    return funcName;
159}
160} // namespace HiviewDFX
161} // namespace OHOS
162