• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2021-2025 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<bool(bool)>>
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                     // unreferenced string item should be mark
157                     auto stringItem = ctx->GetContainer().GetOrCreateStringItem(a);
158                     stringItem->SetDependencyMark();
159                 } else if constexpr (std::is_same_v<T, std::function<bool(bool)>>) {
160                     // nothing
161                 } else {
162                     UNREACHABLE();
163                 }
164             },
165             v);
166     }
167 }
168 
TryDeletePatch()169 void CodePatcher::TryDeletePatch()
170 {
171     auto markDependency = [](auto &a, bool &shouldDelete) {
172         using T = std::remove_cv_t<std::remove_reference_t<decltype(a)>>;
173         if constexpr (std::is_same_v<T, IndexedChange>) {
174             if (!a.mi->GetDependencyMark()) {
175                 shouldDelete = true;
176             }
177         } else if constexpr (std::is_same_v<T, StringChange>) {
178             if (!a.mi->GetDependencyMark()) {
179                 shouldDelete = true;
180             }
181         } else if constexpr (std::is_same_v<T, std::function<bool(bool)>>) {
182             if (a(true)) {
183                 shouldDelete = true;
184             }
185         }
186     };
187 
188     for (auto it = changes_.begin(); it != changes_.end();) {
189         auto &v = *it;
190         bool shouldDelete = false;
191         std::visit([&](auto &a) { markDependency(a, shouldDelete); }, v);
192         if (shouldDelete) {
193             it = changes_.erase(it);
194         } else {
195             ++it;
196         }
197     }
198 }
199 
Patch(const std::pair<size_t,size_t> range)200 void CodePatcher::Patch(const std::pair<size_t, size_t> range)
201 {
202     for (size_t i = range.first; i < range.second; i++) {
203         auto &v = changes_[i];
204         std::visit(
205             [](auto &a) {
206                 using T = std::remove_cv_t<std::remove_reference_t<decltype(a)>>;
207                 if constexpr (std::is_same_v<T, IndexedChange>) {
208                     auto idx = a.it->GetIndex(a.mi);
209                     a.inst.UpdateId(BytecodeId(idx));
210                 } else if constexpr (std::is_same_v<T, StringChange>) {
211                     auto off = a.it->GetOffset();
212                     a.inst.UpdateId(BytecodeId(off));
213                 } else if constexpr (std::is_same_v<T, LiteralArrayChange>) {
214                     auto off = a.it->GetIndex();
215                     a.inst.UpdateId(BytecodeId(off));
216                 } else if constexpr (std::is_same_v<T, std::string>) {
217                     // nothing
218                 } else if constexpr (std::is_same_v<T, std::function<bool(bool)>>) {
219                     a(false);
220                 } else {
221                     UNREACHABLE();
222                 }
223             },
224             v);
225     }
226 }
227 
AddStringDependency()228 void CodePatcher::AddStringDependency()
229 {
230     auto markStringDependency = [](auto &a) {
231         using T = std::remove_cv_t<std::remove_reference_t<decltype(a)>>;
232         if constexpr (std::is_same_v<T, StringChange>) {
233             if (a.mi->GetDependencyMark()) {
234                 a.it->SetDependencyMark();
235             }
236         }
237     };
238 
239     for (auto it = changes_.begin(); it != changes_.end();) {
240         auto &v = *it;
241         std::visit(markStringDependency, v);
242         ++it;
243     }
244 }
245 
HandleStringId(CodePatcher & p,const BytecodeInstruction & inst,const panda_file::File * filePtr,CodeData * data)246 void Context::HandleStringId(CodePatcher &p, const BytecodeInstruction &inst, const panda_file::File *filePtr,
247                              CodeData *data)
248 {
249     BytecodeId bId = inst.GetId();
250     auto oldId = bId.AsFileId();
251     auto sData = filePtr->GetStringData(oldId);
252     auto itemStr = std::string(utf::Mutf8AsCString(sData.data));
253     p.Add(CodePatcher::StringChange {inst, std::move(itemStr), data->nmi});
254 }
255 
HandleLiteralArrayId(CodePatcher & p,const BytecodeInstruction & inst,const panda_file::File * filePtr,const std::map<panda_file::File::EntityId,panda_file::BaseItem * > * items)256 void Context::HandleLiteralArrayId(CodePatcher &p, const BytecodeInstruction &inst, const panda_file::File *filePtr,
257                                    const std::map<panda_file::File::EntityId, panda_file::BaseItem *> *items)
258 {
259     BytecodeId bId = inst.GetId();
260     auto oldIdx = bId.AsRawValue();
261     auto arrs = filePtr->GetLiteralArrays();
262     ASSERT(oldIdx < arrs.size());
263     auto off = arrs[oldIdx];
264     auto iter = items->find(panda_file::File::EntityId(off));
265     ASSERT(iter != items->end());
266     ASSERT(iter->second->GetItemType() == panda_file::ItemTypes::LITERAL_ARRAY_ITEM);
267     p.Add(CodePatcher::LiteralArrayChange {inst, static_cast<panda_file::LiteralArrayItem *>(iter->second)});
268 }
269 
MakeChangeWithId(CodePatcher & p,CodeData * data)270 void Context::MakeChangeWithId(CodePatcher &p, CodeData *data)
271 {
272     using Flags = ark::BytecodeInst<ark::BytecodeInstMode::FAST>::Flags;
273     using EntityId = panda_file::File::EntityId;
274 
275     const auto myId = EntityId(data->omi->GetOffset());
276     auto *items = data->fileReader->GetItems();
277     auto inst = BytecodeInstruction(data->code->data());
278     size_t offset = 0;
279     const auto limit = data->code->size();
280 
281     auto filePtr = data->fileReader->GetFilePtr();
282 
283     using Resolver = EntityId (panda_file::File::*)(EntityId id, panda_file::File::Index idx) const;
284 
285     auto makeWithId = [&p, &inst, &filePtr, &items, &myId, &data, this](Resolver resolve) {
286         auto idx = inst.GetId().AsIndex();
287         auto oldId = (filePtr->*resolve)(myId, idx);
288         auto iter = items->find(oldId);
289         ASSERT(iter != items->end());
290         auto asIndexed = static_cast<panda_file::IndexedItem *>(iter->second);
291         auto found = knownItems_.find(asIndexed);
292         ASSERT(found != knownItems_.end());
293         p.Add(CodePatcher::IndexedChange {inst, data->nmi, static_cast<panda_file::IndexedItem *>(found->second)});
294     };
295 
296     while (offset < limit) {
297         if (offset + inst.GetSize() > limit) {
298             LOG(FATAL, STATIC_LINKER) << "Invalid code size: " << limit << ", offset: " << offset
299                                       << ", instruction size: " << inst.GetSize();
300             break;
301         }
302         if (inst.HasFlag(Flags::TYPE_ID)) {
303             makeWithId(&panda_file::File::ResolveClassIndex);
304             // inst.UpdateId()
305         } else if (inst.HasFlag(Flags::METHOD_ID) || inst.HasFlag(Flags::STATIC_METHOD_ID)) {
306             makeWithId(&panda_file::File::ResolveMethodIndex);
307         } else if (inst.HasFlag(Flags::FIELD_ID) || inst.HasFlag(Flags::STATIC_FIELD_ID)) {
308             makeWithId(&panda_file::File::ResolveFieldIndex);
309         } else if (inst.HasFlag(Flags::STRING_ID)) {
310             HandleStringId(p, inst, filePtr, data);
311         } else if (inst.HasFlag(Flags::LITERALARRAY_ID)) {
312             HandleLiteralArrayId(p, inst, filePtr, items);
313         }
314 
315         offset += inst.GetSize();
316         inst = inst.GetNext();
317     }
318     if (offset != limit) {
319         LOG(FATAL, STATIC_LINKER) << "Code size mismatch: expected " << limit << ", got " << offset;
320     }
321 }
322 
ProcessCodeData(CodePatcher & p,CodeData * data)323 void Context::ProcessCodeData(CodePatcher &p, CodeData *data)
324 {
325     if (data->code != nullptr) {
326         MakeChangeWithId(p, data);
327     }
328 
329     auto *dbg = data->omi->GetDebugInfo();
330     if (dbg == nullptr || conf_.stripDebugInfo) {
331         return;
332     }
333 
334     // Collect string items for each method with debug information.
335     auto file = data->fileReader->GetFilePtr();
336     auto scrapper = LinkerDebugInfoScrapper(file, &p, &cont_);
337     auto off = dbg->GetOffset();
338     ASSERT(off != 0);
339     auto eId = panda_file::File::EntityId(off);
340     scrapper.Scrap(eId);
341 
342     auto newDbg = data->nmi->GetDebugInfo();
343     p.Add([file, this, newDbg, eId, patchLnp = data->patchLnp, nmi = data->nmi](bool peek) -> bool {
344         if (peek) {
345             // peek won't patch lnp, only return method mark for delete judge
346             return !nmi->GetDependencyMark();
347         }
348         auto updater = LinkerDebugInfoUpdater(file, &cont_);
349 
350         auto *constantPool = newDbg->GetConstantPool();
351         if (patchLnp) {
352             // Original `LineNumberProgram` - must emit both instructions and their arguments.
353             auto *lnpItem = newDbg->GetLineNumberProgram();
354             updater.Emit(lnpItem, constantPool, eId);
355         } else {
356             auto *lnpItemold = newDbg->GetLineNumberProgram();
357             if (lnpItemold->GetData().empty()) {
358                 // emit if lnp size zero after delete item
359                 auto *lnpItem = newDbg->GetLineNumberProgram();
360                 updater.Emit(lnpItem, constantPool, eId);
361             } else {
362                 // `LineNumberProgram` is reused and its instructions will be emitted by owner-method.
363                 // Still need to emit instructions' arguments, which are unique for each method.
364                 panda_file::LineNumberProgramItemBase lnpItem;
365                 updater.Emit(&lnpItem, constantPool, eId);
366             }
367         }
368         return false;
369     });
370 }
371 
372 }  // namespace ark::static_linker
373