Исходники Borland C++ 2.0 никогда не публиковались. Теперь у компилятора 1991 года есть исходный код, восстановленный из его же бинарника: если собрать его оригинальным тулчейном Borland, получается BCC.EXE, побайтово совпадающий с фирменным. Ниже перевод README проекта borland-reborn, описания метода и основной части списка находок: что пришлось угадывать про компилятор и его ключи, почему код местами написан «не как человек», и какие ошибки нашлись в самом Borland C++.
Примечание переводчика. Перевод собран из трёх документов репозитория: README, docs/HOW-IT-WORKS.md и bcc20/docs/FINDINGS.md (из последнего переведены первые три раздела, хвост с мелкими находками и заметками о переименованиях опущен). Оригинальных программ Borland в репозитории нет: для пересборки их дистрибутивы нужно скачать самому. Инструментарий Exhume, на котором сделана работа, по описанию его репозитория — набор инструментов и навыков (skills) для Claude Code, выросший из декомпиляции Ultima Underworld II.
borland-reborn
DOS-компиляторы Borland, пересобранные из их собственных бинарников.
Этот репозиторий восстанавливает исходный код классических компиляторов Borland по опубликованным исполняемым файлам. Цель — чтобы восстановленный код на C и ассемблере, собранный оригинальным тулчейном Borland, давал каждый исполняемый файл байт в байт. Потом этот код делается читаемым: настоящие имена, именованные поля записей, комментарии к каждому файлу и заметки о том, как компилятор работает и где он ошибается.
Каталог |
Программа |
Статус |
|---|---|---|
Borland C++ 2.0 (1991), |
Готово: пересобирается побайтово (SHA-256 |
|
Borland C++ 2.0 (1991), |
Готово: пересобирается побайтово (SHA-256 |
|
|
Начато: запись вызовов, стенд воспроизведения, первые функции рантайма |
Позже добавятся другие версии Borland и Turbo, каждая отдельным каталогом верхнего уровня, с общими scripts/ и инструментами сборки Exhume.
Borland C++ 2.0 в цифрах
298 исходных файлов в 15 каталогах подсистем: 265 файлов на C (Borland C++ 2.0 и Turbo C 2.01) и 33 модуля, написанных вручную на TASM. Разделение оригинала на скомпилированный C и ручной ассемблер сохранено: ассемблер ни разу не переписывали на C насильно, а скомпилированный код ни разу не оставляли ассемблером.
Каждое имя восстановлено или описательное: около 3100 функций, глобальных переменных и меток, 300 полей записей (
src/include/FIELDS.H), 40 раскладок записей (src/include/RECORDS.H), константы сообщений, токенов, видов типов и операторов.Каждый файл задокументирован: строка о назначении, обзор и комментарии к функциям (около 10 000 строк комментариев), карта исходников и описание того, как устроен компилятор.
Находки: вероятные ошибки в оригинале, дублированный код, мёртвый код и то, как компилятор Borland был устроен внутри (bcc20/docs/FINDINGS.md). Несколько ярких: парсер написан вручную, рекурсивным спуском, с парсером выражений по приоритету операторов (никакого yacc); слияние хвостов в генераторе кода определило, как пришлось писать исходники;
strrchrчитает байт перед пустой строкой; освобождённое место в файле предкомпилированных заголовков никогда не используется повторно.
Пересобрать самому
Нужны оригинальные программы Borland (в репозиторий они не входят, см. NOTICE.md). Полное руководство — в docs/SETUP.md, а если коротко:
git clone --recurse-submodules https://github.com/abedegno/borland-reborn cd borland-reborn # 1. download Borland C++ 2.0 and Turbo C 2.01 from WinWorld (links in docs/SETUP.md) python3 scripts/setup-toolchains.py --bc20 "Borland CPP 2.0 (1991) (3.5-720k).7z" --tc201 "<Turbo C 2.01>.7z" # 2. Python packages and the DOS emulator (dosbox-x), node python3 -m venv .venv && .venv/bin/pip install -r exhume/tools/requirements.txt # 3. generate the link inputs from your own BCC.EXE, then compile, verify and link everything cd bcc20 ../.venv/bin/python scripts/prepare.py ../.venv/bin/python ../exhume/tools/gate.py check --all
Последняя команда компилирует все 298 исходников в DOS, сверяет каждую функцию с оригиналом байт в байт, проверяет каждую релокацию и каждое имя и линкует новый BCC.EXE, идентичный борландовскому.
Чтобы менять сам компилятор, есть сборка для модификаций. Она линкует те же исходники с раскладкой, которую выбирает TLINK, поэтому любая функция может вырасти или уменьшиться, между любыми двумя элементами данных можно вставить новые данные, можно добавлять новые исходные файлы. Проверяется она так: 1836 компиляций прогоняются обоими компиляторами (среди них примеры программ самого Borland и старые тесты GCC и cfront), и все результаты сравниваются, причём код и данные сдвигаются несколькими способами, с новыми и изменёнными файлами. Трассирующий эмулятор показывает, какие слова данных компилятор использует как адреса и какую часть его кода тесты выполняют: 83% (bcc20/docs/COVERAGE.md). Подробности — в bcc20/docs/MODDING.md.
Метод (подбор компилятора и ключей, подгонка каждой функции, проверка того, чего не видит сравнение, линковка всей программы TLINK и проход на читаемость, всё под тем же побайтовым гейтом) описан в docs/HOW-IT-WORKS.md, его перевод ниже.
Исправления имён, комментариев и находок приветствуются, см. CONTRIBUTING.md. Собственные скрипты и документация проекта распространяются по лицензии MIT (LICENSE). Восстановленные исходники получены из защищённых авторским правом программ Borland, выложены для изучения и сохранения, никаких прав на них не заявляется (NOTICE.md).
Как это работает
Цель — исходный код, который пересобирает опубликованный исполняемый файл в точности: тот же компилятор, те же ключи, те же байты. Побайтово идентичный результат — единственная проверка, с которой не поспоришь. Если пересобранная программа равна оригиналу, исходник честно её описывает. Каждое последующее изменение, включая все переименования и комментарии, проверяется тем же тестом.
Работа сделана на Exhume, наборе инструментов для декомпиляции DOS-программ (и части прошивок) с побайтовым совпадением. Здесь описан метод в том виде, в каком он применялся к BCC.EXE из Borland C++ 2.0.
1. Определить тулчейн
BCC.EXE был собран самим Borland C++ 2.0: TASM 2.51 для ручных частей и TLINK 4.0 с борландовским оверлейным менеджером VROOMM. Функция совпадает только тогда, когда угаданы компилятор, его версия и все ключи (-mm -G -O -Z -p, плюс -Y для оверлейного кода). Правильность определения доказана компиляцией функции в те же байты. Позже выяснилось, что 29 модулей библиотеки времени выполнения собраны более старым компилятором, Turbo C 2.01: они копируют дальние указатели через es:bx и собирают dx:ax перед push, а BC 2.0 так никогда не делает.
2. Разметить программу
Исполняемый файл разбили на сегменты и оверлеи, затем на процедуры и, там, где улики показывали границы модулей, на исходные файлы оригинала. Уликами служили выравнивающие байты между модулями, порядок релокаций и имена сегментов. Для каждого куска составили целевую таблицу: какие байты оригинала должен воспроизвести этот исходный файл.
3. Добиться совпадения, по одному файлу
Для каждого файла: написать код на C (или на TASM), скомпилировать его в DOS оригинальным компилятором, сравнить каждую функцию с оригиналом, прочитать разницу в инструкциях и менять исходник, пока различий не останется. Как писать исходник, диктуют привычки компилятора. Например, BC 2.0 удаляет недостижимые переходы до того, как сливает общие хвосты кода, поэтому некоторые функции совпадают только с ранним return или с повторённым оператором в том месте, где их, по-видимому, и написал человек. Такие места помечены комментариями match:. Всё время соблюдались два правила:
Сохранять язык оригинала. Модули, написанные вручную на ассемблере, остаются на TASM. Модули, которые были скомпилированы из C, остаются на C, даже если вариант на ассемблере дался бы легче (
DVPRINTF.Cвосстановили так в самом конце).Никаких фокусов. Никакой ассемблерной вставки вместо скомпилированного кода, никаких патчей байтов. Где в оригинале явно был странный код (мёртвые присваивания, лишние проверки), исходник воспроизводит его и прямо об этом говорит.
4. Проверить то, чего не видит сравнение
Побайтовое сравнение маскирует места, которые заполнит релокация, поэтому verify проверяет каждое из них: что каждая внешняя ссылка разрешается в нужный символ по нужному адресу, что данные и BSS разложены так же, и что имена совпадают с глобальной таблицей символов (symbols.tsv). Эта таблица пересобирается с нуля при каждой полной проверке.
5. Слинковать всю программу
Все объектные файлы линкуются TLINK в новый BCC.EXE, вместе с оверлеями, и сравниваются с борландовским. Части программы, которые не являются скомпилированным кодом (стартовый код-клей, сегмент данных, обвязка оверлеев), живут в исходниках групп линковки (linkgroups/). Это ассемблерные файлы с исходной раскладкой. При линковке из них вырезаются области, которые теперь поставляют восстановленные исходники, а остальное ассемблируется. Финальная проверка — сравнение SHA-256 всего исполняемого файла.
6. Сделать код читаемым
Когда каждый байт доказан, исходник переписали для людей, шаг за шагом, с полной проверкой после каждого шага:
одна команда сборки и один гейт;
общие заголовки вместо объявлений в каждом файле;
именованные константы: сообщения, токены, виды типов и операторы, с именами из собственных таблиц BCC;
поля записей: около 300 имён полей для записей пулов BCC (
FIELDS.H), доступ к ним через общие функции (POOL.H) и 40 раскладок записей в виде структур (RECORDS.H);имена файлов и дерево каталогов по подсистемам;
комментарии к каждому файлу и каждой процедуре, плюс находки.
Имена никогда не угадывали. Для каждого записано основание (descriptive — описательное или inferred — выведенное) и улики, в файле bcc20/map/names.tsv. Имя говорит, что делает код, даже если его назначение так и осталось неясным (CODEGEN_UNUSED_POOL_ONE). Комментарии проверяли сравнением потока токенов с вырезанными комментариями, так что изменить код они не могут.
Что гарантирует гейт
gate.py check --all компилирует каждый исходник, сверяет и проверяет каждый, пересобирает таблицу символов, линкует и сравнивает исполняемый файл. Если проверка прошла, значит, исходники репозитория, собранные оригинальным тулчейном, дают в точности борландовский BCC.EXE. Как запустить это самому, описано в SETUP.md.
Находки
Что удалось узнать о BCC.EXE, пока его исходник восстанавливали и читали. У каждого пункта указана степень уверенности: подтверждено (есть второй свидетель: данные, таблица или сам путь исполнения), вероятно (ясно из кода, не проверялось), возможно (умозаключение). Пункты, найденные агентами, которые подбирали имена, там, где это отмечено, сверены с кодом.
Вероятные ошибки и странности (оставлены как есть: исходник воспроизводит оригинал)
strrchr читает перед строкой (подтверждено,
RSTRCHR.C): для пустой строки он смотрит наstring[-1]. Последствия: на практике безвредно, но порт не должен это копировать.strcmp возвращает разность байтов, расширенную по знаку через cbw (подтверждено,
RSCMP.C): знак неверен, когда два символа отличаются больше чем на 127. Последствия: имена сравниваются только на равенство, так что, скорее всего, безвредно.Процедура, которая вечно вызывает сама себя (вероятно):
CALL_SELF_FOREVER(RHAPI.C), заглушки кучиRH_SELF_CALL_*(их семь) иCALL_SELF_FOREVER_RG2BENDтолько вызывают сами себя, и никто не вызывает их. Вероятно, это заготовки в исходниках Borland, которые компилятор сохранил.Постинкремент битового поля через дальний указатель не компилируется (подтверждено запуском борландовского BCC.EXE,
tests/modbuild/BITFAIL.C):return f->count++;для битового поля структуры, до которой добираются через указатель, обрывает компиляцию с «Fatal: Register allocation failure» в моделях compact, large и huge, с-Oи без. Постдекремент падает так же. Преинкремент, модель small и глобальная структура компилируются. Последствия: такой код скомпилировать нельзя; порту воспроизводить этот отказ не нужно.Константы перечислений не работают во встроенном ассемблере C++ (подтверждено запуском борландовского BCC.EXE,
tests/modbuild/ASMENUM.CPP):asm mov cx, second, гдеsecond— элемент перечисления, в C++ обрывается с «Improper use of a typedef symbol», а в C проходит. ВENCODE_ASM_SYMBOL_ALT(ASMENC2.C, C+±близнец кодировщика имён для ассемблера) есть ветка, которая записывает значение элемента перечисления, но из-за этой ошибки до неё дело не доходит. Последствия: пишите число; порт мог бы это исправить.Вывод зависит от раскладки самой программы (вероятно, нашли внешние тесты сборки для модификаций): на двух старых тестах g++ из GCC компилятор Borland выдаёт результат, который меняется, когда сдвигается любой его код или данные. В
g++.other/bitfld3.C(-S), где?:между битовыми полями используется как lvalue, он записывает имя символа как@_из байтов, которые никогда не заполнял; вg++.law/access4.Cошибка доступа превращается в «Compiler table limit exceeded in function {». Добавление байтов-заполнителей в любую из пяти групп блоков кода из восьми меняет вывод, так что дело не в одном адресе, записанном как число. Вероятно, это чтение никогда не инициализированной памяти, например старой ячейки стека. Последствия: неверный текст на этих путях ошибок и листинга; оба файла исключены из сравнения (tests/modbuild/EXTERNAL.TSV).Файлы предкомпилированных заголовков содержат неинициализированные байты (подтверждено,
MACROS.C): MACRO_DEFINE округляет каждое определение макроса до чётного размера, байт выравнивания остаётся незаписанным.-Hпишет пул макросов вTCDEF.SYMв том виде, в каком он лежит в памяти. Поэтому в файле оказывается то, что раньше было в этой памяти кучи, а это зависит от раскладки памяти программы. Ещё файл хранит ближние адреса собственных данных BCC, поэтому подходит только тому компилятору, который его записал (tools/modtest.pyсравнивает его размер и проверяет, читая обратно). Последствия: безвредно, этот байт никогда не читается.Ключ -# использовать нельзя (подтверждено запуском BCC.EXE,
RARGV0F0.C): он читается до запуска менеджера памяти и задаёт размер кучи в параграфах, но затем разборщик опций его отвергает («Incorrect command line option»). С маленьким значением всё ломается раньше:-#60напечатал пустое «Error:». С-#1500компилятор в emu2 не завершился и через десять минут. Вероятно, это забытый отладочный ключ.Заглушки, возвращающие 0 (вероятно):
ASM_FLUSH_KIND2_STUB,ASM_COLON_LABEL_STUB, аRETURN_MINUS_ONE_STUBвозвращает -1.Объявление, которое теряет возвращаемое значение (подтверждено,
LEXER.C):RTL_SCAN_LDOUBLEобъявлена какvoid, хотя процедура (RSTRTOD.ASM) возвращает число прочитанных символов. Последствия: в BCC никаких; порт должен объявить её правильно.Код, который никто не вызывает (подтверждено поиском по src/ и linkgroups/): три десятичные обёртки в
RLTOA.ASM; обработчик директивы преамбулы вPCH.C, которого нет вPREAMBLE_DIRECTIVE_TABLE; девять пулов, которые создаётINIT_CODEGEN_POOLSи которые никогда не используются (CODEGEN_UNUSED_POOL_ONE…NINE).
Дублированный код (подтверждено: тела идентичны)
STAOV198(ASMDATA.C) иBCC_FAR_06A5(OBJSEGS.C).GET_TYPE_FLAGSиGET_TYPE_FLAGS_ALT(TYPEREC.C): обе возвращают байт по смещению record+8.FIND_INIT_BY_MEMBERиFIND_INIT_BY_BASE(CPPCTOR.C).RESET_EXPR_POOL(EXPRMAKE.C) иRESET_EXPR_POOL_ALT(EXPRPOOL.C).ADD_JUMP_18_NEXT_FRAGMENT(ASMDATA.C) иADD_JUMP_18_NEXT_FRAGMENT_ALT(OBJSEGS.C).Параллельные реализации одной и той же работы: кодировщики встроенного ассемблера в
ASMENC.CиASMENC2.C(имена с_ALT), просмотр символов в конце файлаSCAN_FILE_SYMBOLS(SYMSCAN.C) иSCAN_FILE_SYMBOLS_C(SYMSCANC.C), разборщики операторов для режимов C++ и C (STMTCPP.C,STMTC.C).
Как устроен движок
Никакого генератора парсеров (подтверждено): рекурсивный спуск для операторов и объявлений, приоритет операторов для выражений, восстановление после ошибок в «режиме паники» до синхронизирующих токенов; ни строк, ни таблиц yacc. См. SUBSYSTEMS.md.
Две раскладки записей для типов (вероятно):
TYPEREC.Cчитает целевой тип по смещению +3 и флаги по +7/+8, аTYPESIZE.CиспользуетTYPE_TARGET(+7) и байт 12. Имена их разводят (TYPE_SIZE/SIZE_OF_TYPE_CHECKED).Банки кучи (вероятно): обычная память, файл подкачки, EMS, расширенная память и пустой банк, у каждого свои обработчики; узлы, которые переносятся между банками, блокируются флагом 8000h (
LOCK_HEAP_NODE).Предкомпилированные заголовки (подтверждено сигнатурой файла):
PCH.C, сигнатура 0xEDFE, версия 6;#pragma hdrfileиhdrstop; директивы преамбулы сохраняются и воспроизводятся заново.Оптимизации генератора кода определили вид исходника (подтверждено): BC 2.0 удаляет недостижимые переходы до слияния хвостов, поэтому переходы, оставшиеся после слияния, выживают. Чтобы совпали последние функции, понадобились ранние return, повторённые операторы и отдельные операторы
return, как в оригинале.Таблица обработчиков фронтенда (подтверждено): фронтенд C копирует 196-байтовую таблицу (
C_FRONTEND_HOOK_TABLE) поверх слотов обработчиков по адресу DS:80AC, так что один и тот же бэкенд обслуживает C и C++ через указатели на функции (*_HOOK).Проверки битов флагов (вероятно,
CGEXPR.C):FLAGS_BIT_TEST_JUMP_KINDпревращает(x & 2^n) != 0в прямой условный переход для n = 0, 2, 6, 7, 11 — это биты CF, PF, ZF, SF и OF.Статические локальные переменные с конструкторами (вероятно,
STMTCPP.C):GUARD_STATIC_LOCAL_INITоборачивает инициализатор в проверку новой переменной-флага и регистрирует деструктор, который выполняется, только если флаг установлен.Встроенный ассемблер (
INLINASM.ASM): записи его таблицы опкодов выбирают кодировщик по номеру формы (ASM_ENCODE_*); выходы по ошибкеASM_FAIL_*соответствуют сообщениям, которые выдаётEMIT_ASM_STATEMENT.dvprintf был скомпилированным C (подтверждено совпадением):
DVPRINTF.C— это C от Turbo C 2.01. Его цикл написан через goto: шаг и проверка стоят внутри switch после последнего case. Тот же цикл в виде оператора for сdefault: returnна 4 байта длиннее, поэтому этот модуль и оставался на TASM.%xот 0 не печатает ничего.На TASM остаётся то, что писали руками: вспомогательные процедуры плавающей точки (циклы на флаге переноса вроде
shr si,1 / jae, проталкивание аргументов, общее для двух вызовов, 64-битная мантисса в четырёх регистрах), встроенный ассемблер, оверлейный менеджер и стартовый код.Раскладки записей (подтверждено при подборе имён полей, src/include/FIELDS.H): пул типов содержит две раскладки, фронтенда C++ и 10-байтовые записи фронтенда C, где те же элементы лежат на 4 байта ниже; в обеих массив хранит тип элемента по смещению 0x07. Флаг функции 4 означает функцию с переменным числом аргументов (EXPRPARS.C принимает
...в выражении только в этом случае, как того требует va_start). «Узлы кода» генератора — это блоки графа потока управления, операции — 4-байтовые кортежи, операнды которых лежат под тем же индексом в параллельном пуле, а символы фронтенда C живут в пуле, который раньше назывался пулом операторов.
Дальше в оригинале идут мелкие находки, сделанные при комментировании; автор помечает их как вероятные, прочитанные из побайтово совпавшего кода (например, SEARCHP читает байт за концом значения PATH без ; в конце; memmove выбирает направление копирования, сначала сравнивая сегменты, так что перекрывающиеся блоки обрабатываются только в пределах одного сегмента), исправленные вводящие в заблуждение имена и открытые вопросы. Их можно прочитать в FINDINGS.md.
Комментарии (21)

rPman
14.11.2026 06:28Интересна легальность этого процесса, ведь если удалось сделать это с одним, то можно сделать и с другими.
Про то что это сделано с помощью ИИ информации мало, косвенно про скилы и claude в исходном репозитории

Siemargl
14.11.2026 06:28Как правило, декомпиляция запрещена.

rPman
14.11.2026 06:28исторически был способ это обойти через серую зону - метод чистой комнаты
одна команда декопмпилирует, объясняет функционал другой команде, которая реализует... серость в том что те что декомпилируют могут находиться в иной юрисдикции.
до ИИ метод был достаточно дорогим, что бы им активно не пользовались, но с ИИ картина становится совсем иной.

Siemargl
14.11.2026 06:28Какая разница. Кто декомпилирует, тот и нарушает.
Второй половине команды тоже может прилететь за соучастие в нарушении.
Липа это все.

solus_factor
14.11.2026 06:28Эх, такое бы для MSVC 6.0...

NeoCode2
14.11.2026 06:28Именно для самого компилятора? Помню среда разработки была шустрая, но что там особенного в компиляторе?
А вообще такое бы для очень много какого софта надо:) Включая саму винду.

solus_factor
14.11.2026 06:28Да это по-моему был пик компиляторостроения :) По соотношению умности к bloat. Новый переписанный с нуля VS еще долго не мог его догнать по скорости, и я так и не узнал, догнал-таки или нет, потому что перестал им пользоваться...
Добавить бы шестому поддержку плюшек современного железа и идеально было бы. А то достало, что с каждой новой версией компилятора размер бинарников всё растет и растет.

solus_factor
14.11.2026 06:28Кстати, про винду, надеюсь ReactOS не игнорируют достижения ИИ :)

ross-s
14.11.2026 06:28OS/2 в открытой реинкарнации в виде osFree, как по мне, более интересная эволюционная ветка

AbuMohammed
14.11.2026 06:28Откуда такая ностальгия?) ice в нем было инициировать после прочтения Александреску было как два пальца об асфальт) уже от 2002й был гораздо стабильнее.

Siemargl
14.11.2026 06:28Для VS6 вышло 6 сервис паков. Даже сама МС долго с неё не слезала.
Вроде до сих пор в Виндовс от неё dll-ки живут, msvcrt.dll и mfc42
NeoCode2
Прекрасные проекты! Может кто-то скажет что зачем восстанавливать древние компиляторы, но по сути это лишь тренировка - теперь любой сколь угодно закрытый и проприетарный софт можно будет восстановить и распространять под GPL. И не обязательно побайтово, но побайтовое совпадение на некотором этапе - отличный способ контроля правильности, а дальше можно и баги исправлять, и фичи добавлять.
Кстати нейросети похоже позволили воплотить одну мою старую идею: декомпилятор с обратной компиляцией в режиме патчинга. То есть декомпилируем по типу hexrays, меняем в этом декомпилированном исходном коде что-то по минимуму, или добавляем пару функций, и полученный измененный код компилируем обратно - причем неизмененные части можно брать оригинальные прямо из старого бинарника. Это потребовало бы особого софта, где декомпилятор и компилятор являются единым программным продуктом, а декомпилированный код и бинарник были бы связаны в единый формат хранения. Сейчас с нейросетями получилось честнее (хотя и менее оптимально) - полная декомпиляция и полная компиляция.
slinkinone
Не получится. Нельзя просто взять чужой код и выставить его под открытой лицензией. Правообладатели просто выставят претензию и предоставят доказательства что код был написан ими. Выиграют суд, и все что было декомпилировано не сможет быть использовано ни в каком более менее серьезном проекте.
Q3_Results
Никто не мешает опубликовать код, похожий на код внутри проприетарного бинарника, если этот же код делает то же самое, что и исходный бинарник, а может даже лучше и эффективнее
slinkinone
Мешают патенты, лицензия и права на интеллектуальную собственность.
Такой код просто будут удалять с крупных платформ (GitHub, GitLab, …), а разработчики не будут заинтересованы в развитии такого проекта.
Ananist
Можно если делать методом чистой комнаты. Берёшь 1 агента и он по декомпиляции описывает как работать код. 2 агент по документации пишет новый код. На выходе программа с таким же функционалом но по факту совершенно новая и без чужого кода. Юристам не до чего докопаться
BugM
Если ваш софт неотличим от коммерческого софта, то как вы его сделали всем все равно. Суд, тюрьма и миллионы штрафа.
dartraiden
А потом окажется, что второй агент сжульничал, как все эти истории о моделях OpenAI, когда модель то взламывала их внутренний механизм, который оценивал её работу, чтобы он ставил ей хорошие оценки, то, вместо того, чтобы честно искать решение, выбирается обходными путями в интернет и взламывает сторонние сервисы.
И при нехватке инфы, сгенерированной первым агентом, второй чего-нибудь там отреверсит сам...
А ещё бывают запатентованные алгоритмы. Как в MP3. Существовал кодек LAME под свободной лицензией, но его использование для сжатия звука всё равно требовало уплаты денег держателям патентов. Потому что платить надо было за сам факт реализации этих запатентованных алгоритмов. По этой же причине кодек в FFMpeg и всяких линуксах лежал в отдельном репозитории. Мол, дорогой пользователь, если ты это себе сознательно подключаешь, то ты тем самым подтверждаешь, что сам утрясёшь вопрос с держателями патентов и получишь от них разрешение.