Skip to content

Commit

Permalink
[compiler/decompiler] Take the address of a variable (#554)
Browse files Browse the repository at this point in the history
* support taking the address of variables

* partially working stack variables

* implement type cast stuff

* remove final
  • Loading branch information
water111 authored Jun 4, 2021
1 parent 9b905f9 commit 542edfb
Show file tree
Hide file tree
Showing 38 changed files with 374 additions and 183 deletions.
3 changes: 3 additions & 0 deletions common/goos/Reader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,9 @@ Object Reader::read_list(TextStream& ts, bool expect_close_paren) {
while (kv != reader_macros.end()) {
// build a stack of reader macros to apply to this form.
reader_macro_string_stack.push_back(kv->second);
if (!ts.text_remains()) {
throw_reader_error(ts, "Something must follow a reader macro", 0);
}
tok = get_next_token(ts);
kv = reader_macros.find(tok.text);
}
Expand Down
21 changes: 16 additions & 5 deletions decompiler/IR2/AtomicOpForm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,15 +43,26 @@ FormElement* SetVarOp::get_as_form(FormPool& pool, const Env& env) const {
if (env.has_type_analysis() && m_src.args() == 2 && m_src.get_arg(1).is_int() &&
m_src.get_arg(0).is_var() && m_src.kind() == SimpleExpression::Kind::ADD) {
if (m_src.get_arg(0).var().reg() == Register(Reg::GPR, Reg::SP)) {
// get a stack variable.
for (auto& var : env.stack_var_hints()) {
if (var.hint.stack_offset == m_src.get_arg(1).get_int()) {
// get a stack structure.
int offset = m_src.get_arg(1).get_int();
for (auto& structure : env.stack_structure_hints()) {
if (structure.hint.stack_offset == offset) {
// match!
return pool.alloc_element<SetVarElement>(
m_dst, pool.alloc_single_element_form<StackVarDefElement>(nullptr, var), true,
var.ref_type);
m_dst, pool.alloc_single_element_form<StackStructureDefElement>(nullptr, structure),
true, structure.ref_type);
}
}
// get a stack variable
auto& spill_map = env.stack_spills().map();
if (spill_map.find(offset) != spill_map.end()) {
return pool.alloc_element<SetVarElement>(
m_dst,
pool.alloc_single_element_form<GenericElement>(
nullptr, GenericOperator::make_fixed(FixedOperatorKind::ADDRESS_OF),
pool.alloc_single_element_form<StackSpillValueElement>(nullptr, -1, offset, false)),
true, env.stack_slot_entries.at(offset).typespec);
}
} else {
// access a field
auto arg0_type = env.get_types_before_op(m_my_idx).get(m_src.get_arg(0).var().reg());
Expand Down
28 changes: 20 additions & 8 deletions decompiler/IR2/AtomicOpTypeAnalysis.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -234,17 +234,21 @@ namespace {
*/
TP_Type get_stack_type_at_constant_offset(int offset,
const Env& env,
const DecompilerTypeSystem& dts) {
const DecompilerTypeSystem& dts,
const TypeState& types) {
(void)dts;
for (auto& var : env.stack_var_hints()) {
if (offset < var.hint.stack_offset || offset >= (var.hint.stack_offset + var.size)) {

// first look for a stack structure
for (auto& structure : env.stack_structure_hints()) {
if (offset < structure.hint.stack_offset ||
offset >= (structure.hint.stack_offset + structure.size)) {
continue; // reject, it isn't in this variable
}

if (offset == var.hint.stack_offset) {
if (offset == structure.hint.stack_offset) {
// special case just getting the variable
if (var.hint.container_type == StackVariableHint::ContainerType::NONE) {
return TP_Type::make_from_ts(coerce_to_reg_type(var.ref_type));
if (structure.hint.container_type == StackStructureHint::ContainerType::NONE) {
return TP_Type::make_from_ts(coerce_to_reg_type(structure.ref_type));
}
}

Expand All @@ -266,7 +270,15 @@ TP_Type get_stack_type_at_constant_offset(int offset,
*/
// if we fail, keep trying others. This lets us have overlays in stack memory.
}
throw std::runtime_error(fmt::format("Failed to find a stack variable at offset {}", offset));

// look for a stack variable
auto kv = types.spill_slots.find(offset);
if (kv != types.spill_slots.end()) {
return TP_Type::make_from_ts(TypeSpec("pointer", {kv->second.typespec()}));
}

throw std::runtime_error(
fmt::format("Failed to find a stack variable or structure at offset {}", offset));
}
} // namespace

Expand Down Expand Up @@ -373,7 +385,7 @@ TP_Type SimpleExpression::get_type_int2(const TypeState& input,
// get stack address:
if (m_args[0].is_var() && m_args[0].var().reg() == Register(Reg::GPR, Reg::SP) &&
m_args[1].is_int()) {
return get_stack_type_at_constant_offset(m_args[1].get_int(), env, dts);
return get_stack_type_at_constant_offset(m_args[1].get_int(), env, dts, input);
}

if (arg0_type.is_product_with(4) && tc(dts, TypeSpec("type"), arg1_type)) {
Expand Down
8 changes: 4 additions & 4 deletions decompiler/IR2/Env.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -499,16 +499,16 @@ void Env::disable_use(const RegisterAccess& access) {
* Set the stack hints. This must be done before type analysis.
* This actually parses the types, so it should be done after the dts is set up.
*/
void Env::set_stack_var_hints(const std::vector<StackVariableHint>& hints) {
void Env::set_stack_structure_hints(const std::vector<StackStructureHint>& hints) {
for (auto& hint : hints) {
StackVarEntry entry;
StackStructureEntry entry;
entry.hint = hint;
// parse the type spec.
TypeSpec base_typespec = dts->parse_type_spec(hint.element_type);
auto type_info = dts->ts.lookup_type(base_typespec);

switch (hint.container_type) {
case StackVariableHint::ContainerType::NONE:
case StackStructureHint::ContainerType::NONE:
// just a plain object on the stack.
if (!type_info->is_reference()) {
throw std::runtime_error(
Expand All @@ -531,7 +531,7 @@ void Env::set_stack_var_hints(const std::vector<StackVariableHint>& hints) {
assert(false);
}

m_stack_vars.push_back(entry);
m_stack_structures.push_back(entry);
}
}

Expand Down
26 changes: 18 additions & 8 deletions decompiler/IR2/Env.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ struct VariableWithCast {
std::optional<TypeSpec> cast;
};

struct StackVarEntry {
StackVariableHint hint;
struct StackStructureEntry {
StackStructureHint hint;
TypeSpec ref_type; // the actual type of the address.
int size = -1;
};
Expand Down Expand Up @@ -131,11 +131,18 @@ class Env {

bool allow_sloppy_pair_typing() const { return m_allow_sloppy_pair_typing; }
void set_sloppy_pair_typing() { m_allow_sloppy_pair_typing = true; }
void set_type_casts(const std::unordered_map<int, std::vector<TypeCast>>& casts) {
void set_type_casts(const std::unordered_map<int, std::vector<RegisterTypeCast>>& casts) {
m_typecasts = casts;
}
const std::unordered_map<int, std::vector<RegisterTypeCast>>& casts() const {
return m_typecasts;
}

void set_stack_casts(const std::unordered_map<int, StackTypeCast>& casts) {
m_stack_typecasts = casts;
}

const std::unordered_map<int, std::vector<TypeCast>>& casts() const { return m_typecasts; }
const std::unordered_map<int, StackTypeCast>& stack_casts() const { return m_stack_typecasts; }

void set_remap_for_function(int nargs);
void set_remap_for_method(int nargs);
Expand All @@ -158,8 +165,10 @@ class Env {
m_label_types = types;
}

void set_stack_var_hints(const std::vector<StackVariableHint>& hints);
const std::vector<StackVarEntry>& stack_var_hints() const { return m_stack_vars; }
void set_stack_structure_hints(const std::vector<StackStructureHint>& hints);
const std::vector<StackStructureEntry>& stack_structure_hints() const {
return m_stack_structures;
}

const UseDefInfo& get_use_def_info(const RegisterAccess& ra) const;
void disable_use(const RegisterAccess& access);
Expand Down Expand Up @@ -210,8 +219,9 @@ class Env {

bool m_allow_sloppy_pair_typing = false;

std::unordered_map<int, std::vector<TypeCast>> m_typecasts;
std::vector<StackVarEntry> m_stack_vars;
std::unordered_map<int, std::vector<RegisterTypeCast>> m_typecasts;
std::unordered_map<int, StackTypeCast> m_stack_typecasts;
std::vector<StackStructureEntry> m_stack_structures;
std::unordered_map<std::string, std::string> m_var_remap;
std::unordered_map<std::string, TypeSpec> m_var_retype;
std::unordered_map<std::string, LabelType> m_label_types;
Expand Down
17 changes: 10 additions & 7 deletions decompiler/IR2/Form.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1538,6 +1538,8 @@ std::string fixed_operator_to_string(FixedOperatorKind kind) {
return "make-u128";
case FixedOperatorKind::SYMBOL_TO_STRING:
return "symbol->string";
case FixedOperatorKind::ADDRESS_OF:
return "&";
default:
assert(false);
return "";
Expand Down Expand Up @@ -2265,27 +2267,28 @@ void LambdaDefinitionElement::get_modified_regs(RegSet&) const {}
// StackVarDefElement
/////////////////////////////

StackVarDefElement::StackVarDefElement(const StackVarEntry& entry) : m_entry(entry) {}
StackStructureDefElement::StackStructureDefElement(const StackStructureEntry& entry)
: m_entry(entry) {}

goos::Object StackVarDefElement::to_form_internal(const Env&) const {
goos::Object StackStructureDefElement::to_form_internal(const Env&) const {
switch (m_entry.hint.container_type) {
case StackVariableHint::ContainerType::NONE:
case StackStructureHint::ContainerType::NONE:
return pretty_print::build_list(
fmt::format("new 'stack-no-clear '{}", m_entry.ref_type.print()));
default:
assert(false);
}
}

void StackVarDefElement::apply_form(const std::function<void(Form*)>&) {}
void StackStructureDefElement::apply_form(const std::function<void(Form*)>&) {}

void StackVarDefElement::apply(const std::function<void(FormElement*)>& f) {
void StackStructureDefElement::apply(const std::function<void(FormElement*)>& f) {
f(this);
}

void StackVarDefElement::collect_vars(RegAccessSet&, bool) const {}
void StackStructureDefElement::collect_vars(RegAccessSet&, bool) const {}

void StackVarDefElement::get_modified_regs(RegSet&) const {}
void StackStructureDefElement::get_modified_regs(RegSet&) const {}

////////////////////////////////
// VectorFloatLoadStoreElement
Expand Down
6 changes: 3 additions & 3 deletions decompiler/IR2/Form.h
Original file line number Diff line number Diff line change
Expand Up @@ -1318,9 +1318,9 @@ class LambdaDefinitionElement : public FormElement {
goos::Object m_def;
};

class StackVarDefElement : public FormElement {
class StackStructureDefElement : public FormElement {
public:
StackVarDefElement(const StackVarEntry& entry);
StackStructureDefElement(const StackStructureEntry& entry);
goos::Object to_form_internal(const Env& env) const override;
void apply(const std::function<void(FormElement*)>& f) override;
void apply_form(const std::function<void(Form*)>& f) override;
Expand All @@ -1334,7 +1334,7 @@ class StackVarDefElement : public FormElement {
const TypeSpec& type() const { return m_entry.ref_type; }

private:
StackVarEntry m_entry;
StackStructureEntry m_entry;
};

class VectorFloatLoadStoreElement : public FormElement {
Expand Down
13 changes: 6 additions & 7 deletions decompiler/IR2/FormExpressionAnalysis.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1081,7 +1081,6 @@ void SimpleExpressionElement::update_from_stack_logor_or_logand(const Env& env,
// this is an ugly hack to make (logand (lognot (enum-bitfield xxxx)) work.
// I have only one example for this, so I think this unlikely to work in all cases.
if (m_expr.get_arg(1).is_var()) {
auto arg1_type = env.get_variable_type(m_expr.get_arg(1).var(), true);
auto eti = env.dts->ts.try_enum_lookup(arg1_type.base_type());
if (eti) {
auto integer = get_goal_integer_constant(args.at(0), env);
Expand Down Expand Up @@ -2025,7 +2024,7 @@ void FunctionCallElement::update_from_stack(const Env& env,
}

if (got_stack_new) {
auto new_op = first_cast->source()->try_as_element<StackVarDefElement>();
auto new_op = first_cast->source()->try_as_element<StackStructureDefElement>();
if (!new_op || new_op->type().base_type() != type_source_form->to_string(env)) {
got_stack_new = false;
}
Expand Down Expand Up @@ -3567,11 +3566,11 @@ void ConstantFloatElement::update_from_stack(const Env&,
result->push_back(this);
}

void StackVarDefElement::update_from_stack(const Env&,
FormPool&,
FormStack&,
std::vector<FormElement*>* result,
bool) {
void StackStructureDefElement::update_from_stack(const Env&,
FormPool&,
FormStack&,
std::vector<FormElement*>* result,
bool) {
mark_popped();
result->push_back(this);
}
Expand Down
1 change: 1 addition & 0 deletions decompiler/IR2/IR2_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ enum class FixedOperatorKind {
NONE,
PCPYLD,
SYMBOL_TO_STRING,
ADDRESS_OF,
INVALID
};

Expand Down
11 changes: 8 additions & 3 deletions decompiler/ObjectFile/ObjectFileDB_IR2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -360,10 +360,14 @@ void ObjectFileDB::ir2_type_analysis_pass(const Config& config) {
attempted_functions++;
// try type analysis here.
auto func_name = func.guessed_name.to_string();
auto casts = try_lookup(config.type_casts_by_function_by_atomic_op_idx, func_name);
auto register_casts =
try_lookup(config.register_type_casts_by_function_by_atomic_op_idx, func_name);
func.ir2.env.set_type_casts(register_casts);
auto label_types = try_lookup(config.label_types, data.to_unique_name());
func.ir2.env.set_type_casts(casts);
func.ir2.env.set_label_types(label_types);
auto stack_casts =
try_lookup(config.stack_type_casts_by_function_by_stack_offset, func_name);
func.ir2.env.set_stack_casts(stack_casts);
if (config.hacks.pair_functions_by_name.find(func_name) !=
config.hacks.pair_functions_by_name.end()) {
func.ir2.env.set_sloppy_pair_typing();
Expand All @@ -373,7 +377,8 @@ void ObjectFileDB::ir2_type_analysis_pass(const Config& config) {
config.hacks.reject_cond_to_value.end()) {
func.ir2.env.aggressively_reject_cond_to_value_rewrite = true;
}
func.ir2.env.set_stack_var_hints(try_lookup(config.stack_var_hints_by_function, func_name));
func.ir2.env.set_stack_structure_hints(
try_lookup(config.stack_structure_hints_by_function, func_name));
if (run_type_analysis_ir2(ts, dts, func)) {
successful_functions++;
func.ir2.env.types_succeeded = true;
Expand Down
1 change: 1 addition & 0 deletions decompiler/analysis/anonymous_function_def.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ int insert_anonymous_functions(Form* top_level_form,

f->clear();
f->push_back(pool.alloc_element<LambdaDefinitionElement>(result));
replaced++;
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion decompiler/analysis/insert_lets.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@ FormElement* fix_up_vector_inline_zero(LetElement* in, const Env& env, FormPool&
}

Form* src = in->entries().at(0).src;
auto src_as_stackvar = src->try_as_element<StackVarDefElement>();
auto src_as_stackvar = src->try_as_element<StackStructureDefElement>();
if (!src_as_stackvar) {
return nullptr;
}
Expand Down
24 changes: 17 additions & 7 deletions decompiler/analysis/type_analysis.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ TypeState construct_initial_typestate(const TypeSpec& f_ts, const Env& env) {
* Modify the the given type state based on the given casts.
*/
void modify_input_types_for_casts(
const std::vector<TypeCast>& casts,
const std::vector<RegisterTypeCast>& casts,
TypeState* state,
std::unordered_map<Register, TP_Type, Register::hash>* changed_types,
DecompilerTypeSystem& dts) {
Expand Down Expand Up @@ -56,7 +56,8 @@ void modify_input_types_for_casts(

void try_modify_input_types_for_casts(
int idx,
const std::unordered_map<int, std::vector<TypeCast>>& casts,
const std::unordered_map<int, std::vector<RegisterTypeCast>>& casts,
const std::unordered_map<int, StackTypeCast>& stack_casts,
TypeState* state,
std::unordered_map<Register, TP_Type, Register::hash>* changed_types,
DecompilerTypeSystem& dts) {
Expand All @@ -65,6 +66,15 @@ void try_modify_input_types_for_casts(
// fmt::print("at idx {}, casting:\n", idx);
modify_input_types_for_casts(kv->second, state, changed_types, dts);
}

for (const auto& [offset, cast] : stack_casts) {
auto stack_kv = state->spill_slots.find(offset);
if (stack_kv == state->spill_slots.end()) {
throw std::runtime_error(
fmt::format("Got a stack cast at offset {}, but didn't find a variable there.", offset));
}
state->spill_slots[offset] = TP_Type::make_from_ts(dts.parse_type_spec(cast.type_name));
}
}
} // namespace

Expand Down Expand Up @@ -106,8 +116,8 @@ bool run_type_analysis_ir2(const TypeSpec& my_type, DecompilerTypeSystem& dts, F
for (int op_id = aop->block_id_to_first_atomic_op.at(block_id);
op_id < aop->block_id_to_end_atomic_op.at(block_id); op_id++) {
std::unordered_map<Register, TP_Type, Register::hash> restore_cast_types;
try_modify_input_types_for_casts(op_id, func.ir2.env.casts(), init_types,
&restore_cast_types, dts);
try_modify_input_types_for_casts(op_id, func.ir2.env.casts(), func.ir2.env.stack_casts(),
init_types, &restore_cast_types, dts);

auto& op = aop->ops.at(op_id);

Expand Down Expand Up @@ -159,11 +169,11 @@ bool run_type_analysis_ir2(const TypeSpec& my_type, DecompilerTypeSystem& dts, F
for (int op_id = aop->block_id_to_first_atomic_op.at(block_id);
op_id < aop->block_id_to_end_atomic_op.at(block_id); op_id++) {
if (op_id == aop->block_id_to_first_atomic_op.at(block_id)) {
try_modify_input_types_for_casts(op_id, func.ir2.env.casts(),
try_modify_input_types_for_casts(op_id, func.ir2.env.casts(), func.ir2.env.stack_casts(),
&block_init_types.at(block_id), nullptr, dts);
} else {
try_modify_input_types_for_casts(op_id, func.ir2.env.casts(), &op_types.at(op_id - 1),
nullptr, dts);
try_modify_input_types_for_casts(op_id, func.ir2.env.casts(), func.ir2.env.stack_casts(),
&op_types.at(op_id - 1), nullptr, dts);
}
}
}
Expand Down
Loading

0 comments on commit 542edfb

Please sign in to comment.