• 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 #include "libpandafile/bytecode_instruction.h"
17 #include "libpandafile/bytecode_instruction-inl.h"
18 #include "libpandafile/file_items.h"
19 #include "libpandafile/debug_info_updater-inl.h"
20 
21 #include "linker_context.h"
22 
23 namespace ark::static_linker {
24 
25 namespace {
26 
27 class LinkerDebugInfoUpdater : public panda_file::DebugInfoUpdater<LinkerDebugInfoUpdater> {
28 public:
29     using Super = panda_file::DebugInfoUpdater<LinkerDebugInfoUpdater>;
30 
LinkerDebugInfoUpdater(const panda_file::File * file,panda_file::ItemContainer * cont)31     LinkerDebugInfoUpdater(const panda_file::File *file, panda_file::ItemContainer *cont) : Super(file), cont_(cont) {}
32 
GetOrCreateStringItem(const std::string & s)33     panda_file::StringItem *GetOrCreateStringItem(const std::string &s)
34     {
35         return cont_->GetOrCreateStringItem(s);
36     }
37 
GetType(panda_file::File::EntityId typeId,const std::string & typeName)38     panda_file::BaseClassItem *GetType([[maybe_unused]] panda_file::File::EntityId typeId, const std::string &typeName)
39     {
40         auto *cm = cont_->GetClassMap();
41         auto iter = cm->find(typeName);
42         if (iter != cm->end()) {
43             return iter->second;
44         }
45         return nullptr;
46     }
47 
48 private:
49     panda_file::ItemContainer *cont_;
50 };
51 
52 class LinkerDebugInfoScrapper : public panda_file::DebugInfoUpdater<LinkerDebugInfoScrapper> {
53 public:
54     using Super = panda_file::DebugInfoUpdater<LinkerDebugInfoScrapper>;
55 
LinkerDebugInfoScrapper(const panda_file::File * file,CodePatcher * patcher,panda_file::ItemContainer * cont)56     LinkerDebugInfoScrapper(const panda_file::File *file, CodePatcher *patcher, panda_file::ItemContainer *cont)
57         : Super(file), patcher_(patcher), cont_(cont)
58     {
59     }
60 
GetOrCreateStringItem(const std::string & s)61     panda_file::StringItem *GetOrCreateStringItem(const std::string &s)
62     {
63         patcher_->Add(s);
64         return nullptr;
65     }
66 
GetType(panda_file::File::EntityId typeId,const std::string & typeName)67     panda_file::BaseClassItem *GetType([[maybe_unused]] panda_file::File::EntityId typeId, const std::string &typeName)
68     {
69         auto *cm = cont_->GetClassMap();
70         if (cm->find(typeName) == cm->end()) {
71             // This action must create a string for primitive types.
72             GetOrCreateStringItem(typeName);
73         }
74         return nullptr;
75     }
76 
77 private:
78     CodePatcher *patcher_;
79     panda_file::ItemContainer *cont_;
80 };
81 }  // namespace
82 
Add(Change c)83 void CodePatcher::Add(Change c)
84 {
85     changes_.emplace_back(std::move(c));
86 }
87 
Devour(CodePatcher && p)88 void CodePatcher::Devour(CodePatcher &&p)
89 {
90     const auto oldSize = changes_.size();
91     changes_.insert(changes_.end(), std::move_iterator(p.changes_.begin()), std::move_iterator(p.changes_.end()));
92     const auto newSize = changes_.size();
93     p.changes_.clear();
94 
95     ranges_.emplace_back(oldSize, newSize);
96 }
97 
AddRange(std::pair<size_t,size_t> range)98 void CodePatcher::AddRange(std::pair<size_t, size_t> range)
99 {
100     ranges_.push_back(range);
101 }
102 
ApplyLiteralArrayChange(LiteralArrayChange & lc,Context * ctx)103 void CodePatcher::ApplyLiteralArrayChange(LiteralArrayChange &lc, Context *ctx)
104 {
105     auto id = ctx->literalArrayId_++;
106     lc.it = ctx->GetContainer().GetOrCreateLiteralArrayItem(std::to_string(id));
107 
108     auto &oldIts = lc.old->GetItems();
109     auto newIts = std::vector<panda_file::LiteralItem>();
110     newIts.reserve(oldIts.size());
111 
112     for (const auto &i : oldIts) {
113         using LIT = panda_file::LiteralItem::Type;
114         switch (i.GetType()) {
115             case LIT::B1:
116             case LIT::B2:
117             case LIT::B4:
118             case LIT::B8:
119                 newIts.emplace_back(i);
120                 break;
121             case LIT::STRING: {
122                 auto str = ctx->StringFromOld(i.GetValue<panda_file::StringItem *>());
123                 newIts.emplace_back(str);
124                 break;
125             }
126             case LIT::METHOD: {
127                 auto meth = i.GetValue<panda_file::MethodItem *>();
128                 auto iter = ctx->knownItems_.find(meth);
129                 ASSERT(iter != ctx->knownItems_.end());
130                 ASSERT(iter->second->GetItemType() == panda_file::ItemTypes::METHOD_ITEM);
131                 newIts.emplace_back(static_cast<panda_file::MethodItem *>(iter->second));
132                 break;
133             }
134             default:
135                 UNREACHABLE();
136         }
137     }
138 
139     lc.it->AddItems(newIts);
140 }
141 
ApplyDeps(Context * ctx)142 void CodePatcher::ApplyDeps(Context *ctx)
143 {
144     for (auto &v : changes_) {
145         std::visit(
146             [this, ctx](auto &a) {
147                 using T = std::remove_cv_t<std::remove_reference_t<decltype(a)>>;
148                 // IndexedChange, StringChange, LiteralArrayChange, std::string, std::function<void()>>
149                 if constexpr (std::is_same_v<T, IndexedChange>) {
150                     a.mi->AddIndexDependency(a.it);
151                 } else if constexpr (std::is_same_v<T, StringChange>) {
152                     a.it = ctx->GetContainer().GetOrCreateStringItem(a.str);
153                 } else if constexpr (std::is_same_v<T, LiteralArrayChange>) {
154                     ApplyLiteralArrayChange(a, ctx);
155                 } else if constexpr (std::is_same_v<T, std::string>) {
156                     ctx->GetContainer().GetOrCreateStringItem(a);
157                 } else if constexpr (std::is_same_v<T, std::function<void()>>) {
158                     // nothing
159                 } else {
160                     UNREACHABLE();
161                 }
162             },
163             v);
164     }
165 }
166 
Patch(const std::pair<size_t,size_t> range)167 void CodePatcher::Patch(const std::pair<size_t, size_t> range)
168 {
169     for (size_t i = range.first; i < range.second; i++) {
170         auto &v = changes_[i];
171         std::visit(
172             [](auto &a) {
173                 using T = std::remove_cv_t<std::remove_reference_t<decltype(a)>>;
174                 if constexpr (std::is_same_v<T, IndexedChange>) {
175                     auto idx = a.it->GetIndex(a.mi);
176                     a.inst.UpdateId(BytecodeId(idx));
177                 } else if constexpr (std::is_same_v<T, StringChange>) {
178                     auto off = a.it->GetOffset();
179                     a.inst.UpdateId(BytecodeId(off));
180                 } else if constexpr (std::is_same_v<T, LiteralArrayChange>) {
181                     auto off = a.it->GetIndex();
182                     a.inst.UpdateId(BytecodeId(off));
183                 } else if constexpr (std::is_same_v<T, std::string>) {
184                     // nothing
185                 } else if constexpr (std::is_same_v<T, std::function<void()>>) {
186                     a();
187                 } else {
188                     UNREACHABLE();
189                 }
190             },
191             v);
192     }
193 }
194 
MakeChangeWithId(CodePatcher & p,CodeData * data)195 void Context::MakeChangeWithId(CodePatcher &p, CodeData *data)
196 {
197     using Flags = ark::BytecodeInst<ark::BytecodeInstMode::FAST>::Flags;
198     using EntityId = panda_file::File::EntityId;
199 
200     const auto myId = EntityId(data->omi->GetOffset());
201     auto *items = data->fileReader->GetItems();
202     auto inst = BytecodeInstruction(data->code->data());
203     size_t offset = 0;
204     const auto limit = data->code->size();
205 
206     auto filePtr = data->fileReader->GetFilePtr();
207 
208     using Resolver = EntityId (panda_file::File::*)(EntityId id, panda_file::File::Index idx) const;
209 
210     auto makeWithId = [&p, &inst, &filePtr, &items, &myId, &data, this](Resolver resolve) {
211         auto idx = inst.GetId().AsIndex();
212         auto oldId = (filePtr->*resolve)(myId, idx);
213         auto iter = items->find(oldId);
214         ASSERT(iter != items->end());
215         auto asIndexed = static_cast<panda_file::IndexedItem *>(iter->second);
216         auto found = knownItems_.find(asIndexed);
217         ASSERT(found != knownItems_.end());
218         p.Add(CodePatcher::IndexedChange {inst, data->nmi, static_cast<panda_file::IndexedItem *>(found->second)});
219     };
220 
221     while (offset < limit) {
222         if (inst.HasFlag(Flags::TYPE_ID)) {
223             makeWithId(&panda_file::File::ResolveClassIndex);
224             // inst.UpdateId()
225         } else if (inst.HasFlag(Flags::METHOD_ID)) {
226             makeWithId(&panda_file::File::ResolveMethodIndex);
227         } else if (inst.HasFlag(Flags::FIELD_ID)) {
228             makeWithId(&panda_file::File::ResolveFieldIndex);
229         } else if (inst.HasFlag(Flags::STRING_ID)) {
230             BytecodeId bId = inst.GetId();
231             auto oldId = bId.AsFileId();
232 
233             auto sData = filePtr->GetStringData(oldId);
234             auto itemStr = std::string(utf::Mutf8AsCString(sData.data));
235             p.Add(CodePatcher::StringChange {inst, std::move(itemStr)});
236         } else if (inst.HasFlag(Flags::LITERALARRAY_ID)) {
237             BytecodeId bId = inst.GetId();
238             auto oldIdx = bId.AsRawValue();
239             auto arrs = filePtr->GetLiteralArrays();
240             ASSERT(oldIdx < arrs.size());
241             auto off = arrs[oldIdx];
242             auto iter = items->find(EntityId(off));
243             ASSERT(iter != items->end());
244             ASSERT(iter->second->GetItemType() == panda_file::ItemTypes::LITERAL_ARRAY_ITEM);
245             p.Add(CodePatcher::LiteralArrayChange {inst, static_cast<panda_file::LiteralArrayItem *>(iter->second)});
246         }
247 
248         offset += inst.GetSize();
249         inst = inst.GetNext();
250     }
251 }
252 
ProcessCodeData(CodePatcher & p,CodeData * data)253 void Context::ProcessCodeData(CodePatcher &p, CodeData *data)
254 {
255     if (data->code != nullptr) {
256         MakeChangeWithId(p, data);
257     }
258 
259     auto *dbg = data->omi->GetDebugInfo();
260     if (dbg == nullptr || conf_.stripDebugInfo) {
261         return;
262     }
263 
264     // Collect string items for each method with debug information.
265     auto file = data->fileReader->GetFilePtr();
266     auto scrapper = LinkerDebugInfoScrapper(file, &p, &cont_);
267     auto off = dbg->GetOffset();
268     ASSERT(off != 0);
269     auto eId = panda_file::File::EntityId(off);
270     scrapper.Scrap(eId);
271 
272     auto newDbg = data->nmi->GetDebugInfo();
273     p.Add([file, this, newDbg, eId, patchLnp = data->patchLnp]() {
274         auto updater = LinkerDebugInfoUpdater(file, &cont_);
275 
276         auto *constantPool = newDbg->GetConstantPool();
277         if (patchLnp) {
278             // Original `LineNumberProgram` - must emit both instructions and their arguments.
279             auto *lnpItem = newDbg->GetLineNumberProgram();
280             updater.Emit(lnpItem, constantPool, eId);
281         } else {
282             // `LineNumberProgram` is reused and its instructions will be emitted by owner-method.
283             // Still need to emit instructions' arguments, which are unique for each method.
284             panda_file::LineNumberProgramItemBase lnpItem;
285             updater.Emit(&lnpItem, constantPool, eId);
286         }
287     });
288 }
289 
290 }  // namespace ark::static_linker
291