How would I translate the following assembly code from the compiler to C when working with structs?

Viewed 82

Suppose I define a new struct:

struct s {
   int *x;
   struct {
      short sh[2];
      int i;
   } w;
   struct s *next;
};

In addition, I write a function to initialize it:

void init_s(struct s *ss) {
   ss->w.sh[1] = /* Line 1 */;
   ss->x = /* Line 2 */;
   ss->next = /* Line 3 */;
}

The compiler generates the following assembly code for init_s:

init_s:             # line 1
   movw 8(%rdi), %ax    # line 2
   movw %ax, 10(%rdi)   # line 3
   leaq 12(%rdi), %rax  # line 4
   movq %rax, (%rdi)    # line 5
   movq %rdi, 16(%rdi)  # line 6
   retq                 # line 7

What I am trying to do is fill in the missing lines of code for init_s based on the assembly. I have figured out (or at least I think so) lines 1 and 2. Line 1 should be ss->w.sh[0] and line 2 should be &(ss->w.sh[2]). However, I am having trouble with line 3. I think it would be &(ss->x) based on the assembly, but I feel like this is incorrect and I'm not sure why. Any feedback or suggestion would be greatly appreciated in helping me learn more about assembly and structs.

1 Answers

Line 1 should be ss->w.sh[0]

I agree.

line 2 should be &(ss->w.sh[2])

That's the right address, except that ss->w.sh only has 2 elements so w.sh[2] is out of bounds. This is instead a pointer to the next member of the struct, i.e. ss->x = &(ss->w.i). That also makes sense with the ss->x member being int * instead of short *.

However, I am having trouble with line 3. I think it would be &(ss->x) based on the assembly

Similar issue: it's true that %rdi could be a pointer to ss->x, but it doesn't make sense type-wise to assign &ss->x (type int **) to ss->next (type struct s *). You could also view %rdi as being a pointer to the struct *ss itself, and this is more sensible: ss->next = ss;. It makes a circular linked list with a single node whose next is itself.

The moral here is that there can be different ways in C to refer to the same address, all of which will generate identical assembly, and you have to use common sense to make an educated guess as to which one the author more likely intended. It's theoretically possible that the author of the C code wrote ss->next = (struct s *)&(ss->x); as the third line - we can't prove that they didn't - but ss->next = ss; is much more sensible and thus much more likely.

This sort of thing is why reverse engineering is an art as well as a science.

Related