что мешает, скажем, визуальному компилятору C ++ в Windows генерировать двоичный исполняемый файл linux?
Кроме нежелания делать это со стороны Microsoft, абсолютно ничего. Препятствия не являются техническими.
Наборы инструментов разработки - это просто программы, которые принимают и выводят результаты. Visual C ++ создает сборку x86, а затем использует ассемблер для преобразования ее в объектный файл COFF. Если Microsoft вместо этого хотела, чтобы она генерировала ELF, это всего лишь код: приходит сборка, ELF уходит. В объектных файлах или библиотеках нет ничего волшебного; это просто клочки данных в понятном формате.
Еще в каменном веке кросс-компиляция была намного сложнее, потому что чаще всего вы писали бы цепочку инструментов для вашей целевой платформы в сборке для платформы, на которой она будет работать. Это означало, что если бы в мире существовали только архитектуры VAX, M68K и Alpha, то для полного набора кросс-компиляторов потребовалось бы написать девять из них, в основном с нуля. (VAX-to-VAX, VAX-to-M68K, VAX-to-Alpha, M68K-to-VAX, M68K-to-M68K и т. Д.) Это немного преувеличено, поскольку части компилятора VAX могут быть повторно использованы и подключен к генераторам кода для каждой цели (например, VAX, M68K и Alpha, каждый написан для VAX.)
Эта проблема ушла, когда мы начали писать компиляторы на языке, который не был привязан к конкретному процессору, например, C. Такой путь означает, что вы пишете весь набор инструментов один раз в C и используете платформу, написанную для локальной платформы. Компилятор C, чтобы построить это. (Вы часто используете компилятор для перекомпиляции после того, как он был загружен на компиляторе локальной платформы, но это другое обсуждение.) Результатом этого является то, что сборка кросс-компилятора стала по существу такой же, как и сборка собственного компилятора на местная платформа. Единственным существенным отличием является то, что где-то в процессе сборки вы сказали, чтобы он компилировался в генераторе кода для вашей целевой платформы, а не для локальной платформы, что было бы логичным выбором.
По мере развития архитектуры компиляторов стало удобно просто включать и создавать все генераторы кода вместе с продуктом и выбирать, какой из них будет использоваться во время выполнения. Clang / LLVM делает это, и я уверен, что есть и другие.
Когда у вас есть рабочий набор инструментов (компилятор, ассемблер, компоновщик), библиотеки собираются из исходных кодов, и в итоге вы получаете все необходимое для создания исполняемого файла для какой-либо другой платформы.