xref: /aosp_15_r20/external/google-breakpad/src/processor/cfi_frame_info.cc (revision 9712c20fc9bbfbac4935993a2ca0b3958c5adad2)
1 // Copyright 2010 Google LLC
2 //
3 // Redistribution and use in source and binary forms, with or without
4 // modification, are permitted provided that the following conditions are
5 // met:
6 //
7 //     * Redistributions of source code must retain the above copyright
8 // notice, this list of conditions and the following disclaimer.
9 //     * Redistributions in binary form must reproduce the above
10 // copyright notice, this list of conditions and the following disclaimer
11 // in the documentation and/or other materials provided with the
12 // distribution.
13 //     * Neither the name of Google LLC nor the names of its
14 // contributors may be used to endorse or promote products derived from
15 // this software without specific prior written permission.
16 //
17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 
29 // Original author: Jim Blandy <[email protected]> <[email protected]>
30 
31 // cfi_frame_info.cc: Implementation of CFIFrameInfo class.
32 // See cfi_frame_info.h for details.
33 
34 #ifdef HAVE_CONFIG_H
35 #include <config.h>  // Must come first
36 #endif
37 
38 #include "processor/cfi_frame_info.h"
39 
40 #include <string.h>
41 
42 #include <sstream>
43 
44 #include "common/scoped_ptr.h"
45 #include "processor/postfix_evaluator-inl.h"
46 
47 namespace google_breakpad {
48 
49 #ifdef _MSC_VER
50 #define strtok_r strtok_s
51 #endif
52 
53 template<typename V>
FindCallerRegs(const RegisterValueMap<V> & registers,const MemoryRegion & memory,RegisterValueMap<V> * caller_registers) const54 bool CFIFrameInfo::FindCallerRegs(const RegisterValueMap<V>& registers,
55                                   const MemoryRegion& memory,
56                                   RegisterValueMap<V>* caller_registers) const {
57   // If there are not rules for both .ra and .cfa in effect at this address,
58   // don't use this CFI data for stack walking.
59   if (cfa_rule_.empty() || ra_rule_.empty())
60     return false;
61 
62   RegisterValueMap<V> working;
63   PostfixEvaluator<V> evaluator(&working, &memory);
64 
65   caller_registers->clear();
66 
67   // First, compute the CFA.
68   V cfa;
69   working = registers;
70   if (!evaluator.EvaluateForValue(cfa_rule_, &cfa))
71     return false;
72 
73   // Then, compute the return address.
74   V ra;
75   working = registers;
76   working[".cfa"] = cfa;
77   if (!evaluator.EvaluateForValue(ra_rule_, &ra))
78     return false;
79 
80   // Now, compute values for all the registers register_rules_ mentions.
81   for (RuleMap::const_iterator it = register_rules_.begin();
82        it != register_rules_.end(); it++) {
83     V value;
84     working = registers;
85     working[".cfa"] = cfa;
86     if (!evaluator.EvaluateForValue(it->second, &value))
87       continue;
88     (*caller_registers)[it->first] = value;
89   }
90 
91   (*caller_registers)[".ra"] = ra;
92   (*caller_registers)[".cfa"] = cfa;
93 
94   return true;
95 }
96 
97 // Explicit instantiations for 32-bit and 64-bit architectures.
98 template bool CFIFrameInfo::FindCallerRegs<uint32_t>(
99     const RegisterValueMap<uint32_t>& registers,
100     const MemoryRegion& memory,
101     RegisterValueMap<uint32_t>* caller_registers) const;
102 template bool CFIFrameInfo::FindCallerRegs<uint64_t>(
103     const RegisterValueMap<uint64_t>& registers,
104     const MemoryRegion& memory,
105     RegisterValueMap<uint64_t>* caller_registers) const;
106 
Serialize() const107 string CFIFrameInfo::Serialize() const {
108   std::ostringstream stream;
109 
110   if (!cfa_rule_.empty()) {
111     stream << ".cfa: " << cfa_rule_;
112   }
113   if (!ra_rule_.empty()) {
114     if (static_cast<std::streamoff>(stream.tellp()) != 0)
115       stream << " ";
116     stream << ".ra: " << ra_rule_;
117   }
118   for (RuleMap::const_iterator iter = register_rules_.begin();
119        iter != register_rules_.end();
120        ++iter) {
121     if (static_cast<std::streamoff>(stream.tellp()) != 0)
122       stream << " ";
123     stream << iter->first << ": " << iter->second;
124   }
125 
126   return stream.str();
127 }
128 
Parse(const string & rule_set)129 bool CFIRuleParser::Parse(const string& rule_set) {
130   size_t rule_set_len = rule_set.size();
131   scoped_array<char> working_copy(new char[rule_set_len + 1]);
132   memcpy(working_copy.get(), rule_set.data(), rule_set_len);
133   working_copy[rule_set_len] = '\0';
134 
135   name_.clear();
136   expression_.clear();
137 
138   char* cursor;
139   static const char token_breaks[] = " \t\r\n";
140   char* token = strtok_r(working_copy.get(), token_breaks, &cursor);
141 
142   for (;;) {
143     // End of rule set?
144     if (!token) return Report();
145 
146     // Register/pseudoregister name?
147     size_t token_len = strlen(token);
148     if (token_len >= 1 && token[token_len - 1] == ':') {
149       // Names can't be empty.
150       if (token_len < 2) return false;
151       // If there is any pending content, report it.
152       if (!name_.empty() || !expression_.empty()) {
153         if (!Report()) return false;
154       }
155       name_.assign(token, token_len - 1);
156       expression_.clear();
157     } else {
158       // Another expression component.
159       assert(token_len > 0); // strtok_r guarantees this, I think.
160       if (!expression_.empty())
161         expression_ += ' ';
162       expression_ += token;
163     }
164     token = strtok_r(NULL, token_breaks, &cursor);
165   }
166 }
167 
Report()168 bool CFIRuleParser::Report() {
169   if (name_.empty() || expression_.empty()) return false;
170   if (name_ == ".cfa") handler_->CFARule(expression_);
171   else if (name_ == ".ra") handler_->RARule(expression_);
172   else handler_->RegisterRule(name_, expression_);
173   return true;
174 }
175 
CFARule(const string & expression)176 void CFIFrameInfoParseHandler::CFARule(const string& expression) {
177   frame_info_->SetCFARule(expression);
178 }
179 
RARule(const string & expression)180 void CFIFrameInfoParseHandler::RARule(const string& expression) {
181   frame_info_->SetRARule(expression);
182 }
183 
RegisterRule(const string & name,const string & expression)184 void CFIFrameInfoParseHandler::RegisterRule(const string& name,
185                                             const string& expression) {
186   frame_info_->SetRegisterRule(name, expression);
187 }
188 
189 } // namespace google_breakpad
190