• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "android-base/stringprintf.h"
21 #include "androidfw/ResourceTypes.h"
22 
23 #include "Diagnostics.h"
24 #include "ResourceParser.h"
25 #include "ResourceTable.h"
26 #include "ResourceUtils.h"
27 #include "ResourceValues.h"
28 #include "ValueVisitor.h"
29 #include "link/Linkers.h"
30 #include "process/IResourceTableConsumer.h"
31 #include "process/SymbolTable.h"
32 #include "trace/TraceBuffer.h"
33 #include "util/Util.h"
34 #include "xml/XmlUtil.h"
35 
36 using ::aapt::ResourceUtils::StringBuilder;
37 using ::android::StringPiece;
38 using ::android::base::StringPrintf;
39 
40 namespace aapt {
41 namespace {
42 struct LoggingResourceName {
LoggingResourceNameaapt::__anon7be2ac4e0111::LoggingResourceName43   LoggingResourceName(const Reference& ref, const CallSite& callsite,
44                       const xml::IPackageDeclStack* decls)
45       : ref_(ref), callsite_(callsite), decls_(decls) {
46   }
47 
48   const Reference& ref_;
49   const CallSite& callsite_;
50   const xml::IPackageDeclStack* decls_;
51 };
52 
operator <<(::std::ostream & out,const LoggingResourceName & name)53 inline ::std::ostream& operator<<(::std::ostream& out, const LoggingResourceName& name) {
54   if (!name.ref_.name) {
55     out << name.ref_.id.value();
56     return out;
57   }
58 
59   out << name.ref_.name.value();
60 
61   Reference fully_qualified = name.ref_;
62   xml::ResolvePackage(name.decls_, &fully_qualified);
63 
64   ResourceName& full_name = fully_qualified.name.value();
65   if (full_name.package.empty()) {
66     full_name.package = name.callsite_.package;
67   }
68 
69   if (full_name != name.ref_.name.value()) {
70     out << " (aka " << full_name << ")";
71   }
72   return out;
73 }
74 
75 }  // namespace
76 
TransformDerived(const Reference * value)77 std::unique_ptr<Reference> ReferenceLinkerTransformer::TransformDerived(const Reference* value) {
78   auto linked_item =
79       ReferenceLinker::LinkReference(callsite_, *value, context_, symbols_, table_, package_decls_);
80   if (linked_item) {
81     auto linked_item_ptr = linked_item.release();
82     if (auto ref = ValueCast<Reference>(linked_item_ptr)) {
83       return std::unique_ptr<Reference>(ref);
84     }
85     context_->GetDiagnostics()->Error(DiagMessage(value->GetSource())
86                                       << "value of '"
87                                       << LoggingResourceName(*value, callsite_, package_decls_)
88                                       << "' must be a resource reference");
89     delete linked_item_ptr;
90   }
91 
92   error_ = true;
93   return CloningValueTransformer::TransformDerived(value);
94 }
95 
TransformDerived(const Style * style)96 std::unique_ptr<Style> ReferenceLinkerTransformer::TransformDerived(const Style* style) {
97   // We visit the Style specially because during this phase, values of attributes are either
98   // RawString or Reference values. Now that we are expected to resolve all symbols, we can lookup
99   // the attributes to find out which types are allowed for the attributes' values.
100   auto new_style = CloningValueTransformer::TransformDerived(style);
101   if (new_style->parent) {
102     new_style->parent = *TransformDerived(&style->parent.value());
103   }
104 
105   for (Style::Entry& entry : new_style->entries) {
106     std::string err_str;
107 
108     // Transform the attribute reference so that it is using the fully qualified package
109     // name. This will also mark the reference as being able to see private resources if
110     // there was a '*' in the reference or if the package came from the private namespace.
111     Reference transformed_reference = entry.key;
112     ResolvePackage(package_decls_, &transformed_reference);
113 
114     // Find the attribute in the symbol table and check if it is visible from this callsite.
115     const SymbolTable::Symbol* symbol = ReferenceLinker::ResolveAttributeCheckVisibility(
116         transformed_reference, callsite_, context_, symbols_, &err_str);
117     if (symbol) {
118       // Assign our style key the correct ID. The ID may not exist.
119       entry.key.id = symbol->id;
120 
121       // Link/resolve the final value if it's a reference.
122       entry.value = entry.value->Transform(*this);
123 
124       // Try to convert the value to a more specific, typed value based on the attribute it is
125       // set to.
126       entry.value = ParseValueWithAttribute(std::move(entry.value), symbol->attribute.get());
127 
128       // Now verify that the type of this item is compatible with the
129       // attribute it is defined for. We pass `nullptr` as the DiagMessage so that this
130       // check is fast and we avoid creating a DiagMessage when the match is successful.
131       if (!symbol->attribute->Matches(*entry.value, nullptr)) {
132         // The actual type of this item is incompatible with the attribute.
133         DiagMessage msg(entry.key.GetSource());
134 
135         // Call the matches method again, this time with a DiagMessage so we fill in the actual
136         // error message.
137         symbol->attribute->Matches(*entry.value, &msg);
138         context_->GetDiagnostics()->Error(msg);
139         error_ = true;
140       }
141     } else {
142       context_->GetDiagnostics()->Error(DiagMessage(entry.key.GetSource())
143                                         << "style attribute '"
144                                         << LoggingResourceName(entry.key, callsite_, package_decls_)
145                                         << "' " << err_str);
146 
147       error_ = true;
148     }
149   }
150   return new_style;
151 }
152 
TransformItem(const Reference * value)153 std::unique_ptr<Item> ReferenceLinkerTransformer::TransformItem(const Reference* value) {
154   auto linked_value =
155       ReferenceLinker::LinkReference(callsite_, *value, context_, symbols_, table_, package_decls_);
156   if (linked_value) {
157     return linked_value;
158   }
159   error_ = true;
160   return CloningValueTransformer::TransformDerived(value);
161 }
162 
163 // Transform a RawString value into a more specific, appropriate value, based on the
164 // Attribute. If a non RawString value is passed in, this is an identity transform.
ParseValueWithAttribute(std::unique_ptr<Item> value,const Attribute * attr)165 std::unique_ptr<Item> ReferenceLinkerTransformer::ParseValueWithAttribute(
166     std::unique_ptr<Item> value, const Attribute* attr) {
167   if (RawString* raw_string = ValueCast<RawString>(value.get())) {
168     std::unique_ptr<Item> transformed =
169         ResourceUtils::TryParseItemForAttribute(*raw_string->value, attr);
170 
171     // If we could not parse as any specific type, try a basic STRING.
172     if (!transformed && (attr->type_mask & android::ResTable_map::TYPE_STRING)) {
173       StringBuilder string_builder;
174       string_builder.AppendText(*raw_string->value);
175       if (string_builder) {
176         transformed = util::make_unique<String>(pool_->MakeRef(string_builder.to_string()));
177       }
178     }
179 
180     if (transformed) {
181       return transformed;
182     }
183   }
184   return value;
185 }
186 
187 namespace {
188 
189 class EmptyDeclStack : public xml::IPackageDeclStack {
190  public:
191   EmptyDeclStack() = default;
192 
TransformPackageAlias(const StringPiece & alias) const193   std::optional<xml::ExtractedPackage> TransformPackageAlias(
194       const StringPiece& alias) const override {
195     if (alias.empty()) {
196       return xml::ExtractedPackage{{}, true /*private*/};
197     }
198     return {};
199   }
200 
201  private:
202   DISALLOW_COPY_AND_ASSIGN(EmptyDeclStack);
203 };
204 
205 struct MacroDeclStack : public xml::IPackageDeclStack {
MacroDeclStackaapt::__anon7be2ac4e0211::MacroDeclStack206   explicit MacroDeclStack(std::vector<Macro::Namespace> namespaces)
207       : alias_namespaces_(std::move(namespaces)) {
208   }
209 
TransformPackageAliasaapt::__anon7be2ac4e0211::MacroDeclStack210   std::optional<xml::ExtractedPackage> TransformPackageAlias(
211       const StringPiece& alias) const override {
212     if (alias.empty()) {
213       return xml::ExtractedPackage{{}, true /*private*/};
214     }
215     for (auto it = alias_namespaces_.rbegin(); it != alias_namespaces_.rend(); ++it) {
216       if (alias == StringPiece(it->alias)) {
217         return xml::ExtractedPackage{it->package_name, it->is_private};
218       }
219     }
220     return {};
221   }
222 
223  private:
224   std::vector<Macro::Namespace> alias_namespaces_;
225 };
226 
227 // The symbol is visible if it is public, or if the reference to it is requesting private access
228 // or if the callsite comes from the same package.
IsSymbolVisible(const SymbolTable::Symbol & symbol,const Reference & ref,const CallSite & callsite)229 bool IsSymbolVisible(const SymbolTable::Symbol& symbol, const Reference& ref,
230                      const CallSite& callsite) {
231   if (symbol.is_public || ref.private_reference) {
232     return true;
233   }
234 
235   if (ref.name) {
236     const ResourceName& name = ref.name.value();
237     if (name.package.empty()) {
238       // If the symbol was found, and the package is empty, that means it was found in the local
239       // scope, which is always visible (private local).
240       return true;
241     }
242 
243     // The symbol is visible if the reference is local to the same package it is defined in.
244     return callsite.package == name.package;
245   }
246 
247   if (ref.id && symbol.id) {
248     return ref.id.value().package_id() == symbol.id.value().package_id();
249   }
250   return false;
251 }
252 
253 }  // namespace
254 
ResolveSymbol(const Reference & reference,const CallSite & callsite,IAaptContext * context,SymbolTable * symbols)255 const SymbolTable::Symbol* ReferenceLinker::ResolveSymbol(const Reference& reference,
256                                                           const CallSite& callsite,
257                                                           IAaptContext* context,
258                                                           SymbolTable* symbols) {
259   if (reference.name) {
260     const ResourceName& name = reference.name.value();
261     if (name.package.empty()) {
262       // Use the callsite's package name if no package name was defined.
263       const SymbolTable::Symbol* symbol = symbols->FindByName(
264           ResourceName(callsite.package, name.type, name.entry));
265       if (symbol) {
266         return symbol;
267       }
268 
269       // If the callsite package is the same as the current compilation package,
270       // check the feature split dependencies as well. Feature split resources
271       // can be referenced without a namespace, just like the base package.
272       if (callsite.package == context->GetCompilationPackage()) {
273         const auto& split_name_dependencies = context->GetSplitNameDependencies();
274         for (const std::string& split_name : split_name_dependencies) {
275           std::string split_package =
276               StringPrintf("%s.%s", callsite.package.c_str(), split_name.c_str());
277           symbol = symbols->FindByName(ResourceName(split_package, name.type, name.entry));
278           if (symbol) {
279             return symbol;
280           }
281         }
282       }
283       return nullptr;
284     }
285     return symbols->FindByName(name);
286   } else if (reference.id) {
287     return symbols->FindById(reference.id.value());
288   } else {
289     return nullptr;
290   }
291 }
292 
ResolveSymbolCheckVisibility(const Reference & reference,const CallSite & callsite,IAaptContext * context,SymbolTable * symbols,std::string * out_error)293 const SymbolTable::Symbol* ReferenceLinker::ResolveSymbolCheckVisibility(const Reference& reference,
294                                                                          const CallSite& callsite,
295                                                                          IAaptContext* context,
296                                                                          SymbolTable* symbols,
297                                                                          std::string* out_error) {
298   const SymbolTable::Symbol* symbol = ResolveSymbol(reference, callsite, context, symbols);
299   if (!symbol) {
300     if (out_error) *out_error = "not found";
301     return nullptr;
302   }
303 
304   if (!IsSymbolVisible(*symbol, reference, callsite)) {
305     if (out_error) *out_error = "is private";
306     return nullptr;
307   }
308   return symbol;
309 }
310 
ResolveAttributeCheckVisibility(const Reference & reference,const CallSite & callsite,IAaptContext * context,SymbolTable * symbols,std::string * out_error)311 const SymbolTable::Symbol* ReferenceLinker::ResolveAttributeCheckVisibility(
312     const Reference& reference, const CallSite& callsite, IAaptContext* context,
313     SymbolTable* symbols, std::string* out_error) {
314   const SymbolTable::Symbol* symbol =
315       ResolveSymbolCheckVisibility(reference, callsite, context, symbols, out_error);
316   if (!symbol) {
317     return nullptr;
318   }
319 
320   if (!symbol->attribute) {
321     if (out_error) *out_error = "is not an attribute";
322     return nullptr;
323   }
324   return symbol;
325 }
326 
CompileXmlAttribute(const Reference & reference,const CallSite & callsite,IAaptContext * context,SymbolTable * symbols,std::string * out_error)327 std::optional<xml::AaptAttribute> ReferenceLinker::CompileXmlAttribute(const Reference& reference,
328                                                                        const CallSite& callsite,
329                                                                        IAaptContext* context,
330                                                                        SymbolTable* symbols,
331                                                                        std::string* out_error) {
332   const SymbolTable::Symbol* symbol =
333       ResolveAttributeCheckVisibility(reference, callsite, context, symbols, out_error);
334   if (!symbol) {
335     return {};
336   }
337 
338   if (!symbol->attribute) {
339     if (out_error) *out_error = "is not an attribute";
340     return {};
341   }
342   return xml::AaptAttribute(*symbol->attribute, symbol->id);
343 }
344 
WriteAttributeName(const Reference & ref,const CallSite & callsite,const xml::IPackageDeclStack * decls,DiagMessage * out_msg)345 void ReferenceLinker::WriteAttributeName(const Reference& ref, const CallSite& callsite,
346                                          const xml::IPackageDeclStack* decls,
347                                          DiagMessage* out_msg) {
348   CHECK(out_msg != nullptr);
349   if (!ref.name) {
350     *out_msg << ref.id.value();
351     return;
352   }
353 
354   const ResourceName& ref_name = ref.name.value();
355   CHECK_EQ(ref_name.type.type, ResourceType::kAttr);
356 
357   if (!ref_name.package.empty()) {
358     *out_msg << ref_name.package << ":";
359   }
360   *out_msg << ref_name.entry;
361 
362   Reference fully_qualified = ref;
363   xml::ResolvePackage(decls, &fully_qualified);
364 
365   ResourceName& full_name = fully_qualified.name.value();
366   if (full_name.package.empty()) {
367     full_name.package = callsite.package;
368   }
369 
370   if (full_name != ref.name.value()) {
371     *out_msg << " (aka " << full_name.package << ":" << full_name.entry << ")";
372   }
373 }
374 
LinkReference(const CallSite & callsite,const Reference & reference,IAaptContext * context,SymbolTable * symbols,ResourceTable * table,const xml::IPackageDeclStack * decls)375 std::unique_ptr<Item> ReferenceLinker::LinkReference(const CallSite& callsite,
376                                                      const Reference& reference,
377                                                      IAaptContext* context, SymbolTable* symbols,
378                                                      ResourceTable* table,
379                                                      const xml::IPackageDeclStack* decls) {
380   if (!reference.name && !reference.id) {
381     // This is @null.
382     return std::make_unique<Reference>(reference);
383   }
384 
385   Reference transformed_reference = reference;
386   xml::ResolvePackage(decls, &transformed_reference);
387 
388   if (transformed_reference.name.value().type.type == ResourceType::kMacro) {
389     if (transformed_reference.name.value().package.empty()) {
390       transformed_reference.name.value().package = callsite.package;
391     }
392 
393     auto result = table->FindResource(transformed_reference.name.value());
394     if (!result || result.value().entry->values.empty()) {
395       context->GetDiagnostics()->Error(
396           DiagMessage(reference.GetSource())
397           << "failed to find definition for "
398           << LoggingResourceName(transformed_reference, callsite, decls));
399       return {};
400     }
401 
402     auto& macro_values = result.value().entry->values;
403     CHECK(macro_values.size() == 1) << "Macros can only be defined in the default configuration.";
404 
405     auto macro = ValueCast<Macro>(macro_values[0]->value.get());
406     CHECK(macro != nullptr) << "Value of macro resource is not a Macro (actual "
407                             << *macro_values[0]->value << ")";
408 
409     // Re-create the state used to parse the macro tag to compile the macro contents as if it was
410     // defined inline
411     uint32_t type_flags = 0;
412     if (reference.type_flags.has_value()) {
413       type_flags = reference.type_flags.value();
414     }
415 
416     MacroDeclStack namespace_stack(macro->alias_namespaces);
417     FlattenedXmlSubTree sub_tree{.raw_value = macro->raw_value,
418                                  .style_string = macro->style_string,
419                                  .untranslatable_sections = macro->untranslatable_sections,
420                                  .namespace_resolver = &namespace_stack,
421                                  .source = macro->GetSource()};
422 
423     auto new_value = ResourceParser::ParseXml(sub_tree, type_flags, reference.allow_raw, *table,
424                                               macro_values[0]->config, *context->GetDiagnostics());
425     if (new_value == nullptr) {
426       context->GetDiagnostics()->Error(
427           DiagMessage(reference.GetSource())
428           << "failed to substitute macro "
429           << LoggingResourceName(transformed_reference, callsite, decls)
430           << ": failed to parse contents as one of type(s) " << Attribute::MaskString(type_flags));
431       return {};
432     }
433 
434     if (auto ref = ValueCast<Reference>(new_value.get())) {
435       return LinkReference(callsite, *ref, context, symbols, table, decls);
436     }
437     return new_value;
438   }
439 
440   std::string err_str;
441   const SymbolTable::Symbol* s =
442       ResolveSymbolCheckVisibility(transformed_reference, callsite, context, symbols, &err_str);
443   if (s) {
444     // The ID may not exist. This is fine because of the possibility of building
445     // against libraries without assigned IDs.
446     // Ex: Linking against own resources when building a static library.
447     auto new_ref = std::make_unique<Reference>(reference);
448     new_ref->id = s->id;
449     new_ref->is_dynamic = s->is_dynamic;
450     return std::move(new_ref);
451   }
452 
453   context->GetDiagnostics()->Error(DiagMessage(reference.GetSource())
454                                    << "resource "
455                                    << LoggingResourceName(transformed_reference, callsite, decls)
456                                    << " " << err_str);
457   return {};
458 }
459 
Consume(IAaptContext * context,ResourceTable * table)460 bool ReferenceLinker::Consume(IAaptContext* context, ResourceTable* table) {
461   TRACE_NAME("ReferenceLinker::Consume");
462   EmptyDeclStack decl_stack;
463   bool error = false;
464   for (auto& package : table->packages) {
465     // Since we're linking, each package must have a name.
466     CHECK(!package->name.empty()) << "all packages being linked must have a name";
467 
468     for (auto& type : package->types) {
469       for (auto& entry : type->entries) {
470         // First, unmangle the name if necessary.
471         ResourceName name(package->name, type->type, entry->name);
472         NameMangler::Unmangle(&name.entry, &name.package);
473 
474         // Symbol state information may be lost if there is no value for the resource.
475         if (entry->visibility.level != Visibility::Level::kUndefined && entry->values.empty()) {
476           context->GetDiagnostics()->Error(DiagMessage(entry->visibility.source)
477                                                << "no definition for declared symbol '" << name
478                                                << "'");
479           error = true;
480         }
481 
482         // Ensure that definitions for values declared as overlayable exist
483         if (entry->overlayable_item && entry->values.empty()) {
484           context->GetDiagnostics()->Error(DiagMessage(entry->overlayable_item.value().source)
485                                            << "no definition for overlayable symbol '"
486                                            << name << "'");
487           error = true;
488         }
489 
490         // The context of this resource is the package in which it is defined.
491         const CallSite callsite{name.package};
492         ReferenceLinkerTransformer reference_transformer(callsite, context,
493                                                          context->GetExternalSymbols(),
494                                                          &table->string_pool, table, &decl_stack);
495 
496         for (auto& config_value : entry->values) {
497           config_value->value = config_value->value->Transform(reference_transformer);
498         }
499 
500         if (reference_transformer.HasError()) {
501           error = true;
502         }
503       }
504     }
505   }
506   return !error;
507 }
508 
509 }  // namespace aapt
510