1 /*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "link/ReferenceLinker.h"
18
19 #include "android-base/logging.h"
20 #include "androidfw/ResourceTypes.h"
21
22 #include "Diagnostics.h"
23 #include "ResourceTable.h"
24 #include "ResourceUtils.h"
25 #include "ResourceValues.h"
26 #include "ValueVisitor.h"
27 #include "link/Linkers.h"
28 #include "process/IResourceTableConsumer.h"
29 #include "process/SymbolTable.h"
30 #include "trace/TraceBuffer.h"
31 #include "util/Util.h"
32 #include "xml/XmlUtil.h"
33
34 using ::aapt::ResourceUtils::StringBuilder;
35 using ::android::StringPiece;
36
37 namespace aapt {
38
39 namespace {
40
41 // The ReferenceLinkerVisitor will follow all references and make sure they point
42 // to resources that actually exist, either in the local resource table, or as external
43 // symbols. Once the target resource has been found, the ID of the resource will be assigned
44 // to the reference object.
45 //
46 // NOTE: All of the entries in the ResourceTable must be assigned IDs.
47 class ReferenceLinkerVisitor : public DescendingValueVisitor {
48 public:
49 using DescendingValueVisitor::Visit;
50
ReferenceLinkerVisitor(const CallSite & callsite,IAaptContext * context,SymbolTable * symbols,StringPool * string_pool,xml::IPackageDeclStack * decl)51 ReferenceLinkerVisitor(const CallSite& callsite, IAaptContext* context, SymbolTable* symbols,
52 StringPool* string_pool, xml::IPackageDeclStack* decl)
53 : callsite_(callsite),
54 context_(context),
55 symbols_(symbols),
56 package_decls_(decl),
57 string_pool_(string_pool) {}
58
Visit(Reference * ref)59 void Visit(Reference* ref) override {
60 if (!ReferenceLinker::LinkReference(callsite_, ref, context_, symbols_, package_decls_)) {
61 error_ = true;
62 }
63 }
64
65 // We visit the Style specially because during this phase, values of attributes are
66 // all RawString values. Now that we are expected to resolve all symbols, we can
67 // lookup the attributes to find out which types are allowed for the attributes' values.
Visit(Style * style)68 void Visit(Style* style) override {
69 if (style->parent) {
70 Visit(&style->parent.value());
71 }
72
73 for (Style::Entry& entry : style->entries) {
74 std::string err_str;
75
76 // Transform the attribute reference so that it is using the fully qualified package
77 // name. This will also mark the reference as being able to see private resources if
78 // there was a '*' in the reference or if the package came from the private namespace.
79 Reference transformed_reference = entry.key;
80 ResolvePackage(package_decls_, &transformed_reference);
81
82 // Find the attribute in the symbol table and check if it is visible from this callsite.
83 const SymbolTable::Symbol* symbol = ReferenceLinker::ResolveAttributeCheckVisibility(
84 transformed_reference, callsite_, symbols_, &err_str);
85 if (symbol) {
86 // Assign our style key the correct ID. The ID may not exist.
87 entry.key.id = symbol->id;
88
89 // Try to convert the value to a more specific, typed value based on the attribute it is
90 // set to.
91 entry.value = ParseValueWithAttribute(std::move(entry.value), symbol->attribute.get());
92
93 // Link/resolve the final value (mostly if it's a reference).
94 entry.value->Accept(this);
95
96 // Now verify that the type of this item is compatible with the
97 // attribute it is defined for. We pass `nullptr` as the DiagMessage so that this
98 // check is fast and we avoid creating a DiagMessage when the match is successful.
99 if (!symbol->attribute->Matches(*entry.value, nullptr)) {
100 // The actual type of this item is incompatible with the attribute.
101 DiagMessage msg(entry.key.GetSource());
102
103 // Call the matches method again, this time with a DiagMessage so we fill in the actual
104 // error message.
105 symbol->attribute->Matches(*entry.value, &msg);
106 context_->GetDiagnostics()->Error(msg);
107 error_ = true;
108 }
109
110 } else {
111 DiagMessage msg(entry.key.GetSource());
112 msg << "style attribute '";
113 ReferenceLinker::WriteResourceName(entry.key, callsite_, package_decls_, &msg);
114 msg << "' " << err_str;
115 context_->GetDiagnostics()->Error(msg);
116 error_ = true;
117 }
118 }
119 }
120
HasError()121 bool HasError() {
122 return error_;
123 }
124
125 private:
126 DISALLOW_COPY_AND_ASSIGN(ReferenceLinkerVisitor);
127
128 // Transform a RawString value into a more specific, appropriate value, based on the
129 // Attribute. If a non RawString value is passed in, this is an identity transform.
ParseValueWithAttribute(std::unique_ptr<Item> value,const Attribute * attr)130 std::unique_ptr<Item> ParseValueWithAttribute(std::unique_ptr<Item> value,
131 const Attribute* attr) {
132 if (RawString* raw_string = ValueCast<RawString>(value.get())) {
133 std::unique_ptr<Item> transformed =
134 ResourceUtils::TryParseItemForAttribute(*raw_string->value, attr);
135
136 // If we could not parse as any specific type, try a basic STRING.
137 if (!transformed && (attr->type_mask & android::ResTable_map::TYPE_STRING)) {
138 StringBuilder string_builder;
139 string_builder.AppendText(*raw_string->value);
140 if (string_builder) {
141 transformed =
142 util::make_unique<String>(string_pool_->MakeRef(string_builder.to_string()));
143 }
144 }
145
146 if (transformed) {
147 return transformed;
148 }
149 }
150 return value;
151 }
152
153 const CallSite& callsite_;
154 IAaptContext* context_;
155 SymbolTable* symbols_;
156 xml::IPackageDeclStack* package_decls_;
157 StringPool* string_pool_;
158 bool error_ = false;
159 };
160
161 class EmptyDeclStack : public xml::IPackageDeclStack {
162 public:
163 EmptyDeclStack() = default;
164
TransformPackageAlias(const StringPiece & alias) const165 Maybe<xml::ExtractedPackage> TransformPackageAlias(const StringPiece& alias) const override {
166 if (alias.empty()) {
167 return xml::ExtractedPackage{{}, true /*private*/};
168 }
169 return {};
170 }
171
172 private:
173 DISALLOW_COPY_AND_ASSIGN(EmptyDeclStack);
174 };
175
176 // The symbol is visible if it is public, or if the reference to it is requesting private access
177 // or if the callsite comes from the same package.
IsSymbolVisible(const SymbolTable::Symbol & symbol,const Reference & ref,const CallSite & callsite)178 bool IsSymbolVisible(const SymbolTable::Symbol& symbol, const Reference& ref,
179 const CallSite& callsite) {
180 if (symbol.is_public || ref.private_reference) {
181 return true;
182 }
183
184 if (ref.name) {
185 const ResourceName& name = ref.name.value();
186 if (name.package.empty()) {
187 // If the symbol was found, and the package is empty, that means it was found in the local
188 // scope, which is always visible (private local).
189 return true;
190 }
191
192 // The symbol is visible if the reference is local to the same package it is defined in.
193 return callsite.package == name.package;
194 }
195
196 if (ref.id && symbol.id) {
197 return ref.id.value().package_id() == symbol.id.value().package_id();
198 }
199 return false;
200 }
201
202 } // namespace
203
ResolveSymbol(const Reference & reference,const CallSite & callsite,SymbolTable * symbols)204 const SymbolTable::Symbol* ReferenceLinker::ResolveSymbol(const Reference& reference,
205 const CallSite& callsite,
206 SymbolTable* symbols) {
207 if (reference.name) {
208 const ResourceName& name = reference.name.value();
209 if (name.package.empty()) {
210 // Use the callsite's package name if no package name was defined.
211 return symbols->FindByName(ResourceName(callsite.package, name.type, name.entry));
212 }
213 return symbols->FindByName(name);
214 } else if (reference.id) {
215 return symbols->FindById(reference.id.value());
216 } else {
217 return nullptr;
218 }
219 }
220
ResolveSymbolCheckVisibility(const Reference & reference,const CallSite & callsite,SymbolTable * symbols,std::string * out_error)221 const SymbolTable::Symbol* ReferenceLinker::ResolveSymbolCheckVisibility(const Reference& reference,
222 const CallSite& callsite,
223 SymbolTable* symbols,
224 std::string* out_error) {
225 const SymbolTable::Symbol* symbol = ResolveSymbol(reference, callsite, symbols);
226 if (!symbol) {
227 if (out_error) *out_error = "not found";
228 return nullptr;
229 }
230
231 if (!IsSymbolVisible(*symbol, reference, callsite)) {
232 if (out_error) *out_error = "is private";
233 return nullptr;
234 }
235 return symbol;
236 }
237
ResolveAttributeCheckVisibility(const Reference & reference,const CallSite & callsite,SymbolTable * symbols,std::string * out_error)238 const SymbolTable::Symbol* ReferenceLinker::ResolveAttributeCheckVisibility(
239 const Reference& reference, const CallSite& callsite, SymbolTable* symbols,
240 std::string* out_error) {
241 const SymbolTable::Symbol* symbol =
242 ResolveSymbolCheckVisibility(reference, callsite, symbols, out_error);
243 if (!symbol) {
244 return nullptr;
245 }
246
247 if (!symbol->attribute) {
248 if (out_error) *out_error = "is not an attribute";
249 return nullptr;
250 }
251 return symbol;
252 }
253
CompileXmlAttribute(const Reference & reference,const CallSite & callsite,SymbolTable * symbols,std::string * out_error)254 Maybe<xml::AaptAttribute> ReferenceLinker::CompileXmlAttribute(const Reference& reference,
255 const CallSite& callsite,
256 SymbolTable* symbols,
257 std::string* out_error) {
258 const SymbolTable::Symbol* symbol =
259 ResolveAttributeCheckVisibility(reference, callsite, symbols, out_error);
260 if (!symbol) {
261 return {};
262 }
263
264 if (!symbol->attribute) {
265 if (out_error) *out_error = "is not an attribute";
266 return {};
267 }
268 return xml::AaptAttribute(*symbol->attribute, symbol->id);
269 }
270
WriteResourceName(const Reference & ref,const CallSite & callsite,const xml::IPackageDeclStack * decls,DiagMessage * out_msg)271 void ReferenceLinker::WriteResourceName(const Reference& ref, const CallSite& callsite,
272 const xml::IPackageDeclStack* decls, DiagMessage* out_msg) {
273 CHECK(out_msg != nullptr);
274 if (!ref.name) {
275 *out_msg << ref.id.value();
276 return;
277 }
278
279 *out_msg << ref.name.value();
280
281 Reference fully_qualified = ref;
282 xml::ResolvePackage(decls, &fully_qualified);
283
284 ResourceName& full_name = fully_qualified.name.value();
285 if (full_name.package.empty()) {
286 full_name.package = callsite.package;
287 }
288
289 if (full_name != ref.name.value()) {
290 *out_msg << " (aka " << full_name << ")";
291 }
292 }
293
WriteAttributeName(const Reference & ref,const CallSite & callsite,const xml::IPackageDeclStack * decls,DiagMessage * out_msg)294 void ReferenceLinker::WriteAttributeName(const Reference& ref, const CallSite& callsite,
295 const xml::IPackageDeclStack* decls,
296 DiagMessage* out_msg) {
297 CHECK(out_msg != nullptr);
298 if (!ref.name) {
299 *out_msg << ref.id.value();
300 return;
301 }
302
303 const ResourceName& ref_name = ref.name.value();
304 CHECK_EQ(ref_name.type, ResourceType::kAttr);
305
306 if (!ref_name.package.empty()) {
307 *out_msg << ref_name.package << ":";
308 }
309 *out_msg << ref_name.entry;
310
311 Reference fully_qualified = ref;
312 xml::ResolvePackage(decls, &fully_qualified);
313
314 ResourceName& full_name = fully_qualified.name.value();
315 if (full_name.package.empty()) {
316 full_name.package = callsite.package;
317 }
318
319 if (full_name != ref.name.value()) {
320 *out_msg << " (aka " << full_name.package << ":" << full_name.entry << ")";
321 }
322 }
323
LinkReference(const CallSite & callsite,Reference * reference,IAaptContext * context,SymbolTable * symbols,const xml::IPackageDeclStack * decls)324 bool ReferenceLinker::LinkReference(const CallSite& callsite, Reference* reference,
325 IAaptContext* context, SymbolTable* symbols,
326 const xml::IPackageDeclStack* decls) {
327 CHECK(reference != nullptr);
328 if (!reference->name && !reference->id) {
329 // This is @null.
330 return true;
331 }
332
333 Reference transformed_reference = *reference;
334 xml::ResolvePackage(decls, &transformed_reference);
335
336 std::string err_str;
337 const SymbolTable::Symbol* s =
338 ResolveSymbolCheckVisibility(transformed_reference, callsite, symbols, &err_str);
339 if (s) {
340 // The ID may not exist. This is fine because of the possibility of building
341 // against libraries without assigned IDs.
342 // Ex: Linking against own resources when building a static library.
343 reference->id = s->id;
344 reference->is_dynamic = s->is_dynamic;
345 return true;
346 }
347
348 DiagMessage error_msg(reference->GetSource());
349 error_msg << "resource ";
350 WriteResourceName(*reference, callsite, decls, &error_msg);
351 error_msg << " " << err_str;
352 context->GetDiagnostics()->Error(error_msg);
353 return false;
354 }
355
Consume(IAaptContext * context,ResourceTable * table)356 bool ReferenceLinker::Consume(IAaptContext* context, ResourceTable* table) {
357 TRACE_NAME("ReferenceLinker::Consume");
358 EmptyDeclStack decl_stack;
359 bool error = false;
360 for (auto& package : table->packages) {
361 // Since we're linking, each package must have a name.
362 CHECK(!package->name.empty()) << "all packages being linked must have a name";
363
364 for (auto& type : package->types) {
365 for (auto& entry : type->entries) {
366 // First, unmangle the name if necessary.
367 ResourceName name(package->name, type->type, entry->name);
368 NameMangler::Unmangle(&name.entry, &name.package);
369
370 // Symbol state information may be lost if there is no value for the resource.
371 if (entry->visibility.level != Visibility::Level::kUndefined && entry->values.empty()) {
372 context->GetDiagnostics()->Error(DiagMessage(entry->visibility.source)
373 << "no definition for declared symbol '" << name
374 << "'");
375 error = true;
376 }
377
378 // Ensure that definitions for values declared as overlayable exist
379 if (entry->overlayable_item && entry->values.empty()) {
380 context->GetDiagnostics()->Error(DiagMessage(entry->overlayable_item.value().source)
381 << "no definition for overlayable symbol '"
382 << name << "'");
383 error = true;
384 }
385
386 // The context of this resource is the package in which it is defined.
387 const CallSite callsite{name.package};
388 ReferenceLinkerVisitor visitor(callsite, context, context->GetExternalSymbols(),
389 &table->string_pool, &decl_stack);
390
391 for (auto& config_value : entry->values) {
392 config_value->value->Accept(&visitor);
393 }
394
395 if (visitor.HasError()) {
396 error = true;
397 }
398 }
399 }
400 }
401 return !error;
402 }
403
404 } // namespace aapt
405