Почему он работает для сохранения результатов CPUID в массиве int и передачи в printf "% s"? - PullRequest
0 голосов
/ 19 апреля 2020
#include <stdio.h>

int main() {
    int index = 0;
    int reg[3];
    __asm__(
        "cpuid \n\t"
        : "=b"(reg[0]), "=c"(reg[2]), "=d"(reg[1])
            // editor's note: CPUID also modifies EAX; this buggy code doesn't tell the compiler about it
        : "a"(index)); 
    printf("%s\n",&reg);
}

Почему int обрабатывается как строка. Мне нужна помощь, чтобы объяснить, почему это так.

1 Ответ

3 голосов
/ 19 апреля 2020

Если в eax указан ноль, инструкция cpuid возвращает две вещи:

  • в eax 1 , максимальное значение, поддерживаемое для входного операнда в eax для cpuid на этом процессоре и
  • в ebx, edx и eax, строка «GenuineIntel», распределенная по этим регистрам в указанном порядке. Байты строки просто помещаются в регистры, по четыре байта в каждом регистре.

Код сборки, который вы показали, заставляет компилятор G CC или Clang скопировать последние регистры в ваш reg массив.

Чтобы правильно распечатать этот массив, вы можете передать printf:

  • строку формата, содержащую %.12s для печати не более 12 символов и
  • a char *, который указывает на первый байт reg.

Например:

printf("%.12s\n", (char *) reg);

(Обратите внимание, что это преобразование в char * специально разрешено правилами псевдонимов * 1057: доступ к байтам любого объекта возможен с использованием типа указателя на символ. Другие преобразования указателя или использование их результатов не всегда определяются стандартом C. Педантически, * Может потребоваться 1038 *, поскольку он предоставляет указатель на первый байт всего массива, а не указатель на первый байт reg[0]. Строгая интерпретация стандарта C может сказать, что последний указатель не является относительным может использоваться для арифметики c за пределами reg[0].)


Сноска 1: Изменение операнда только для ввода является ошибкой; компилятор будет предполагать, что EAX не изменяется в операторе asm. В этом случае это может привести к вызову printf с al != 0, даже если в регистрах XMM нет значений с плавающей запятой. (x86-64 Соглашение о вызовах System V.) С другими вызывающими / окружающим кодом проблемы могут быть более серьезными.

Поскольку вам не важно значение index после выражения asm, a чтение / запись "+a" Операнд - это простой способ сообщить компилятору, что EAX также модифицирован:

    int index = 0;
    int reg[3];
    __asm__(
        "cpuid"
        : "+a"(index), "=b"(reg[0]), "=c"(reg[2]), "=d"(reg[1])
        );
...