Перезапись памяти при использовании сильных и слабых символов в двух связанных файлах - PullRequest
0 голосов
/ 25 января 2020

Предположим, что у нас есть 64-битная машина x86, которая имеет младший порядок байтов и поэтому хранит младший байт слова в байте с самым низким адресом. Предполагая стандартные правила выравнивания для 64-битного компилятора x86 Linux C.

Рассмотрим

Файл 1:

#include <stdio.h>

struct cs {
    int count;
    unsigned short flags;
};
struct cs gcount;
extern void add_counter( int n );

int main(int c, char *argv[]);

int main(int c, char *argv[]) {
    gcount.flags = 0xe700;
    gcount.count = 1;
    add_counter(42);
    printf("count =%d\n", gcount.count);
    return 0;
}

Файл 2:

struct cs {
    unsigned short flags;
    int count;
};

struct cs gcount = {0,0};

void add_counter (int n) {
    gcount.count +=n;
}

Если скомпилировано, вывод будет 1.

Объяснение:

count определяется как сильная глобальная переменная int, поэтому второй файл инициализируется равным {0,0}, здесь порядок еще не имеет значения, поскольку он содержит только нули.

Структура / тип определяется для каждой единицы компиляции, поэтому первый файл использует первое определение для записи в структуру, что означает

gcount.flags = 0xe700; gcount.count = 1;

заставляет память выглядеть как

[e7 00 | 00 00 00 01] где (в обратном порядке) левый верх, а правый - нижняя часть памяти.

(между двумя полями нет заполнения, поскольку short в конце, sizeof сообщит 8B хотя)

при вызове add_counter (42) второй файл будет использовать второе определение cs и смотреть на память как

[e7 00 00 00 | 00 01]

Теперь между двумя полями добавляется 2B, и доступ на запись к счетчику, таким образом, влияет на диапазон

[ e7 00 00 00 | 00 01]

42 равно 0x2a в шестнадцатеричном (2 * 16 + 10) и, следовательно, приведет к

[ e7 2a 00 00 | 00 01]

, преобразовав это обратно в представление, которое имеет первый файл, мы получим

[e7 2a | 00 00 00 01]

и, таким образом, результат равен 1 вместо ожидаемых 43.

Теперь я понимаю общую суть, но я немного запутался, почему мы получаем [*e7 2a* 00 00 | 00 01] при добавлении 42=0x2a, а не [*e7 00 00 2a | 00 01].

Я ожидаю [*e7 00 00 2a | 00 01], потому что мы используем little-endian, то есть самый правильный бит - младший бит. Таким образом, e7 на самом деле представляет здесь самые значимые 8 бит.

1 Ответ

1 голос
/ 25 января 2020

Несмотря на мои пренебрежительные комментарии о самом упражнении, можно интерпретировать вопрос как более простой вопрос о порядке байтов. В этом смысле проблема заключается в следующем утверждении:

little-endian, что означает, что самый правый бит - младший бит.

Little-endian означает, что байты упорядочены от наименее значимых до наиболее значимых. Термин, введенный в Engli sh и Engli sh, пишется слева направо, это означает, что самый left байт - младший бит в порядке с прямым порядком байтов.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...