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