COM-файл: что это такое и почему формат почти исчез из практики

COM-файл — это простейший исполняемый файл эпохи MS-DOS: образ программы, который операционная система загружает в память «как есть», без заголовков, таблиц и служебных структур. Вся программа — это один непрерывный блок машинного кода и данных размером не более 64 КБ минус небольшая служебная область. Именно эта предельная простота сделала формат популярным в 1980-е годы, а затем же и погубила его: когда программы выросли, а процессоры стали многозадачными и защищёнными, ограничения COM-файла превратились из особенности в непреодолимый барьер.

В этой статье разберём, как устроен COM-файл изнутри, чем он отличается от EXE, какие преимущества давал программисту, где формат ещё может встретиться сегодня и почему он практически исчез.

Как устроен COM-файл

Формат COM настолько минималистичен, что его можно описать одним предложением: файл — это точная копия того, что окажется в памяти. Никакого заголовка, никакой метаинформации, никаких полей с точкой входа. Загрузчик DOS читает содержимое файла и записывает его в память начиная с определённого смещения, после чего передаёт управление самому первому байту.

Последовательность загрузки выглядит так:

  1. DOS находит свободный сегмент памяти размером 64 КБ (65536 байт).
  2. В первые 256 байт этого сегмента (страницу PSP, Program Segment Prefix) записывается служебная информация: командная строка, окружение, таблица файловых дескрипторов.
  3. Сразу за PSP, со смещения 100h, размещается содержимое файла.
  4. Все сегментные регистры указывают на начало этого сегмента, а регистр команд передаёт управление на адрес 100h.
  5. Программа начинает выполняться с первого своего байта.

Отсюда следует характерное правило ассемблерного программирования под DOS: любая COM-программа начиналась с директивы org 100h, потому что первые 256 байт сегмента занимает PSP, а не код. Забытая директива означала, что процессор начнёт исполнять служебные данные вместо программы — результат был непредсказуем.

Ограничение в 64 килобайта

Ключевое ограничение формата вытекает из архитектуры реального режима процессоров Intel 8086 и 8088. Адрес в этих процессорах вычисляется как пара «сегмент:смещение», где смещение — 16-битное число. Один сегмент покрывает ровно 64 КБ, а COM-программа обязана целиком помещаться в один сегмент: код, данные и стек живут в общем адресном пространстве размером 65536 байт, из которых 256 байт съедает PSP.

На практике доступный объём был ещё меньше. Стек по умолчанию располагался в самом конце сегмента, и при старте программы в него обычно записывали слово 0 — чтобы переполнение стека затёрло нулевой сегмент и привело к аварийному завершению, а не к порче данных. Итого полезного пространства оставалось порядка 65 КБ минус стек, что для серьёзного приложения уже в середине 1980-х было тесно.

Чем COM отличается от EXE

EXE-формат появился одновременно с COM, но решал противоположную задачу: дать программам больше памяти и гибкости ценой усложнения структуры файла.

Характеристика COM EXE
Заголовок файла Отсутствует Обязателен, минимум 28 байт
Максимальный размер Около 65 КБ минус служебные области Ограничен только доступной памятью DOS
Сегменты Один общий для кода, данных и стека Несколько, настраиваются загрузчиком
Точка входа Всегда первый байт (смещение 100h) Указывается в заголовке
Релокация (настройка адресов) Не требуется Таблица релокаций обрабатывается при загрузке
Скорость загрузки Максимальная Чуть ниже из-за обработки заголовка
Расширение как признак формата Да, определяет способ загрузки Да, определяется по сигнатуре MZ

Важный нюанс, который часто упускают: расширение имени файла для DOS — не просто ярлык, а указание способа загрузки. Если переименовать работающий EXE в .com, он перестанет запускаться, потому что загрузчик попытается исполнить заголовок «MZ» как машинный код. Обратное тоже верно: COM-файл, переименованный в .exe, не запустится, поскольку у него нет корректного заголовка.

Почему COM был быстрее и проще

При загрузке COM-файла системе не нужно ничего вычислять: нет таблицы релокаций, нет настройки сегментов, нет поиска точки входа. Файл копируется в память и запускается. Для медленных дискет и слабых процессоров начала 1980-х это давало ощутимую экономию времени и памяти — сам файл не содержал ни одного лишнего байта.

Для программиста на ассемблере COM-модель была ещё и концептуально проще: одно адресное пространство, все обращения через один сегмент, не нужно думать о настройке сегментных регистров. Маленькие утилиты, резидентные программы (TSR), загрузчики и демки писались именно в этом формате.

Почему формат почти исчез

Исчезновение COM-файлов — не результат чьего-то решения, а закономерное следствие развития платформы. Можно выделить несколько причин, каждая из которых по отдельности делала формат всё менее пригодным, а вместе они сделали его бесперспективным.

1. Потолок в 64 КБ стал неприемлемым

Уже к концу 1980-х типичное приложение — текстовый редактор, электронная таблица, база данных — требовало сотни килобайт кода и данных. Программы начали обходить ограничение оверлеями (подгрузкой частей кода с диска по мере необходимости) и расширениями памяти, но это были костыли, усложнявшие разработку. EXE-формат снимал проблему естественно: несколько сегментов, работа со всей доступной памятью.

2. Защищённый режим и многозадачность

Процессоры начиная с Intel 80286 получили защищённый режим, а Windows и другие многозадачные среды построили на нём свою работу. В защищённом режиме модель «один сегмент на всю программу» потеряла смысл: сегменты стали описываться дескрипторами, память распределялась диспетчером ОС, а прямое управление сегментными регистрами приложению запрещалось. COM-модель физически не вписывалась в эту архитектуру.

3. Отсутствие метаданных

Современные исполняемые форматы хранят информацию, без которой невозможны привычные функции: целевую архитектуру, зависимости от библиотек, ресурсы (иконки, строки интерфейса), цифровую подпись, контрольные суммы. У COM-файла нет даже заголовка — системе неоткуда взять эти сведения. Проверить, является ли файл действительно программой, а не случайным набором байт, можно только запустив его — со всеми вытекающими рисками.

4. Проблема безопасности

Модель «исполняется всё, что в файле» идеально подходила вирусам. Первые массовые компьютерные вирусы для PC заражали именно COM-файлы: дописать свой код в конец файла и поправить первый байт на переход к себе мог даже начинающий автор вредоносной программы. EXE-формат заражать тоже умели, но структура с заголовком и таблицей релокаций хотя бы позволяла антивирусам надёжнее восстанавливать оригинальный код.

5. Смена инструментов разработки

Компиляторы C, Pascal и последующих языков ориентировались на модели памяти с несколькими сегментами и генерировали EXE. Поддержка COM сохранилась в основном в ассемблерах и Tiny-режимах компиляторов, то есть стала нишевой возможностью, а не основным путём.

Где COM-файлы ещё встречаются

Несмотря на исчезновение из mainstream-разработки, формат не умер полностью:

  • Ретро-вычисления и демосцена. Написание крошечных программ на 256 байт или меньше — отдельный жанр, и COM здесь идеален: файл буквально совпадает с образом в памяти.
  • Образцы вирусов и их анализ. Исторические образцы вредоносного кода 1980–1990-х годов изучаются исследователями безопасности, и многие из них — COM-файлы.
  • Встроенные системы и загрузчики. Принцип «плоский бинарный образ, исполняемый с фиксированного адреса» живёт во встраиваемой разработке, хоть и без расширения .com.
  • Эмуляторы DOS. DOSBox и аналогичные эмуляторы полноценно поддерживают запуск COM-программ, что позволяет работать со старым ПО.
  • Обучающие материалы по ассемблеру. Из-за предельной простоты формат до сих пор удобен для первых шагов в низкоуровневом программировании.

Отдельно стоит упомянуть ложных тёзок. Расширение .com сегодня прочно ассоциируется с доменами первого уровня, а в Windows существовали (и частично существуют) системные компоненты вроде command.com — интерпретатора командной строки DOS-подсистемы. Это не обязательно классические COM-образы: некоторые такие файлы внутри являются EXE с изменённым расширением. То есть само расширение .com давно перестало быть надёжным признаком формата.

Можно ли создать COM-файл сегодня

Технически — да, и это несложно. Понадобится ассемблер (например, NASM) и знание нескольких правил:

  1. Указать базовое смещение 100h, учитывая страницу PSP.
  2. Не превышать суммарный объём кода, данных и стека примерно в 65 КБ.
  3. Явно инициализировать сегментные регистры, если программа будет менять их значения.
  4. Для завершения использовать функцию DOS (например, через прерывание 21h) либо команду RET при правильно настроенном стеке.

Запустить такой файл напрямую современная Windows не даст: подсистема NTVDM, обеспечивавшая выполнение DOS-программ в 32-битных версиях, из 64-битных выпусков удалена. Практический путь — эмулятор DOSBox или виртуальная машина с DOS/FreeDOS. На Linux аналогичную роль играют эмуляторы вроде DOSEMU (актуальность конкретных проектов стоит проверять на дату обращения).

Практические выводы

COM-формат — наглядный пример того, как техническое решение, идеально соответствующее своей эпохе, становится препятствием при смене условий. Его история полезна не только как ретроспектива:

  • Простота имеет цену. Отказ от заголовка дал мгновенную загрузку, но лишил систему любой информации о программе — от точки входа до зависимостей.
  • Ограничения платформы диктуют форматы. 16-битная сегментная адресация 8086 прямо породила и потолок 64 КБ, и саму модель COM.
  • Совместимость требует явных описаний. Современные форматы (PE в Windows, ELF в Unix) выросли именно из потребности хранить метаданные, а не только код.

Если вам встретился файл с расширением .com из старого архива, разумный порядок действий такой: сначала проверить его происхождение и целостность антивирусом — исторические COM-файлы нередко оказываются носителями старых вирусов; затем запускать только в изолированной среде, например в DOSBox или виртуальной машине, а не пытаться исполнить в основной системе. Если цель — изучить код, проще разобрать файл дизассемблером: отсутствие заголовка означает, что весь файл, начиная с первого байта, является машинным кодом.

Главный принцип, который стоит удержать из этой истории: формат исполняемого файла — это договор между программой и системой о том, как её читать и запускать. COM-файл заключал этот договор в самом примитивном виде, и именно поэтому он был быстрым, крошечным — и обречённым, как только программы переросли один сегмент памяти.

PEFile.ru