• 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 
ProcessCodeData(CodePatcher & p,CodeData * data)195 void Context::ProcessCodeData(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 
203     if (data->code != nullptr) {
204         auto inst = BytecodeInstruction(data->code->data());
205         size_t offset = 0;
206         const auto limit = data->code->size();
207 
208         auto filePtr = data->fileReader->GetFilePtr();
209 
210         using Resolver = EntityId (panda_file::File::*)(EntityId id, panda_file::File::Index idx) const;
211 
212         auto makeWithId = [&p, &inst, &filePtr, &items, &myId, &data, this](Resolver resolve) {
213             auto idx = inst.GetId().AsIndex();
214             auto oldId = (filePtr->*resolve)(myId, idx);
215             auto iter = items->find(oldId);
216             ASSERT(iter != items->end());
217             auto asIndexed = static_cast<panda_file::IndexedItem *>(iter->second);
218             auto found = knownItems_.find(asIndexed);
219             ASSERT(found != knownItems_.end());
220             p.Add(CodePatcher::IndexedChange {inst, data->nmi, static_cast<panda_file::IndexedItem *>(found->second)});
221         };
222 
223         while (offset < limit) {
224             if (inst.HasFlag(Flags::TYPE_ID)) {
225                 makeWithId(&panda_file::File::ResolveClassIndex);
226                 // inst.UpdateId()
227             } else if (inst.HasFlag(Flags::METHOD_ID)) {
228                 makeWithId(&panda_file::File::ResolveMethodIndex);
229             } else if (inst.HasFlag(Flags::FIELD_ID)) {
230                 makeWithId(&panda_file::File::ResolveFieldIndex);
231             } else if (inst.HasFlag(Flags::STRING_ID)) {
232                 BytecodeId bId = inst.GetId();
233                 auto oldId = bId.AsFileId();
234 
235                 auto sData = filePtr->GetStringData(oldId);
236                 auto itemStr = std::string(utf::Mutf8AsCString(sData.data));
237                 p.Add(CodePatcher::StringChange {inst, std::move(itemStr)});
238             } else if (inst.HasFlag(Flags::LITERALARRAY_ID)) {
239                 BytecodeId bId = inst.GetId();
240                 auto oldIdx = bId.AsRawValue();
241                 auto arrs = filePtr->GetLiteralArrays();
242                 ASSERT(oldIdx < arrs.size());
243                 auto off = arrs[oldIdx];
244                 auto iter = items->find(EntityId(off));
245                 ASSERT(iter != items->end());
246                 ASSERT(iter->second->GetItemType() == panda_file::ItemTypes::LITERAL_ARRAY_ITEM);
247                 p.Add(
248                     CodePatcher::LiteralArrayChange {inst, static_cast<panda_file::LiteralArrayItem *>(iter->second)});
249             }
250 
251             offset += inst.GetSize();
252             inst = inst.GetNext();
253         }
254     }
255 
256     auto *dbg = data->omi->GetDebugInfo();
257     if (dbg == nullptr || conf_.stripDebugInfo) {
258         return;
259     }
260 
261     // Collect string items for each method with debug information.
262     auto file = data->fileReader->GetFilePtr();
263     auto scrapper = LinkerDebugInfoScrapper(file, &p, &cont_);
264     auto off = dbg->GetOffset();
265     ASSERT(off != 0);
266     auto eId = panda_file::File::EntityId(off);
267     scrapper.Scrap(eId);
268 
269     auto newDbg = data->nmi->GetDebugInfo();
270     p.Add([file, this, newDbg, eId, patchLnp = data->patchLnp]() {
271         auto updater = LinkerDebugInfoUpdater(file, &cont_);
272 
273         auto *constantPool = newDbg->GetConstantPool();
274         if (patchLnp) {
275             // Original `LineNumberProgram` - must emit both instructions and their arguments.
276             auto *lnpItem = newDbg->GetLineNumberProgram();
277             updater.Emit(lnpItem, constantPool, eId);
278         } else {
279             // `LineNumberProgram` is reused and its instructions will be emitted by owner-method.
280             // Still need to emit instructions' arguments, which are unique for each method.
281             panda_file::LineNumberProgramItemBase lnpItem;
282             updater.Emit(&lnpItem, constantPool, eId);
283         }
284     });
285 }
286 
287 }  // namespace ark::static_linker
288