COM-файл — это простейший исполняемый файл эпохи MS-DOS: образ программы, который операционная система загружает в память «как есть», без заголовков, таблиц и служебных структур. Вся программа — это один непрерывный блок машинного кода и данных размером не более 64 КБ минус небольшая служебная область. Именно эта предельная простота сделала формат популярным в 1980-е годы, а затем же и погубила его: когда программы выросли, а процессоры стали многозадачными и защищёнными, ограничения COM-файла превратились из особенности в непреодолимый барьер.
В этой статье разберём, как устроен COM-файл изнутри, чем он отличается от EXE, какие преимущества давал программисту, где формат ещё может встретиться сегодня и почему он практически исчез.
- Как устроен COM-файл
- Ограничение в 64 килобайта
- Чем COM отличается от EXE
- Почему COM был быстрее и проще
- Почему формат почти исчез
- 1. Потолок в 64 КБ стал неприемлемым
- 2. Защищённый режим и многозадачность
- 3. Отсутствие метаданных
- 4. Проблема безопасности
- 5. Смена инструментов разработки
- Где COM-файлы ещё встречаются
- Можно ли создать COM-файл сегодня
- Практические выводы
Как устроен COM-файл
Формат COM настолько минималистичен, что его можно описать одним предложением: файл — это точная копия того, что окажется в памяти. Никакого заголовка, никакой метаинформации, никаких полей с точкой входа. Загрузчик DOS читает содержимое файла и записывает его в память начиная с определённого смещения, после чего передаёт управление самому первому байту.
Последовательность загрузки выглядит так:
- DOS находит свободный сегмент памяти размером 64 КБ (65536 байт).
- В первые 256 байт этого сегмента (страницу PSP, Program Segment Prefix) записывается служебная информация: командная строка, окружение, таблица файловых дескрипторов.
- Сразу за PSP, со смещения 100h, размещается содержимое файла.
- Все сегментные регистры указывают на начало этого сегмента, а регистр команд передаёт управление на адрес 100h.
- Программа начинает выполняться с первого своего байта.
Отсюда следует характерное правило ассемблерного программирования под 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) и знание нескольких правил:
- Указать базовое смещение 100h, учитывая страницу PSP.
- Не превышать суммарный объём кода, данных и стека примерно в 65 КБ.
- Явно инициализировать сегментные регистры, если программа будет менять их значения.
- Для завершения использовать функцию 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-файл заключал этот договор в самом примитивном виде, и именно поэтому он был быстрым, крошечным — и обречённым, как только программы переросли один сегмент памяти.
