从编译器 asm 输出逆向工程数组维度/结构布局?

Reverse engineer array dimensions / struct layout from compiler asm output?

在这段代码中,A和B是用#define定义的常量。 A 和 B 的值是多少?

typedef struct {
    int x[A][B];
    long y;
} str1;

typedef struct {
    char array[B];
    int t;
    short S[A];
    long u;
} str2;

void setVal(str1 *p, str2 *q) {
    long v1 = q->t;
    long v2 = q->u;
    p->y = v1+v2;
}

setVal 过程生成以下汇编代码:

setVal:
    movslq  8(%rsi), %rax
    addq   32(%rsi), %rax
    movq     %rax, 184(%rdi)
    ret

该结构具有以下对齐要求:

  • a char 可以从任何字节开始
  • a short 可能从偶数字节开始
  • 一个int可以从字节开始,可以被四整除
  • a long 可以从字节开始,可以被八整除

str1.y 字段是一个 long 并从 184 开始,这意味着 str1.x 可能包含 184180 字节。

str2.t 字段是一个 int 并从 8 开始,这意味着 str1.array 可能从 58 字节。

str2.u 字段是一个 long 并从 32 开始,这意味着 str2.S 可能从 1420 字节。

这是 str1 结构字段的图表:

+---------------+---+--------+
|  int x[A][B]  | ? | long y |
+---------------+---+--------+
|        184        |    8   |
+-------------------+--------+

这是 str2 字段的图表:

+---------------+---+-------+------------+---+--------+
| char array[B] | ? | int t | short S[A] | ? | long u |
+---------------+---+-------+------------+---+--------+
|         8         |   4   |        20      |    8   |
+-------------------+-------+----------------+--------+

之后,你应该解决以下系统:

177 <= 4 * A * B <= 184
5 <= B <= 8
14 <= A * 2 <= 20      // 7 <= A <= 10

答案是:A = 9B = 5


您可以使用遵循生成原始代码的编译器使用的相同 ABI/调用约定的编译器来测试您的答案(以及每个不等式的范围)。它使用 8 字节 long:注意 addq 的 64 位操作数大小,而不是 addl 和 8 字节存储。因此,我们可以推断它很可能是 x86-64 System V ABI,而不是 Windows x86-64 调用约定(使用 4 字节 long)。

Godbolt 编译器资源管理器有 gcc、clang、ICC 和 MSVC。前 3 个目标 Linux,但 MSVC 以 Windows 调用约定为目标,因此不会同意具有较小 long 需要较少对齐的结构布局。

Replacing int x[A][B] with char t[177] (or other sizes) 证明 177 是最小值,184 是导致存储到 184(%rdi) 的最大大小。所以我们可以写 176 < 4*A*B <= 184。或者,为了保持 4 的倍数,180 <= 4*A*B <= 184 也或多或少是正确的;我们可以根据 int.

的大小排除 177..179