grekuliPuli
grekuliPuli

Reputation: 31

how to reverse-engineer the C integer type from the assembly code?

Consider the following C function prototype, where num_t is a data type declared using typedef:

void store_prod(num_t *dest, unsigned x, num_t y) {
 *dest = x*y;
}

gcc generates the following assembly code implementing the body of the computation: What data type is num_t?

The correct answer is that num_t is unsigned long long but I really don't understand why, any help will be greatly appreciated!!

# dest  at %ebp +8, x at %ebp +12, y at %ebp +16
movl  12(%ebp), %eax
movl  20(%ebp), %ecx
imull %eax, %ecx
mull  16(%ebp)
leal  (%ecx,%edx), %edx
movl  8(%ebp), %ecx
movl  %eax, (%ecx)
movl  %edx, 4(%ecx)

Upvotes: 2

Views: 261

Answers (1)

Peter Cordes
Peter Cordes

Reputation: 364997

We can tell from the use of (%ebp) addressing modes that this is 32-bit code, not x86-64.

In 32-bit mode, unsigned long long is the only 64-bit unsigned integer type, in any of the common ABIs. (e.g. the i386 System V ABI, used on Linux). long is 32-bit.

We can tell that num_t is a 64-bit integer type because it's stored in two 32-bit halves from the result of integer multiply and addition.

We can tell that it's an unsigned integer type because gcc used mul instead of imul between the x and the low half of y. (2-operand imul %eax, %ecx to multiply x with the upper half of y is the same binary operation for signed or unsigned: only full-multiply (N x N => 2N bits) cares about signedness.)

IDK why gcc would use leal (%ecx,%edx), %edx instead of add %ecx, %edx. Maybe you compiled with -mtune=atom or something? Preserving flags isn't necessary.

Anyway, it's an ordinary 64 x 32 => 64 bit extended-precision multiplication.


This is C, not C++, so a class wrapping a 64-bit integer with an overloaded * operator can be ruled out, too.

We can rule out FP types because that would have used an FP multiply.

Upvotes: 4

Related Questions