HAL  v4.5.0-130-g83e8bfb3f
The Hardware Analyzer - a comprehensive reverse engineering and manipulation framework for gate-level netlists.
state_transition_graph.cpp
Go to the documentation of this file.
1 // MIT License
2 //
3 // Copyright (c) 2019 Ruhr University Bochum, Chair for Embedded Security. All Rights reserved.
4 // Copyright (c) 2019 Marc Fyrbiak, Sebastian Wallat, Max Hoffmann ("ORIGINAL AUTHORS"). All rights reserved.
5 // Copyright (c) 2021 Max Planck Institute for Security and Privacy. All Rights reserved.
6 // Copyright (c) 2021 Jörn Langheinrich, Julian Speith, Nils Albartus, René Walendy, Simon Klix ("ORIGINAL AUTHORS"). All Rights reserved.
7 //
8 // Permission is hereby granted, free of charge, to any person obtaining a copy
9 // of this software and associated documentation files (the "Software"), to deal
10 // in the Software without restriction, including without limitation the rights
11 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 // copies of the Software, and to permit persons to whom the Software is
13 // furnished to do so, subject to the following conditions:
14 //
15 // The above copyright notice and this permission notice shall be included in all
16 // copies or substantial portions of the Software.
17 //
18 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24 // SOFTWARE.
25 
27 
29 #include "hal_core/netlist/gate.h"
30 #include "hal_core/netlist/net.h"
32 
33 #include <bitset>
34 #include <fstream>
35 #include <set>
36 #include <sstream>
37 
38 namespace hal
39 {
40  namespace solve_fsm
41  {
42  namespace
43  {
47  std::string format_value(const u64 value, const u32 size, const u32 base)
48  {
49  if (base == 2)
50  {
51  return std::bitset<64>(value).to_string().substr(64 - size, 64);
52  }
53  return std::to_string(value);
54  }
55 
60  std::string format_function(const BooleanFunction& bf, const u32 base)
61  {
62  if (bf.is_constant())
63  {
64  if (const auto res = bf.get_constant_value_u64(); res.is_ok())
65  {
66  return format_value(res.get(), bf.size(), base);
67  }
68  }
69  return bf.to_string();
70  }
71 
75  template<typename T>
76  std::string format_element(const T* element)
77  {
78  return "'" + element->get_name() + "' with ID " + std::to_string(element->get_id());
79  }
80  } // namespace
81 
83  {
84  return state_register.size();
85  }
86 
87  Result<std::string> StateTransitionGraph::generate_dot_graph(const std::filesystem::path& graph_path, const u32 max_condition_length, const u32 base) const
88  {
89  std::string graph_str = "digraph {\ncomment=\"created by HAL plugin solve_fsm\"\n";
90 
91  const auto format_state = [this, base](const u64 state) -> std::string {
92  if (base == 2)
93  {
94  return std::bitset<64>(state).to_string().substr(64 - this->get_state_size(), 64);
95  }
96  return std::to_string(state);
97  };
98 
99  if (base != 2 && base != 10)
100  {
101  return ERR("failed to generate DOT graph: base " + std::to_string(base) + "not implemented.");
102  }
103 
104  // states only carry an explicit node statement if there is something to annotate them with
105  if (!outputs.empty())
106  {
107  std::set<u64> states;
108  for (const auto& [org, successors] : transitions)
109  {
110  states.insert(org);
111  for (const auto& [suc, _] : successors)
112  {
113  states.insert(suc);
114  }
115  }
116 
117  for (const auto& state : states)
118  {
119  std::string label = format_state(state);
120 
121  if (const auto it = outputs.find(state); it != outputs.end())
122  {
123  for (const auto& [name, bf] : it->second)
124  {
125  // an output that only depends on the state has a constant value, one that also depends on
126  // the inputs of the FSM is printed as the function that it is
127  std::string value;
128  if (bf.is_constant())
129  {
130  if (const auto val_res = bf.get_constant_value_u64(); val_res.is_ok())
131  {
132  value = (base == 2) ? std::bitset<64>(val_res.get()).to_string().substr(64 - bf.size(), 64) : std::to_string(val_res.get());
133  }
134  }
135  if (value.empty())
136  {
137  value = bf.to_string().substr(0, max_condition_length);
138  }
139 
140  label += "\\n" + name + " = " + value;
141  }
142  }
143 
144  graph_str += format_state(state) + " [label=\"" + label + "\"];\n";
145  }
146  }
147 
148  for (const auto& [org, successors] : transitions)
149  {
150  for (const auto& [suc, cond] : successors)
151  {
152  const std::string start_name = format_state(org);
153  const std::string end_name = format_state(suc);
154 
155  graph_str +=
156  start_name + " -> " + end_name + "[label=\"" + cond.to_string().substr(0, max_condition_length) + "\", weight=\"" + cond.to_string().substr(0, max_condition_length) + "\"];\n";
157  ;
158  }
159  }
160 
161  graph_str += "}";
162 
163  // write to file
164  if (!graph_path.empty())
165  {
166  std::ofstream ofs(graph_path);
167  if (!ofs.is_open())
168  {
169  return ERR("failed to generate DOT graph: could not open file '" + graph_path.string() + "' for writing.");
170  }
171  ofs << graph_str;
172  ofs.close();
173 
174  }
175 
176  return OK(graph_str);
177  }
179  {
180  if (base != 2 && base != 10)
181  {
182  return ERR("failed to print state transition graph: base " + std::to_string(base) + " is not implemented.");
183  }
184 
185  const u32 state_size = get_state_size();
186  std::stringstream ss;
187 
188  ss << "FSM with " << state_size << " state bits and " << transitions.size() << " reachable states" << std::endl << std::endl;
189 
190  // the legend maps a state and its outputs back to the netlist elements they are made of
191  ss << "state register (least significant bit first):" << std::endl;
192  for (u32 i = 0; i < state_register.size(); i++)
193  {
194  ss << " bit " << i << ": " << format_element(state_register.at(i)) << std::endl;
195  }
196  ss << std::endl;
197 
198  if (!output_nets.empty())
199  {
200  ss << "outputs (least significant bit first):" << std::endl;
201  for (const auto& [name, nets] : output_nets)
202  {
203  ss << " " << name << ":";
204  for (u32 i = 0; i < nets.size(); i++)
205  {
206  ss << (i == 0 ? " " : ", ") << format_element(nets.at(i));
207  }
208  ss << std::endl;
209  }
210  ss << std::endl;
211  }
212 
213  // the Boolean functions refer to nets by a variable derived from the net ID, which matches no name in the
214  // netlist, so every variable that appears anywhere below is resolved here
215  std::set<std::string> variables;
216  for (const auto& [state, successors] : transitions)
217  {
218  for (const auto& [successor, condition] : successors)
219  {
220  const auto vars = condition.get_variable_names();
221  variables.insert(vars.begin(), vars.end());
222  }
223  }
224  for (const auto& [state, state_outputs] : outputs)
225  {
226  for (const auto& [name, bf] : state_outputs)
227  {
228  const auto vars = bf.get_variable_names();
229  variables.insert(vars.begin(), vars.end());
230  }
231  }
232 
233  if (!variables.empty())
234  {
235  ss << "nets referenced in the Boolean functions below:" << std::endl;
236  for (const auto& variable : variables)
237  {
238  ss << " " << variable << ": ";
239  if (netlist == nullptr)
240  {
241  ss << "unknown, the netlist is not available" << std::endl;
242  continue;
243  }
244 
245  if (const auto net_res = BooleanFunctionNetDecorator::get_net_from(netlist, variable); net_res.is_ok())
246  {
247  ss << "'" << net_res.get()->get_name() << "' with ID " << net_res.get()->get_id() << std::endl;
248  }
249  else
250  {
251  ss << "not a net of this netlist" << std::endl;
252  }
253  }
254  ss << std::endl;
255  }
256 
257  for (const auto& [state, successors] : transitions)
258  {
259  ss << "state " << format_value(state, state_size, base) << std::endl;
260 
261  if (const auto it = outputs.find(state); it != outputs.end() && !it->second.empty())
262  {
263  ss << " outputs:" << std::endl;
264  for (const auto& [name, bf] : it->second)
265  {
266  ss << " " << name << " = " << format_function(bf, base) << std::endl;
267  }
268  }
269 
270  ss << " transitions:" << std::endl;
271  if (successors.empty())
272  {
273  ss << " none" << std::endl;
274  }
275  for (const auto& [successor, condition] : successors)
276  {
277  ss << " to " << format_value(successor, state_size, base) << " if " << condition.to_string() << std::endl;
278  }
279 
280  ss << std::endl;
281  }
282 
283  return OK(ss.str());
284  }
285 
286  Result<std::monostate> StateTransitionGraph::write_txt(const std::filesystem::path& file_path, const u32 base) const
287  {
288  auto res = to_string(base);
289  if (res.is_error())
290  {
291  return ERR_APPEND(res.get_error(), "failed to write state transition graph to '" + file_path.string() + "'.");
292  }
293 
294  std::ofstream ofs(file_path);
295  if (!ofs.is_open())
296  {
297  return ERR("failed to write state transition graph: could not open file '" + file_path.string() + "' for writing.");
298  }
299  ofs << res.get();
300  ofs.close();
301 
302  return OK({});
303  }
304  } // namespace solve_fsm
305 } // namespace hal
u32 size
static Result< Net * > get_net_from(const Netlist *netlist, const BooleanFunction &var)
uint64_t u64
Definition: defines.h:42
uint32_t u32
Definition: defines.h:41
#define ERR(message)
Definition: result.h:60
#define OK(...)
Definition: result.h:56
#define ERR_APPEND(prev_error, message)
Definition: result.h:64
Result< StateTransitionGraph > solve_fsm(const Configuration &config)
Recover the state transition graph of an FSM from the netlist that implements it.
Definition: solve_fsm.cpp:622
Definition: defines.h:45
std::string name
This file contains the struct that holds the state transition graph of an FSM.
std::vector< std::pair< std::string, std::vector< Net * > > > output_nets
The outputs of the FSM, each given as a name and the nets that make up that output.
Result< std::string > generate_dot_graph(const std::filesystem::path &graph_path="", const u32 max_condition_length=128, const u32 base=10) const
Render the state transition graph in the DOT format.
std::map< u64, std::map< u64, BooleanFunction > > transitions
A map from each state to its successor states, together with the condition under which the respective...
u32 get_state_size() const
Get the number of flip-flops that make up the state register, i.e., the bit-size of a state.
std::vector< Gate * > state_register
The flip-flops that make up the state register, in the order that determines the encoding of a state.
Netlist * netlist
The netlist that implements the FSM.
Result< std::string > to_string(const u32 base=10) const
Render the state transition graph as human-readable text, without truncating anything.
std::map< u64, std::vector< std::pair< std::string, BooleanFunction > > > outputs
A map from each state to the value of every output of the FSM in that state.
Result< std::monostate > write_txt(const std::filesystem::path &file_path, const u32 base=10) const
Write the state transition graph to a text file, without truncating anything.