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