MSVC

Материал из SynapseOS wiki
Перейти к навигации Перейти к поиску

Описание

Microsoft Visual C++ (MSVC) — компилятор для приложений на языке C++, разработанный корпорацией Microsoft и поставляемый либо как часть комплекта Microsoft Visual Studio, либо Build Tools for Visual Studio.

Возможности

  • Поддерживает современные стандарты языка, на текущий момент C++20
  • Использование модулей, в качестве замены заголовочных файлов
  • Быстрое внесение изменений стандарта в компилятор

Особенности

  • Использование ABI x64
  • Отсутствует inline asm при сборке x64 проекта
  • Создание исполняемых файлов только формата PE и MS COFF
  • Линкер не поддерживает скрипты
  • Чтобы получить на выходе чистый бинарный файл нужно использовать утилиту objcopy из набора GNU

Компилятор

Флаги

Все флаги можно найти здесь

Но основные, которые могут вам понадобиться это:

  • Общие
    • /W - Отключите все предупреждения
    • /Wn - n уровень серьезности предупреждений варьируемый от 0 до 4 включительно
    • /Wall - Включите все предупреждения, включая предупреждения, которые отключены по умолчанию
  • Оптимизация
    • /Od - Отключает оптимизацию
    • /O1 - Уменьшает размер кода
    • /O2 - Создает быстрый код
    • /Os - Отдает приоритет уменьшению размера кода
    • /Ot - Отдает приоритет быстрому коду
    • /Oi - Создает встроенные функции
  • Создание кода
    • /GL - Оптимизация всей программы
    • /Gy - Включает компоновку на уровне функций
    • /GS - Проверяет безопасность буфера
  • Препроцессор
    • /X - Пропускает стандартный каталог включаемых файлов
  • Язык
    • /std:c++14 Стандарт C++14
    • /std:c++17 Стандарт C++17
    • /std:c++20 Стандарт C++20
    • /std:c++latest Последний черновик стандартных предварительных версий функций C++.
    • /std:c11 Стандарт C11
    • /std:c17 Стандарт C17
  • Выходные файлы
    • /FA Создает файл содержащий код ассемблера
      • /FAc Включает машинный код в файл ассемблера
      • /FAs Включает исходный код в файл ассемблера
      • /FAcs Включает машинный код и исходный код в файл ассемблера

Линкер

Флаги

Все флаги можно найти здесь

Но основные, которые могут вам понадобиться это:

  • Ввод
    • /NODEFAULTLIB - Пропускает все (или только указанные) библиотеки по умолчанию при разрешении внешних ссылок
    • /MANIFESTUAC - Создает параллельный файл манифеста и при необходимости включает его в двоичный файл
      • /MANIFESTUAC:NO - Нет
  • Отладка
    • /DEBUG - Создает отладочную информацию
      • /DEBUG:NO - Нет
    • /MAP - Создает файл сопоставления
  • Система
    • /SUBSYSTEM - Сообщает операционной системе, как запустить исполняемый файл
      • /SUBSYSTEM:NATIVE - Машинный код
    • /DRIVER - Создает драйвер режима ядра
  • Оптимизация
    • /LTCG - Задает создание кода во время компоновки
    • /ORDER - Помещает секции COMDAT в образ в предопределенном порядке
  • Дополнительно
    • /ENTRY - Задает начальный адрес
    • /BASE - Задает базовый адрес для программы
    • /FIXED - Создает программу, которая может загружаться только по предпочтительному базовому адресу
    • /NXCOMPAT - Помечает исполняемый файл как файл, проверенный на совместимость с компонентом предотвращения выполнения данных Windows
    • /ALIGN - Задает выравнивание каждой секции

Соединение ассемблера(FASM) и MSVC

Линкер

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

Например секции идущие в коде в таком порядке с названиями .text$mn, .text$c, .text$a, .text$b. Будут рассортированы линкером как .text$a, .text$b, .text$c, .text$mn.

Если у вас свой загрузчик

Если вы используете собственный загрузчик, то необходимо в начале файла установить формат генерации объектного файла поддерживаемый линкером MSVC

Для x64

format MS64 COFF

Для x86

format MS COFF

Так нужно объявить символ _fltused, при использовании переменных с плавающей запятой.

public _fltused

Чтобы загрузчик был всегда первым в объектном файле, нужно объявить секцию с названием .text$a, а так же указать параметры code, readable, executable.

section '.text$a' code readable executable

А так же нужно создать файл например order.txt в проекте и передать его линкеру через флаг /ORDER order.txt. В этом файле нужно написать название точки входа в ядро допустим kernel_start. Это позволит в секции кода поместить точку входа в ядро на первое место.

После этого можно писать код как обычно.

Важно загрузить ядро по базовому адресу равному тому, что прописано в настройках линкера. Проблема в том что линкер разрешает базовые адреса кратные 0x10000 байтам, поэтому скорее всего вы загрузите ядро в пустую область до 1 Мб памяти, а потом скопируете его на адрес выше 1 Мб, т.е. базовый адрес в таком случае должен быть больше или равен 0x100000 и при этом кратен 0x10000. Учитывайте, что адрес kernel_start будет отличаться от базового адреса на размер PE заголовка + размер вашего загрузчика.

Для того чтобы скопировать ядро в нужный участок памяти и прыгнуть в ядро, необходимо объявить внешний символ на функцию входа в ядро kernel_start

extrn kernel_start

В коде C++ функция kernel_start должна быть объявлена с использованием extern "C"

extern "C" void kernel_start()
{
}

Далее нужно скопировать загруженное ядро по адресу равному kernel_start. Это можно сделать например таким кодом. Где KERNEL это адрес куда вы изначально загрузили ядро, а SIZEOFKERNELINSECTORS количество секторов занимаемых вашим ядром.

mov rsi, KERNEL
mov rdi, kernel_start
mov rcx, (SIZEOFKERNELINSECTORS * 0x200 / 8)
rep movsq

После чего вы можете поместить адрес функции kernel_start в регистр, например rdi, и вызвать функцию call kernel_start

mov rdi, kernel_start
call rdi

Дополнительно нужно передать флаг /ALIGN линкеру с значением кратным степени двойки, меньше или равным 512 байтам, чтобы линкер не раздул ваш загрузчик.

Окончательный файл ASM.

format MS64 COFF
public _fltused
extrn kernel_start
section '.text$a' code readable executable
use16
org 0x7c00
...
use64
startLongMode:
mov rsi, KERNEL
mov rdi, kernel_start
mov rcx, (SIZEOFKERNELINSECTORS * 0x200 / 8)
rep movsq
mov rdi, kernel_start
call rdi