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