Как структура организована в сборке? - PullRequest
1 голос
/ 18 июня 2020

Я пытаюсь выяснить, как происходит пространство между каждым элементом структуры компилятора. В этом примере:

    struct s{
        int a,b,c;
    };
    struct s get(int a){
        struct s foo = {.a=a,.b=a+1,.c=a+2};
        return foo;
    }

скомпилировано с cc -S a.c:

    .file   "a.c"
    .text
    .globl  get
    .type   get, @function
get:
.LFB0:
    pushq   %rbp
    movq    %rsp, %rbp
    movl    %edi, -36(%rbp)
    movl    -36(%rbp), %eax
    movl    %eax, -24(%rbp)
    movl    -36(%rbp), %eax
    addl    $1, %eax
    movl    %eax, -20(%rbp)
    movl    -36(%rbp), %eax
    addl    $2, %eax
    movl    %eax, -16(%rbp)
    movq    -24(%rbp), %rax
    movq    %rax, -12(%rbp)
    movl    -16(%rbp), %eax
    movl    %eax, -4(%rbp)
    movq    -12(%rbp), %rax
    movl    -4(%rbp), %ecx
    movq    %rcx, %rdx
    popq    %rbp
    ret
.LFE0:
    .size   get, .-get
    .ident  "GCC: (Debian 8.3.0-6) 8.3.0"
    .section    .note.GNU-stack,"",@progbits

Оптимизация не используется. Возникает вопрос, почему -36(%rbp) используется в качестве «ссылки» первого члена, когда они расположены последовательно в

.a == -24(%rbp)
.b == -20(%rbp)
.c == -16(%rbp)

Нет необходимости выделять -36(%rbp), который здесь использует компилятор. Это намеренно (поскольку комната или компилятор использует -36(%rbp) как «ссылку» на первый член)?

Также, в конце,

movq    -24(%rbp), %rax  #take first member
movq    %rax, -12(%rbp)  #place it randomly
movl    -16(%rbp), %eax  #take third member
movl    %eax, -4(%rbp)   #place it randomly

Не имеет смысла, он не является последовательным с исходной структурой, а первый и третий член структуры случайным образом копируются в пространство функция get была выделена.

Каковы соглашения для структур?

1 Ответ

3 голосов
/ 18 июня 2020

Код, который вы наблюдаете, представляет собой смесь трех разных вещей: фактического макета struct s, спецификации ABI о том, как возвращать структуры из функций, и антиоптимизации , вставленных многими компиляторами. в своем режиме по умолчанию (эквивалентном -O0), чтобы простые отладчики могли находить и изменять значения переменных при остановке в любой точке останова (см. Почему clang создает неэффективный asm с -O0 (для этой простой суммы с плавающей запятой )? , чтобы узнать больше об этом).

Вы можете вырезать второй из этих факторов, указав get записать в аргумент struct s * вместо того, чтобы возвращать структуру по значению, а третий путем компиляции с gcc -O2 -S вместо gcc -S. (Также попробуйте -Og и -O1; сложные оптимизации, примененные в -O2, тоже могут сбивать с толку.) Например:

$ cat test.c
struct s {
  int a,b,c;
};
void get(int a, struct s *s)
{
  s->a = a;
  s->b = a+1;
  s->c = a+2;
}
$ gcc -O2 -S test.c
$ cat test.s
    .file   "test.c"
    .text
    .p2align 4
    .globl  get
    .type   get, @function
get:
.LFB0:
    .cfi_startproc
    leal    1(%rdi), %eax
    movl    %edi, (%rsi)
    addl    $2, %edi
    movl    %eax, 4(%rsi)
    movl    %edi, 8(%rsi)
    ret
    .cfi_endproc
.LFE0:
    .size   get, .-get
    .ident  "GCC: (Debian 9.3.0-13) 9.3.0"
    .section    .note.GNU-stack,"",@progbits

Из этого ассемблера должно быть яснее, что a находится со смещением 0 в пределах struct s, b находится со смещением 4, а c со смещением 8.

Структура структуры определяется "psABI" (двоичный интерфейс приложения c, специфичный для процессора ) для каждой архитектуры ЦП. Вы можете прочитать спецификации psABI для x86 по адресу https://github.com/hjl-tools/x86-psABI/wiki/X86-psABI. Они также объясняют, как структуры возвращаются из функций. Также важно знать, что макет кадра стека только частично определяется psABI. Некоторые из "случайных" смещений в дампе сборки фактически произвольно выбираются компилятором.

...