• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2021-2022 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 panda::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         ASSERT(iter != cm->end());
43         return iter->second;
44     }
45 
46 private:
47     panda_file::ItemContainer *cont_;
48 };
49 
50 class LinkerDebugInfoScrapper : public panda_file::DebugInfoUpdater<LinkerDebugInfoScrapper> {
51 public:
52     using Super = panda_file::DebugInfoUpdater<LinkerDebugInfoScrapper>;
53 
LinkerDebugInfoScrapper(const panda_file::File * file,CodePatcher * patcher)54     LinkerDebugInfoScrapper(const panda_file::File *file, CodePatcher *patcher) : Super(file), patcher_(patcher) {}
55 
GetOrCreateStringItem(const std::string & s)56     panda_file::StringItem *GetOrCreateStringItem(const std::string &s)
57     {
58         patcher_->Add(s);
59         return nullptr;
60     }
61 
GetType(panda_file::File::EntityId typeId,const std::string & typeName)62     panda_file::BaseClassItem *GetType([[maybe_unused]] panda_file::File::EntityId typeId,
63                                        [[maybe_unused]] const std::string &typeName)
64     {
65         return nullptr;
66     }
67 
68 private:
69     CodePatcher *patcher_;
70 };
71 }  // namespace
72 
Add(Change c)73 void CodePatcher::Add(Change c)
74 {
75     changes_.emplace_back(std::move(c));
76 }
77 
Devour(CodePatcher && p)78 void CodePatcher::Devour(CodePatcher &&p)
79 {
80     const auto oldSize = changes_.size();
81     changes_.insert(changes_.end(), std::move_iterator(p.changes_.begin()), std::move_iterator(p.changes_.end()));
82     const auto newSize = changes_.size();
83     p.changes_.clear();
84 
85     ranges_.emplace_back(oldSize, newSize);
86 }
87 
AddRange(std::pair<size_t,size_t> range)88 void CodePatcher::AddRange(std::pair<size_t, size_t> range)
89 {
90     ranges_.push_back(range);
91 }
92 
ApplyLiteralArrayChange(LiteralArrayChange & lc,Context * ctx)93 void CodePatcher::ApplyLiteralArrayChange(LiteralArrayChange &lc, Context *ctx)
94 {
95     auto id = ctx->literalArrayId_++;
96     lc.it = ctx->GetContainer().GetOrCreateLiteralArrayItem(std::to_string(id));
97 
98     auto &oldIts = lc.old->GetItems();
99     auto newIts = std::vector<panda_file::LiteralItem>();
100     newIts.reserve(oldIts.size());
101 
102     for (const auto &i : oldIts) {
103         using LIT = panda_file::LiteralItem::Type;
104         switch (i.GetType()) {
105             case LIT::B1:
106             case LIT::B2:
107             case LIT::B4:
108             case LIT::B8:
109                 newIts.emplace_back(i);
110                 break;
111             case LIT::STRING: {
112                 auto str = ctx->StringFromOld(i.GetValue<panda_file::StringItem *>());
113                 newIts.emplace_back(str);
114                 break;
115             }
116             case LIT::METHOD: {
117                 auto meth = i.GetValue<panda_file::MethodItem *>();
118                 auto iter = ctx->knownItems_.find(meth);
119                 ASSERT(iter != ctx->knownItems_.end());
120                 ASSERT(iter->second->GetItemType() == panda_file::ItemTypes::METHOD_ITEM);
121                 newIts.emplace_back(static_cast<panda_file::MethodItem *>(iter->second));
122                 break;
123             }
124             default:
125                 UNREACHABLE();
126         }
127     }
128 
129     lc.it->AddItems(newIts);
130 }
131 
ApplyDeps(Context * ctx)132 void CodePatcher::ApplyDeps(Context *ctx)
133 {
134     for (auto &v : changes_) {
135         std::visit(
136             [this, ctx](auto &a) {
137                 using T = std::remove_cv_t<std::remove_reference_t<decltype(a)>>;
138                 // IndexedChange, StringChange, LiteralArrayChange, std::string, std::function<void()>>
139                 if constexpr (std::is_same_v<T, IndexedChange>) {
140                     a.mi->AddIndexDependency(a.it);
141                 } else if constexpr (std::is_same_v<T, StringChange>) {
142                     a.it = ctx->GetContainer().GetOrCreateStringItem(a.str);
143                 } else if constexpr (std::is_same_v<T, LiteralArrayChange>) {
144                     ApplyLiteralArrayChange(a, ctx);
145                 } else if constexpr (std::is_same_v<T, std::string>) {
146                     ctx->GetContainer().GetOrCreateStringItem(a);
147                 } else if constexpr (std::is_same_v<T, std::function<void()>>) {
148                     // nothing
149                 } else {
150                     UNREACHABLE();
151                 }
152             },
153             v);
154     }
155 }
156 
Patch(const std::pair<size_t,size_t> range)157 void CodePatcher::Patch(const std::pair<size_t, size_t> range)
158 {
159     for (size_t i = range.first; i < range.second; i++) {
160         auto &v = changes_[i];
161         std::visit(
162             [](auto &a) {
163                 using T = std::remove_cv_t<std::remove_reference_t<decltype(a)>>;
164                 if constexpr (std::is_same_v<T, IndexedChange>) {
165                     auto idx = a.it->GetIndex(a.mi);
166                     a.inst.UpdateId(BytecodeId(idx));
167                 } else if constexpr (std::is_same_v<T, StringChange>) {
168                     auto off = a.it->GetOffset();
169                     a.inst.UpdateId(BytecodeId(off));
170                 } else if constexpr (std::is_same_v<T, LiteralArrayChange>) {
171                     auto off = a.it->GetIndex();
172                     a.inst.UpdateId(BytecodeId(off));
173                 } else if constexpr (std::is_same_v<T, std::string>) {
174                     // nothing
175                 } else if constexpr (std::is_same_v<T, std::function<void()>>) {
176                     a();
177                 } else {
178                     UNREACHABLE();
179                 }
180             },
181             v);
182     }
183 }
184 
ProcessCodeData(CodePatcher & p,CodeData * data)185 void Context::ProcessCodeData(CodePatcher &p, CodeData *data)
186 {
187     using Flags = panda::BytecodeInst<panda::BytecodeInstMode::FAST>::Flags;
188     using EntityId = panda_file::File::EntityId;
189 
190     const auto myId = EntityId(data->omi->GetOffset());
191     auto *items = data->fileReader->GetItems();
192 
193     if (data->code != nullptr) {
194         auto inst = BytecodeInstruction(data->code->data());
195         size_t offset = 0;
196         const auto limit = data->code->size();
197 
198         auto filePtr = data->fileReader->GetFilePtr();
199 
200         using Resolver = EntityId (panda_file::File::*)(EntityId id, panda_file::File::Index idx) const;
201 
202         auto makeWithId = [&](Resolver resolve) {
203             auto idx = inst.GetId().AsIndex();
204             auto oldId = (filePtr->*resolve)(myId, idx);
205             auto iter = items->find(oldId);
206             ASSERT(iter != items->end());
207             auto asIndexed = static_cast<panda_file::IndexedItem *>(iter->second);
208             auto found = knownItems_.find(asIndexed);
209             ASSERT(found != knownItems_.end());
210             p.Add(CodePatcher::IndexedChange {inst, data->nmi, static_cast<panda_file::IndexedItem *>(found->second)});
211         };
212 
213         while (offset < limit) {
214             if (inst.HasFlag(Flags::TYPE_ID)) {
215                 makeWithId(&panda_file::File::ResolveClassIndex);
216                 // inst.UpdateId()
217             } else if (inst.HasFlag(Flags::METHOD_ID)) {
218                 makeWithId(&panda_file::File::ResolveMethodIndex);
219             } else if (inst.HasFlag(Flags::FIELD_ID)) {
220                 makeWithId(&panda_file::File::ResolveFieldIndex);
221             } else if (inst.HasFlag(Flags::STRING_ID)) {
222                 BytecodeId bId = inst.GetId();
223                 auto oldId = bId.AsFileId();
224 
225                 auto sData = filePtr->GetStringData(oldId);
226                 auto itemStr = std::string(utf::Mutf8AsCString(sData.data));
227                 p.Add(CodePatcher::StringChange {inst, std::move(itemStr)});
228             } else if (inst.HasFlag(Flags::LITERALARRAY_ID)) {
229                 BytecodeId bId = inst.GetId();
230                 auto oldIdx = bId.AsRawValue();
231                 auto arrs = filePtr->GetLiteralArrays();
232                 ASSERT(oldIdx < arrs.size());
233                 auto off = arrs[oldIdx];
234                 auto iter = items->find(EntityId(off));
235                 ASSERT(iter != items->end());
236                 ASSERT(iter->second->GetItemType() == panda_file::ItemTypes::LITERAL_ARRAY_ITEM);
237                 p.Add(
238                     CodePatcher::LiteralArrayChange {inst, static_cast<panda_file::LiteralArrayItem *>(iter->second)});
239             }
240 
241             offset += inst.GetSize();
242             inst = inst.GetNext();
243         }
244     }
245 
246     if (auto *dbg = data->omi->GetDebugInfo(); data->patchLnp && dbg != nullptr) {
247         auto file = data->fileReader->GetFilePtr();
248         auto scrapper = LinkerDebugInfoScrapper(file, &p);
249         auto off = data->omi->GetDebugInfo()->GetOffset();
250         ASSERT(off != 0);
251         auto eId = panda_file::File::EntityId(off);
252         scrapper.Scrap(eId);
253 
254         auto newDbg = data->nmi->GetDebugInfo();
255         p.Add([file, this, newDbg, eId]() {
256             auto updater = LinkerDebugInfoUpdater(file, &cont_);
257             updater.Emit(newDbg, eId);
258         });
259     }
260 }
261 
262 }  // namespace panda::static_linker
263