domingo, 24 de abril de 2011

GCC - Atributos (1)

O GCC permite que você especifique atributos de funções, variáveis e estruturas usando a palavra-chave __attribute__, cuja sintaxe é __attribute__((lista de atributos)).

Os atributos podem ser especificados com dois underscores __ antes e depois de cada palavra. O atributo que irei falar aqui é o packed, ou __packed__.

The packed attribute specifies that a variable or structure field should have the smallest possible alignment--one byte for a variable, and one bit for a field, unless you specify a larger value with the aligned attribute.
(GCC 3.3.3 Manual)


Em tradução livre: O atributo packed especifica que uma variável ou campo de uma estrutura deve ter o mínimo de alinhamento possível -- um byte para uma variável, e um bit para um campo, a não ser que você especifique um valor maior que o atributo alinhado.

Isso significa que o GCC não vai adicionar zeros para preencher (para o alinhamento da memória), de modo que as variáveis ou campos fiquem imediatamente ao lado de cada uma. Por exemplo,


struct abc {
    int a;
    char b;
    int c;
};

struct abc    teste = {10, 20, 30};


Compilando o programa com a opção -S (compila o programa, mas não faz a montagem nem o link).

gcc -S -c sempacked.c -o sempacked.s


Olhando o arquivo gerado, sempacked.s:


    .file    "sempacked.c"
.globl test
    .data
    .align 4
    .type    teste, @object
    .size    teste, 12
teste:
    .long    10
    .byte    20
    .zero    3
    .long    30

    .ident    "GCC: (Gentoo 4.4.4-r2 p1.4, pie-0.4.5) 4.4.4"
    .section    .note.GNU-stack,"",@progbits


Veja o código em negrito. É aonde uma variável teste da estrutura é declarada. Atribuindo valores, para o campo "a" (int) como .long 10 seguido de "b" (char) como .byte 20. Para manter os campos alinhados, veja que o GCC adicionou 3 bytes com valor zero (.zero 3) antes do campo "c" (int), que foi declarado com .long 30. Isso faz com que a estrutura tenha tamanho 12 ao invés de 9, como é esperado.



struct abc {
    int a;
    char b;
    int c;
} __attribute__((__packed__));

struct abc    teste = {10, 20, 30};


E ele compilado:


    .file    "packed.c"
.globl teste
    .data
    .type    teste, @object
    .size    teste, 9
teste:
    .long    
10
    .byte    20
    .long    30

    .ident    "GCC: (Gentoo 4.4.4-r2 p1.4, pie-0.4.5) 4.4.4"
    .section    .note.GNU-stack,"",@progbits


Agora ele não gerou o preenchimento de zeros, fazendo com que o tamanho da estrutura abc seja 9. Lembre-se que alinhamento de memória sempre é bom, mesmo que comprometa espaço, então pense duas vezes antes de usar esse atributo. Geralmente, ele é útil quando você quer atribuir a estrutura para um bloco de memória e manipular ele através dos campos da estrutura.