what makes it difficult for say the visual C++ compiler on windows to generate a linux binary executable file?
Oltre alla riluttanza a farlo da parte di Microsoft, assolutamente nulla. Gli ostacoli non sono tecnici.
I toolchain di sviluppo sono solo programmi che prendono input e producono output. Visual C ++ produce l'assembly x86 e quindi usa un assemblatore per convertirlo in un file oggetto COFF. Se Microsoft volesse farlo generare ELF, è solo un codice: l'assembly entra, ELF si spegne. Non c'è nulla di magico nei file di oggetti o nelle librerie; sono solo blocchi di dati in un formato ben compreso.
All'inizio dell'età della pietra, la compilazione incrociata era molto più difficile perché, molto spesso, avresti dovuto scrivere la catena di strumenti per la piattaforma di destinazione in assembly per la piattaforma su cui sarebbe stata eseguita. Ciò significava che se tutto quello che c'era al mondo fossero le architetture VAX, M68K e Alpha, una suite completa di compilatori incrociati richiederebbe la scrittura di nove di essi, principalmente da zero. (Da VAX a VAX, da VAX a M68K, da VAX a Alpha, da M68K a VAX, da M68K a M68K, ecc.) È un po 'esagerato dal momento che parti del compilatore VAX possono essere riutilizzate e collegato a generatori di codice per ciascun target (ad esempio, VAX, M68K e Alpha, ciascuno scritto per VAX.)
Questo problema scomparve quando iniziammo a scrivere compilatori in una lingua che non era legata a un processore specifico, come ad esempio C. Andando su quella strada significa che scrivi l'intera toolchain una volta in C e usi una scrittura per il compilatore C della piattaforma locale per costruirlo. (Si usa spesso il compilatore per ricompilare se stesso dopo che è stato riavviato nel compilatore della piattaforma locale, ma questa è un'altra discussione.) La conclusione di questo è che la costruzione di un cross-compiler è diventato essenzialmente lo stesso sforzo di creare un compilatore nativo su la piattaforma locale. L'unica differenza significativa è che da qualche parte nel processo di compilazione, gli hai detto di compilare il generatore di codice per la tua piattaforma di destinazione invece di quella per la piattaforma locale, che sarebbe stata la scelta logica. GCC ha fatto (e continua a farlo) costruendo un binario per coppia di piattaforme locali / target.
Con l'evoluzione dell'architettura dei compilatori, è diventato utile includere e creare semplicemente tutti i generatori di codice con il prodotto e selezionare quale viene utilizzato in fase di esecuzione. Clang / LLVM fa questo, e sono sicuro che ce ne sono altri.
Una volta che hai una toolchain funzionante (compilatore, assemblatore, linker), le librerie si costruiscono da fonti e alla fine si finisce con tutto ciò che serve per produrre un file eseguibile per qualche altra piattaforma.