• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2021-2024 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 
17 #include "file_ext.h"
18 #include "panda_file_external.h"
19 #include "libpandafile/file-inl.h"
20 #include "libpandafile/class_data_accessor-inl.h"
21 #include "libpandafile/method_data_accessor-inl.h"
22 #include "libpandafile/code_data_accessor-inl.h"
23 #include "libpandafile/debug_helpers.h"
24 #include "libpandabase/utils/logger.h"
25 #include "libpandabase/os/native_stack.h"
26 #include <map>
27 
28 namespace ark::panda_file::ext {
29 
30 struct MethodSymEntry {
31     ark::panda_file::File::EntityId id;
32     uint32_t length {0};
33     std::string name;
34 };
35 
36 }  // namespace ark::panda_file::ext
37 
38 #ifdef __cplusplus
39 extern "C" {
40 #endif
41 
42 struct PandaFileExt {
43 public:
PandaFileExtPandaFileExt44     explicit PandaFileExt(std::unique_ptr<const ark::panda_file::File> &&pandaFile) : pandaFile_(std::move(pandaFile))
45     {
46     }
47 
GetExtFileLineNumberPandaFileExt48     size_t GetExtFileLineNumber(ark::panda_file::MethodDataAccessor mda, uint32_t bcOffset)
49     {
50         return ark::panda_file::debug_helpers::GetLineNumber(mda, bcOffset, pandaFile_.get());
51     }
52 
QueryMethodSymByOffsetPandaFileExt53     ark::panda_file::ext::MethodSymEntry *QueryMethodSymByOffset(uint64_t offset)
54     {
55         auto it = methodSymbols_.upper_bound(offset);
56         if (it != methodSymbols_.end() && offset >= it->second.id.GetOffset()) {
57             return &it->second;
58         }
59 
60         // Enmuate all methods and put them to local cache.
61         ark::panda_file::ext::MethodSymEntry *found = nullptr;
62 
63         auto callBack = [this, offset, &found](ark::panda_file::MethodDataAccessor &mda) -> void {
64             if (mda.GetCodeId().has_value()) {
65                 ark::panda_file::CodeDataAccessor ca {*pandaFile_, mda.GetCodeId().value()};
66                 ark::panda_file::ext::MethodSymEntry entry;
67                 entry.id = mda.GetCodeId().value();
68                 entry.length = ca.GetCodeSize();
69                 entry.name = std::string(ark::utf::Mutf8AsCString(pandaFile_->GetStringData(mda.GetNameId()).data));
70 
71                 auto ret = methodSymbols_.emplace(offset, entry);
72                 if (mda.GetCodeId().value().GetOffset() <= offset &&
73                     offset < mda.GetCodeId().value().GetOffset() + ca.GetCodeSize()) {
74                     found = &ret.first->second;
75                 }
76             }
77         };
78 
79         for (uint32_t id : pandaFile_->GetClasses()) {
80             if (pandaFile_->IsExternal(ark::panda_file::File::EntityId(id))) {
81                 continue;
82             }
83 
84             ark::panda_file::ClassDataAccessor cda {*pandaFile_, ark::panda_file::File::EntityId(id)};
85             cda.EnumerateMethods(callBack);
86         }
87         return found;
88     }
89 
EnumerateAllMethodsPandaFileExt90     ark::panda_file::ext::MethodSymEntry *EnumerateAllMethods(uint32_t id, uint64_t offset,
91                                                               ark::panda_file::ext::MethodSymEntry *found)
92     {
93         ark::panda_file::ClassDataAccessor cda {*pandaFile_, ark::panda_file::File::EntityId(id)};
94         cda.EnumerateMethods([this, &cda, &offset, &found](ark::panda_file::MethodDataAccessor &mda) -> void {
95             if (mda.GetCodeId().has_value()) {
96                 ark::panda_file::CodeDataAccessor ca {*pandaFile_, mda.GetCodeId().value()};
97                 ark::panda_file::ext::MethodSymEntry entry;
98                 entry.id = mda.GetCodeId().value();
99                 entry.length = ca.GetCodeSize();
100                 entry.name = std::string(ark::utf::Mutf8AsCString(pandaFile_->GetStringData(mda.GetNameId()).data));
101 
102                 auto ret = methodSymbols_.emplace(offset, entry);
103                 if (mda.GetCodeId().value().GetOffset() <= offset &&
104                     offset < mda.GetCodeId().value().GetOffset() + ca.GetCodeSize()) {
105                     size_t lineNumber = GetExtFileLineNumber(mda, offset - mda.GetCodeId().value().GetOffset());
106                     found = &ret.first->second;
107                     ark::panda_file::File::EntityId idNew(lineNumber);
108                     auto nameId = cda.GetDescriptor();
109                     found->id = idNew;
110                     found->name = ark::os::native_stack::ChangeJaveStackFormat(reinterpret_cast<const char *>(nameId)) +
111                                   "." + found->name;
112                 }
113             }
114         });
115 
116         return found;
117     }
118 
QueryMethodSymAndLineByOffsetPandaFileExt119     ark::panda_file::ext::MethodSymEntry *QueryMethodSymAndLineByOffset(uint64_t offset)
120     {
121         auto it = methodSymbols_.upper_bound(offset);
122         if (it != methodSymbols_.end() && offset >= it->second.id.GetOffset()) {
123             return &it->second;
124         }
125 
126         // Enmuate all methods and put them to local cache.
127         ark::panda_file::ext::MethodSymEntry *found = nullptr;
128         for (uint32_t id : pandaFile_->GetClasses()) {
129             if (pandaFile_->IsExternal(ark::panda_file::File::EntityId(id))) {
130                 continue;
131             }
132 
133             found = EnumerateAllMethods(id, offset, found);
134         }
135         return found;
136     }
137 
QueryAllMethodSymsPandaFileExt138     std::vector<struct MethodSymInfoExt> QueryAllMethodSyms()
139     {
140         // Enmuate all methods and put them to local cache.
141         std::vector<ark::panda_file::ext::MethodSymEntry> res;
142         for (uint32_t id : pandaFile_->GetClasses()) {
143             if (pandaFile_->IsExternal(ark::panda_file::File::EntityId(id))) {
144                 continue;
145             }
146 
147             EnumerateMethods(id, res);
148         }
149 
150         std::vector<struct MethodSymInfoExt> methodInfo;
151         methodInfo.reserve(res.size());
152         for (auto const &me : res) {
153             struct MethodSymInfoExt msi {};
154             msi.offset = me.id.GetOffset();
155             msi.length = me.length;
156             msi.name = me.name;
157             methodInfo.push_back(msi);
158         }
159         return methodInfo;
160     }
161 
162 private:
EnumerateMethodsPandaFileExt163     inline void EnumerateMethods(uint32_t id, std::vector<ark::panda_file::ext::MethodSymEntry> &res)
164     {
165         ark::panda_file::ClassDataAccessor cda {*pandaFile_, ark::panda_file::File::EntityId(id)};
166         cda.EnumerateMethods([&](ark::panda_file::MethodDataAccessor &mda) -> void {
167             if (mda.GetCodeId().has_value()) {
168                 ark::panda_file::CodeDataAccessor ca {*pandaFile_, mda.GetCodeId().value()};
169                 res.push_back({mda.GetCodeId().value(), ca.GetCodeSize(), GetMethodSymName(mda)});
170             }
171         });
172     }
173 
GetMethodSymNamePandaFileExt174     inline std::string GetMethodSymName(ark::panda_file::MethodDataAccessor &mda)
175     {
176         std::stringstream ss;
177         std::string_view cname(ark::utf::Mutf8AsCString(pandaFile_->GetStringData(mda.GetClassId()).data));
178         if (!cname.empty()) {
179             ss << cname.substr(0, cname.size() - 1);
180         }
181         ss << "." << ark::utf::Mutf8AsCString(pandaFile_->GetStringData(mda.GetNameId()).data);
182 
183         return ss.str();
184     }
185 
186     std::map<uint64_t, ark::panda_file::ext::MethodSymEntry> methodSymbols_;
187     std::unique_ptr<const ark::panda_file::File> pandaFile_;
188 };
189 
OpenPandafileFromMemoryExt(void * addr,const uint64_t * size,const std::string & fileName,PandaFileExt ** pandaFileExt)190 bool OpenPandafileFromMemoryExt(void *addr, const uint64_t *size, [[maybe_unused]] const std::string &fileName,
191                                 PandaFileExt **pandaFileExt)
192 {
193     if (pandaFileExt == nullptr) {
194         return false;
195     }
196 
197     ark::os::mem::ConstBytePtr ptr(
198         reinterpret_cast<std::byte *>(addr), *size,
199         []([[maybe_unused]] std::byte *paramBuffer, [[maybe_unused]] size_t paramSize) noexcept {});
200     auto pf = ark::panda_file::File::OpenFromMemory(std::move(ptr));
201     if (pf == nullptr) {
202         return false;
203     }
204 
205     auto pfExt = std::make_unique<PandaFileExt>(std::move(pf));
206     *pandaFileExt = pfExt.release();
207     return true;
208 }
209 
OpenPandafileFromFdExt(int fd,uint64_t offset,const std::string & fileName,PandaFileExt ** pandaFileExt)210 bool OpenPandafileFromFdExt([[maybe_unused]] int fd, [[maybe_unused]] uint64_t offset, const std::string &fileName,
211                             PandaFileExt **pandaFileExt)
212 {
213     auto pf = ark::panda_file::OpenPandaFile(fileName);
214     if (pf == nullptr) {
215         return false;
216     }
217 
218     auto pfExt = std::make_unique<PandaFileExt>(std::move(pf));
219     *pandaFileExt = pfExt.release();
220     return true;
221 }
222 
QueryMethodSymByOffsetExt(struct PandaFileExt * pf,uint64_t offset,struct MethodSymInfoExt * methodInfo)223 bool QueryMethodSymByOffsetExt(struct PandaFileExt *pf, uint64_t offset, struct MethodSymInfoExt *methodInfo)
224 {
225     auto entry = pf->QueryMethodSymByOffset(offset);
226     if ((entry != nullptr) && (methodInfo != nullptr)) {
227         methodInfo->offset = entry->id.GetOffset();
228         methodInfo->length = entry->length;
229         methodInfo->name = entry->name;
230         return true;
231     }
232     return false;
233 }
234 
QueryMethodSymAndLineByOffsetExt(struct PandaFileExt * pf,uint64_t offset,struct MethodSymInfoExt * methodInfo)235 bool QueryMethodSymAndLineByOffsetExt(struct PandaFileExt *pf, uint64_t offset, struct MethodSymInfoExt *methodInfo)
236 {
237     auto entry = pf->QueryMethodSymAndLineByOffset(offset);
238     if ((entry != nullptr) && (methodInfo != nullptr)) {
239         methodInfo->offset = entry->id.GetOffset();
240         methodInfo->length = entry->length;
241         methodInfo->name = entry->name;
242         return true;
243     }
244     return false;
245 }
246 
QueryAllMethodSymsExt(PandaFileExt * pf,MethodSymInfoExtCallBack callback,void * userData)247 void QueryAllMethodSymsExt(PandaFileExt *pf, MethodSymInfoExtCallBack callback, void *userData)
248 {
249     auto methodInfos = pf->QueryAllMethodSyms();
250     for (auto mi : methodInfos) {
251         callback(&mi, userData);
252     }
253 }
254 
255 #ifdef __cplusplus
256 }  // extern "C"
257 #endif
258