2016-06-27 11 views
1

Я работаю над проектом C++ в Visual Studio 2008 IDE, где мне нужно использовать новую инструкцию Intel RDRAND. Я сделал быстрый поиск и MSDN рекомендует использовать _rdrand64_step искробезопасности, определенный в immintrin.h, что я не имею в VS 2008.Как добавить инструкцию RDRAND в 64-разрядный код, скомпилированный с помощью VS 2008?

В 32-битном скомпилированный код в я могу уйти с использованием asm ключевого слова, как, например:

__asm 
    { 
     xor eax, eax 

     ;RDRAND instruction = Set random value into EAX. 
     ;Will set overflow [C] flag if success 
     _emit 0x0F 
     _emit 0xC7 
     _emit 0xF0 
    } 

Но на x6 не поддерживается.

Можете ли вы предложить, как я могу скомпилировать мой проект для 64-бит с инструкцией RDRAND?

+0

С отключенным asm больше не существует способа использовать новые операционные системы со старыми компиляторами. Вы * можете * компилировать один отдельный файл объекта отдельно, хотя и затем связывать его, хотя ... – o11c

+0

Может ли кто-то с более поздней версией VS (2012 и выше) скопировать и вставить внутреннее определение для '_rdrand64_step'? Или он поставляется с отдельным двоичным файлом? – c00000fd

+2

@ c00000fd Это неотъемлемая часть. Нет определения. Вот что такое внутреннее средство. – immibis

ответ

4

Вам необходимо обновить свой компилятор до тех, который поддерживает встроенный _rdrand64_step (поддерживается с Visual Studio 2012), или использовать обычную (внешнюю) сборку для создания собственных функций (поскольку Visual C++ не поддерживает встроенную сборку для x86 -64 целей).

Например:

_TEXT SEGMENT 

    PUBLIC rdrand32_step 
    PUBLIC rdrand32_retry 
    PUBLIC rdrand64_step 
    PUBLIC rdrand64_retry 

    ; int rdrand32_step(unsigned *p) 
rdrand32_step PROC 
    xor  eax, eax 
    rdrand edx 
    ; DB 0fh, 0c7h, 0f2h 
    setc al 
    mov  [rcx], edx 
    ret 
rdrand32_step ENDP 

    ; unsigned rdrand32_retry() 
rdrand32_retry PROC 
retry: 
    rdrand eax 
    ; DB 0fh, 0c7h, 0f0h 
    jnc  retry 
    ret 
rdrand32_retry ENDP 

    ; int rdrand64_step(unsigned long long *p) 
rdrand64_step PROC 
    xor  eax, eax 
    rdrand rdx 
    ; DB 048h, 0fh, 0c7h, 0f2h 
    setc al 
    mov  [rcx], edx 
    ret 
rdrand64_step ENDP 

    ; unsigned long long rdrand64_retry() 
rdrand64_retry PROC 
retry: 
    rdrand rax 
    ; DB 048h, 0fh, 0c7h, 0f0h 
    jnc  retry 
    ret 
rdrand64_retry ENDP 

_TEXT ENDS 

    END 

Если вы используете версию MASM из Visual Studio 2008, вы, вероятно, придется закомментировать инструкции RDRAND и раскомментируйте директивы DB, которые следуют за ними.

+0

Спасибо. Хотя вы склонны смешивать регистры 'x86' и' x64'. Также не переходите на «rdrand», если флаг переполнения не установлен. Вы можете создать бесконечный цикл или «съесть» много циклов процессора. Intel не рекомендует делать это более 10 раз подряд. Проверьте [4.2.1 Рекомендации по повторному запуску] (https://software.intel.com/en-us/articles/intel-digital-random-number-generator-drng-software-implementation-guide) – c00000fd

+2

@ c00000fd: в 64- битный код нет ничего плохого в использовании 32-разрядных регистров. В некоторых из его кода он использует тот факт, что в 64-битном режиме вы можете воспользоваться тем фактом, что запись в 32-разрядный регистр автоматически равна нулю для всего 64-битного регистра. Это также может сохранять байты, поскольку префиксы REX не требуют кодирования. Например, 'xor eax, eax' закодирован с меньшим байтом, чем' xor rax, rax'. Росс также возвращает 32-битный 'int' из своих функций, поэтому использует _EDX_, а не _RDX_. –

+0

@MichaelPetch: Это было бы нормально для кода asm. Я предполагаю, что смешанный код соответствует соглашениям о вызовах Microsoft C++, которые будут нарушены, если пренебречь более высокими 32 битами в регистрах. – c00000fd

0

Ничего себе, мне потребовалось некоторое время, чтобы понять это. Вот шаги для Visual Studio 2008 для x64 только для компиляции:

(A) Создайте пустой проект: Файл -> Создать -> Проект. Затем нажмите «Visual C++» и выберите «Empty Project». Назовите это что-нибудь и нажмите «ОК», чтобы создать.

(B) Перейти в папку установки VS, в моем случае это был C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\VCProjectDefaults и скопировать masm.rules файл и назовите его masm64.rules

(C) Открыть masm64.rules в блокноте и поиск Microsoft Macro Assembler и изменить его до x64 Microsoft Macro Assembler. Для этого будет два места. Затем найдите ml.exe и измените его на ml64.exe. Затем сохраните этот файл и закройте Блокнот.

(D) Щелкните правой кнопкой мыши проект в «Обозревателе решений» и выберите «Пользовательские правила сборки» и установите флажок x64 Microsoft Macro Assembler и нажмите «ОК».

(E) правой кнопкой мыши проект в обозревателе решений «» и выберите Add -> New Item выберите Text File (.txt) и назовите его что-то с .asm расширением. Я назову это funcs_asm_x64.asm. Затем нажмите «ОК».

(F) Открыть funcs_asm_x64.asm и набрать x64 asm. Для меня мне было интересно позвонить RDRAND с 64-битным операндом. Я сделал следующее. Эта функция примет один параметр в качестве указателя на 64-битное целое число, которое будет заполнено случайными битами. Он вернет 1 в rax если успех, иначе он вернет 0.

Одна вещь, чтобы помнить, что x64-код использует только __fastcallcalling convention, а это означает, что первые 4 параметра для функции передаются в регистрах: RCX, RDX, R8 и R9:

.code 

RdRand64 PROC 
    ; RCX = pointer to receive random 64-bit value 
    ; RETURN: [RAX] = 1 if success, 0 if failed 

    xor   rax, rax 

    test  rcx, rcx 
    jz   lbl_out 

    ;push  rdx 
    xor   rdx, rdx 
    DB   048h, 0fh, 0c7h, 0f2h  ;RDRAND RDX 

    setc  al 
    mov   [rcx], rdx 
    ;pop  rdx 

lbl_out: 
    ret 

RdRand64 ENDP 

END 

(G) Затем щелкните правой кнопкой мыши проект в «Обозревателе решений» и выберите «Добавить» - «Новый элемент», выберите C++ File (.cpp) и назовите его main.cpp и нажмите «ОК», чтобы создать. Затем добавьте следующую строку в файл main.cpp:

extern "C" __int64 __fastcall RdRand64(unsigned __int64* pRndVal); 

void main() 
{ 
} 

Основная часть является extern "C" определение. main() необходим для удовлетворения требований MASM.

(H) Затем перейдите в сборку -> Диспетчер конфигурации и откройте раскрывающийся список, где указано «Активная платформа решений», и выберите «Создать». Затем выберите «x64» в «Тип или выберите новую платформу» и нажмите «ОК». Затем выберите «x64» как «Активная платформа решений», а также выберите «Отпустить» в «Конфигурация активного решения».

(I) Закрыть окно диспетчера конфигурации и построить решение. Если вам удалось найти файл funcs_asm_x64.obj в папке \x64\Release для вашего решения. Скопируйте этот файл в основную папку решения (где вы, необходимые для использования RDRAND инструкции.)

(J) Тогда в главном решении, где вам необходимо использовать RDRAND инструкции, щелкните правой кнопкой мыши на проект в «обозревателе решений »и перейдите в« Свойства ». Затем перейдите в Linker -> Command Line и добавьте свое имя файла obj. Очевидно, что это делается только для платформы x64 для Debug и Release. В моем случае это было funcs_asm_x64.obj. Нажмите «ОК» для сохранения.

(K) Тогда, чтобы использовать эту функцию, что я только что создали, первый добавить extern "C" определение так же, как вы имели в первом проекте:

extern "C" __int64 __fastcall RdRand64(unsigned __int64* pRndVal); 

, а затем вы можете назвать его как таковой (очевидно, она не может быть встраиваемыми):

unsigned __int64 randomNumber = 0; 
__int64 bResult = RdRand64(&randomNumber); 

(1) Очевидно, что все вышесказанное не является необходимым для Win32 или x86 сборка. Для этого просто используйте встроенную сборку, как я показал в своем оригинальном посте.

(2) Также необходимо выполнить команду __cpuid, чтобы гарантировать, что инструкция RDRAND поддерживается. На многих процессорах это еще не так. Поэтому, если это не так, тогда не вызывайте мой метод RdRand64, так как он сработает! Вы можете использовать этот код, чтобы проверить и сохранить результат где-то в глобальной переменной:

#include <intrin.h> 

bool is_RDRAND_supported() 
{ 
    int name[4] = {0}; 
    __cpuid(name, 0); 

    if(name[1] == 0x756e6547 &&   //uneG 
     name[2] == 0x6c65746e &&  //letn 
     name[3] == 0x49656e69)   //Ieni 
    { 
     int data[4] = {0}; 
     __cpuid(data, 1); 

     //Check bit 30 on the 2nd index (ECX register) 
     if(data[2] & (0x1 << 30)) 
     { 
      //Supported! 
      return true; 
     } 
    } 

    return false; 
} 

(3) Существует способ включить asm файл в том же проекте в VS 2008. К сожалению, если вы это сделаете, вы не сможете переключить проект обратно на Win32 и скомпилировать, если вам нужно.Поэтому, если вы компилируете его только для x64, сохраните шаг и сделайте все это в том же решении.

+0

Так как RDX - это изменчивый регистр в соглашении с 64-битным MS-сервером, почему вы сохраняете и восстанавливаете _RDX_ (с push/pop)? 'xor eax, eax', чтобы обнулить регистр, не нарушает соглашение о вызове. Он имеет тот же эффект, что и «xor rax, rax», но занимает один байт (оба очищают весь 64-битный регистр). То же самое относится к 'xor rdx, rdx' –

+0

@MichaelPetch: Ahhh. Хорошая точка зрения. Это не требует экономии. Это просто привычка спасать государство. – c00000fd

0

Это довольно просто, хотя и косвенное: создайте крошечную C-оболочку для _rdrand64_step, скомпилируйте ее в файл .OBJ с помощью VS2012 без каких-либо причудливых параметров (No/LTCG, no/Gs и т. Д.) И свяжите этот объектный файл как находится в вашем проекте VS2008. Компилятор VS2008 может не знать эту инструкцию, но компоновщику VS2008 все равно.

+0

Ну, есть одна мелочь, необходимая для этого - против 2012 года. – c00000fd

+0

Правда, но Express Edition должен делать. – MSalters