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