Недопустимый доступ к битовому полю в MinGW64 - PullRequest
1 голос
/ 21 октября 2019

Я исследую, как разные компиляторы обрабатывают не выровненный доступ к элементам битовых полей структуры, а также к элементам, которые пересекают границы примитивных типов, и я думаю, что MinGW64 содержит ошибки. Моя тестовая программа:

#include <stdint.h>
#include <stdio.h>

/* Structure for testing element access

The crux is the ISO C99 6.7.2.1p10 item:

An implementation may allocate any addressable storage unit large enough to hold a bitfield.
If enough space remains, a bit-field that immediately follows another bit-field in a
structure shall be packed into adjacent bits of the same unit. If insufficient space remains,
whether a bit-field that does not fit is put into the next unit or overlaps adjacent units is
implementation-defined. The order of allocation of bit-fields within a unit (high-order to
low-order or low-order to high-order) is implementation-defined. The alignment of the
addressable storage unit is unspecified.
*/

typedef struct _my_struct
{
    /* word 0 */
    uint32_t    first           :32;    /**< A whole word element   */
    /* word 1 */
    uint32_t    second          :8;     /**< bits 7-0               */
    uint32_t    third           :8;     /**< bits 15-8              */
    uint32_t    fourth          :8;     /**< bits 23-16             */
    uint32_t    fifth           :8;     /**< bits 31-24             */
    /* word 2 */
    uint32_t    sixth           :16;    /**< bits 15-0              */
    uint32_t    seventh         :16;    /**< bits 31-16             */
    /* word 3 */
    uint32_t    eigth           :24;    /**< bits 23-0              */
    uint32_t    ninth           :8;     /**< bits 31-24             */
    /* word 4 */
    uint32_t    tenth           :8;     /**< bits 7-0               */
    uint32_t    eleventh        :24;    /**< bits 31-8              */
    /* word 5 */
    uint32_t    twelfth         :8;     /**< bits 7-0               */
    uint32_t    thirteeneth     :16;    /**< bits 23-8              */
    uint32_t    fourteenth      :8;     /**< bits 31-24             */
    /* words 6 & 7 */
    uint32_t    fifteenth       :16;    /**< bits 15-0              */
    uint32_t    sixteenth       :8;     /**< bits 23-16             */
    uint32_t    seventeenth     :16;    /**< bits 31-24 & 7-0       */
    /* word 7 */
    uint32_t    eighteenth      :24;    /**< bits 31-8              */
    /* word 8 */
    uint32_t    nineteenth      :32;    /**< bits 31-0              */
    /* words 9 & 10 */
    uint32_t    twentieth       :16;    /**< bits 15-0              */
    uint32_t    twenty_first    :32;    /**< bits 31-16 & 15-0      */
    uint32_t    twenty_second   :16;    /**< bits 31-16             */
    /* word 11 */
    uint32_t    twenty_third    :32;    /**< bits 31-0              */
} __attribute__((packed)) my_struct;


uint32_t buf[] = {
        0x11223344, 0x55667788, 0x99AABBCC, 0x01020304, /* words 0  - 3     */
        0x05060708, 0x090A0B0C, 0x0D0E0F10, 0x12131415, /* words 4  - 7     */
        0x16171819, 0x20212324, 0x25262728, 0x29303132, /* words 8  - 11    */
        0x34353637, 0x35363738, 0x39404142, 0x43454647  /* words 12 - 15    */
};

uint32_t data[64];

int main(void)
{
    my_struct *p;

    p = (my_struct*) buf;

    data[0] = 0;
    data[1] = p->first;
    data[2] = p->second;
    data[3] = p->third;
    data[4] = p->fourth;
    data[5] = p->fifth;
    data[6] = p->sixth;
    data[7] = p->seventh;
    data[8] = p->eigth;
    data[9] = p->ninth;
    data[10] = p->tenth;
    data[11] = p->eleventh;
    data[12] = p->twelfth;
    data[13] = p->thirteeneth;
    data[14] = p->fourteenth;
    data[15] = p->fifteenth;
    data[16] = p->sixteenth;
    data[17] = p->seventeenth;
    data[18] = p->eighteenth;
    data[19] = p->nineteenth;
    data[20] = p->twentieth;
    data[21] = p->twenty_first;
    data[22] = p->twenty_second;
    data[23] = p->twenty_third;

    if( p->fifth == 0x55 )
    {
        data[0] = 0xCAFECAFE;
    }
    else
    {
        data[0] = 0xDEADBEEF;
    }

    int i;
    for (i = 0; i < 24; ++i) {
        printf("data[%d] = 0x%0x\n", i, data[i]);
    }
    return data[0];
}

И результаты, которые я нашел:

| Data Member | Type    | GCC Cortex M3  | GCC mingw64   | GCC Linux     | GCC Cygwin    |
|:------------|:-------:|:---------------|:--------------|:--------------|:--------------|
| data[0]     | uint32_t| 0x0            | 0xcafecafe    | 0xcafecafe    | 0xcafecafe    |
| data[1]     | uint32_t| 0x11223344     | 0x11223344    | 0x11223344    | 0x11223344    |
| data[2]     | uint32_t| 0x88           | 0x88          | 0x88          | 0x88          |
| data[3]     | uint32_t| 0x77           | 0x77          | 0x77          | 0x77          |
| data[4]     | uint32_t| 0x66           | 0x66          | 0x66          | 0x66          |
| data[5]     | uint32_t| 0x55           | 0x55          | 0x55          | 0x55          |
| data[6]     | uint32_t| 0xbbcc         | 0xbbcc        | 0xbbcc        | 0xbbcc        |
| data[7]     | uint32_t| 0x99aa         | 0x99aa        | 0x99aa        | 0x99aa        |
| data[8]     | uint32_t| 0x20304        | 0x20304       | 0x20304       | 0x20304       |
| data[9]     | uint32_t| 0x1            | 0x1           | 0x1           | 0x1           |
| data[10]    | uint32_t| 0x8            | 0x8           | 0x8           | 0x8           |
| data[11]    | uint32_t| 0x50607        | 0x50607       | 0x50607       | 0x50607       |
| data[12]    | uint32_t| 0xc            | 0xc           | 0xc           | 0xc           |
| data[13]    | uint32_t| 0xa0b          | 0xa0b         | 0xa0b         | 0xa0b         |
| data[14]    | uint32_t| 0x9            | 0x9           | 0x9           | 0x9           |
| data[15]    | uint32_t| 0xf10          | 0xf10         | 0xf10         | 0xf10         |
| data[16]    | uint32_t| 0xe            | 0xe           | 0xe           | 0xe           |
| data[17]    | uint32_t| 0x150d         | 0x1415        | 0x150d        | 0x150d        |
| data[18]    | uint32_t| 0x121314       | 0x171819      | 0x121314      | 0x121314      |
| data[19]    | uint32_t| 0x16171819     | 0x20212324    | 0x16171819    | 0x16171819    |
| data[20]    | uint32_t| 0x2324         | 0x2728        | 0x2324        | 0x2324        |
| data[21]    | uint32_t| 0x27282021     | 0x29303132    | 0x27282021    | 0x27282021    |
| data[22]    | uint32_t| 0x2526         | 0x3637        | 0x2526        | 0x2526        |
| data[23]    | uint32_t| 0x29303132     | 0x35363738    | 0x29303132    | 0x29303132    |

GCC Cortex M3 is
arm-none-eabi-gcc (GNU MCU Eclipse ARM Embedded GCC, 32-bit) 8.2.1 20181213 (release) [gcc-8-branch revision 267074]

GCC Mingw is
gcc.exe (i686-posix-dwarf-rev0, Built by MinGW-W64 project) 8.1.0

GCC Linux is
gcc (GCC) 4.4.7 20120313 (Red Hat 4.4.7-23)

GCC Cygwin is
gcc (GCC) 7.4.0

Кажется, что все версии GCC корректно обрабатывают нелицензированный доступ (например, my_struct.thirteeneth).

Проблема не в том, что члены, которые пересекают границу слова (my_struct.seventeenth), различаются, так как приведенный выше стандарт C99 четко утверждает, что поведение определяется реализацией. Проблема в том, что все последующие обращения явно некорректны (данные [17] и вкл.) Даже для выровненных элементов (my_struct.nineteenth & my_struct.twenty_third). Что здесь происходит, это ошибка или это допустимые значения?

Ответы [ 3 ]

3 голосов
/ 23 октября 2019

Он не содержит ошибок, он размещает битовые поля в соответствии с Windows ABI.

В соответствии с gcc docs :

Если упакованный используется в структуреили если используются битовые поля, возможно, Microsoft ABI излагает структуру иначе, чем обычно делает GCC.

Скомпилируйте версию mingw64 с -mno-ms-bitfields, чтобы исправить разницу. Или скомпилируйте все другие версии с -mms-bitfields, чтобы выложить структуру так же, как mingw.

3 голосов
/ 22 октября 2019

Вероятность того, что широко используемый компилятор, такой как GCC, содержит ошибку, не равна нулю, а действительно минимальна. И есть вероятность, что ПЕБКАС. ; -)

В любом случае, я скомпилировал вашу программу с помощью «gcc (x86_64-posix-seh-rev0, созданный проектом MinGW-W64) 8.1.0» и получил тот же результат, что и вы в столбце »mingw64 ".

Более тонкий внешний вид показывает, что компилятор выравнивает битовые поля по 32-битным границам, которые равны ширине int. Это полностью соответствует главе 6.7.2.1 стандарта C17, в которой говорится, что "колебание" (в его словах из приложения J.3.9) определяется реализацией.

Другие варианты GCC не выравнивают битполя и поддержка, пересекающие 32-битные границы.

Это явно не ошибка, значения действительны. Возможно, стоит изучить причины и, возможно, опубликовать запрос функции.

Редактировать:

Просто чтобы уточнить, это макет с выравниванием. Нет ничего плохого в элементах seventeenth и следующих:

/* 0x11223344: word 0 */
uint32_t    first           :32;
/* 0x55667788: word 1 */
uint32_t    second          :8;
uint32_t    third           :8;
uint32_t    fourth          :8;
uint32_t    fifth           :8;
/* 0x99AABBCC: word 2 */
uint32_t    sixth           :16;
uint32_t    seventh         :16;
/* 0x01020304: word 3 */
uint32_t    eigth           :24;
uint32_t    ninth           :8;
/* 0x05060708: word 4 */
uint32_t    tenth           :8;
uint32_t    eleventh        :24;
/* 0x090A0B0C: word 5 */
uint32_t    twelfth         :8;
uint32_t    thirteeneth     :16;
uint32_t    fourteenth      :8;
/* 0x0D0E0F10: words 6 */
uint32_t    fifteenth       :16;
uint32_t    sixteenth       :8;
/* 0x12131415: word 7, because "seventeenth" does not fit in the space left */
uint32_t    seventeenth     :16;
/* 0x16171819: word 8, because "eighteenth" does not fit in the space left */
uint32_t    eighteenth      :24;
/* 0x20212324: word 9, because "nineteenth" does not fit in the space left */
uint32_t    nineteenth      :32;
/* 0x25262728: words 10 */
uint32_t    twentieth       :16;
/* 0x29303132: word 11, because "twenty_first" does not fit in the space left */
uint32_t    twenty_first    :32;
/* 0x34353637: word 12 */
uint32_t    twenty_second   :16;
/* 0x35363738: word 13, because "twenty_third" does not fit in the space left */
uint32_t    twenty_third    :32;
1 голос
/ 22 октября 2019

Вы вообще не можете полагаться , каким-либо образом, на то, как битовые поля расположены в структуре.

В 6.7.2.1 Структура и объединениеспецификаторы , параграф 11 стандарта C11 (выделение жирным шрифтом):

Реализация может выделить любую адресуемую единицу хранения, достаточно большую для хранения битового поля ,Если остается достаточно места, битовое поле, которое следует сразу за другим битовым полем в структуре, должно быть упаковано в смежные биты той же единицы. Если остается недостаточно места, независимо от того, помещается ли битовое поле, которое не умещается, в следующий блок или перекрывает соседние блоки, определяется реализацией . Порядок выделения битовых полей в блоке (от старшего к младшему или от младшего к старшему) определяется реализацией . Выравнивание адресуемой единицы хранения не указано.

Вы даже цитировали это. Учитывая это, для реализации не существует «неправильного» способа размещения битового поля.

Таким образом, вы не можете полагаться на размер контейнера битового поля.

Вы можетене полагайтесь на то, пересекает ли единица битовое поле или нет.

Вы не можете полагаться на порядок битовых полей в единице.

Тем не менее, ваш вопрос предполагает, что вы можете делать все это,даже используя такие термины, как «правильный», когда вы видите то, что вы ожидали, и «явно неправильный» для описания макетов битовых полей, которых вы не ожидали.

Это не «явно неправильный».

Если вам нужно знать, где находится бит в структуре, вы просто не можете использовать битовые поля переносимым образом.

На самом деле все ваши усилия по этому вопросу являются идеальным примером того, почему вы можете't полагаться на битовые поля.

...