dissect.cstruct icon indicating copy to clipboard operation
dissect.cstruct copied to clipboard

Ignore casts when evaluating #defines

Open d3dave opened this issue 1 year ago • 3 comments

For example:

#define A ((unsigned long) 1)
#define B ((unsigned long) 2)
#define C (A | B)

causes A, B and C to have string values like "(A | B)" instead of the numeric values.

d3dave avatar Apr 29 '24 17:04 d3dave

This looks very fixable. Probably requires dealing with casts in the expression parser though.

@sMezaOrellana can you perhaps recommend the best way to add this to your parser?

Schamper avatar May 06 '24 10:05 Schamper

I will try to have a proper look in the coming days. In order implement or deal with type-casting it will require changing the evaluate function.

    def evaluate(self, context: Optional[dict[str, int]] = None) -> int:
        """Evaluates an expression using a Shunting-Yard implementation."""

        ...
        while i < len(tmp_expression):
            current_token = tmp_expression[i]
            ...
            elif current_token == "sizeof":
                if len(tmp_expression) < i + 3 or (tmp_expression[i + 1] != "(" or tmp_expression[i + 3] != ")"):
                    raise ExpressionParserError("Invalid sizeof operation")
                self.queue.append(len(self.cstruct.resolve(tmp_expression[i + 2])))
                i += 3
            ...
            elif current_token == "(":
                if i > 0:
                    previous_token = tmp_expression[i - 1]
                    if self.is_number(previous_token):
                        raise ExpressionParserError(
                            f"Parser expected sizeof or an arethmethic operator instead got: '{previous_token}'"
                        )

                self.stack.append(current_token)
            elif current_token == ")":
                if i > 0:
                    previous_token = tmp_expression[i - 1]
                    if previous_token == "(":
                        raise ExpressionParserError(
                            f"Parser expected an expression, instead received empty parenthesis. Index: {i}"
                        )

        ...
        while len(self.stack) != 0:
            if self.stack[-1] == "(":
                raise ExpressionParserError("Invalid expression")

            self.evaluate_exp()

        return self.queue[0]

Take for example ((unsigned long) 10) this is something like an expression with the left-hand side being (unsigned long) and the right-hand side being 10, there is no explicit operator.

This could simply evaluate to 10. In practise a simple solution would be to change the case where elif current_token == "(": and elif current_token == ")":. If whatever is between ( and ) is a casting expression just skip over it. It's not very pretty but it would work. In order to actually implement casting we would need to extend the parser, to support evaluating the casting expression.

Does cstruct implement casting?

I hope this helps. And will look at this properly sometime this week.

sMezaOrellana avatar May 06 '24 19:05 sMezaOrellana

We don't really support casting, no. The new v4 (#35) should make this easier/possible, but it's not a feature as of yet.

Schamper avatar May 07 '24 15:05 Schamper