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