Я мало что знаю о низкоуровневых вещах. Но, насколько я понимаю, компиляторы производят бинарники с системными и библиотечными вызовами внутри.
Если вы не делаете никаких системных вызовов и не вызываете какие-либо библиотеки, являются ли двоичные файлы просто необработанным машинным кодом, исполняемым без каких-либо изменений, когда вы находитесь на той же машине, но в другой ОС? Что еще связывает бинарники с их ОС? Можно ли написать кросс-платформенную ASM-программу «Hello world» без какого-либо системного вызова? Потребуется ли доступ к пространству ядра, следовательно, это невозможно?
Вы знаете, почему программы выполняют системные и библиотечные вызовы?
См. superuser.com/questions/1343849/… «двоичные файлы — это просто необработанная сборка» — нет, сборка по-прежнему является исходным кодом, а не исполняемым машинным кодом.
@sawdust Насколько я знаю, я исправил свой вопрос. Это было в основном неправильное использование языка





Нет.
Исполняемые файлы содержат метаданные, а не только исходный машинный код, и разные ОС используют разные форматы. (Если у вас нет исполняемого файла DOS .com; этот формат не имеет метаданных и просто загружается в память с фиксированным смещением относительно любого сегмента, который выбирает ОС.) Это позволяет вам получить полезное сообщение об ошибке (вместо того, чтобы запускать его и получать ошибку сегментации ) при попытке запустить исполняемый файл x86-64 FreeBSD на x86-64 OpenBSD или Linux, даже если все они используют один и тот же формат (ELF).
Или пусть ОС прозрачно вызывает эмулятор на нем, например, как macOS на оборудовании AArch64 может запускать двоичные файлы x86-64 через Rosetta или Linux вызывает QEMU через binfmt-misc, вместо того, чтобы загружать машинный код для неправильной архитектуры и иметь ошибку ЦП с ошибкой. незаконная инструкция или ошибка страницы или что-то еще довольно скоро.
Hello World необходимо сделать системный вызов write или MessageBoxA, эквивалентный вызову WinAPI, или что-то подобное. ABI системных вызовов зависят от ОС.
например в Linux x86-64 mov eax, 1 / syscall — это write системный вызов. В MacOS x86-64 mov eax, 0x2000004/syscall. И это обе Unix-подобные ОС; другие, такие как Windows, даже не имеют стабильного ABI для системных вызовов; единственный переносимый (в разных версиях Windows) способ сделать что-либо за пределами вашего собственного процесса — это вызвать функции DLL, и это зависит от специфичных для Windows метаданных в вашем исполняемом файле.
Предполагая, что у вас есть портативный способ получить блок машинного кода для правильной работы ISA, вы можете переносимым образом выполнять некоторую обработку чисел в бесконечном цикле или что-то в этом роде, но передача результатов вне вашего процесса требует системного вызова. (Предполагая, что современная основная ОС достойна этого имени, т. Е. Которая обеспечивает защиту памяти и многозадачность, поэтому, в отличие от MS-DOS, не позволяет процессам пользовательского пространства напрямую обращаться к какому-либо оборудованию.)
Даже выход в переносном режиме является проблемой; обычно это делается с помощью системного вызова выхода, поэтому зависит от ОС. Но если ваш процесс даст сбой (например, незаконная инструкция, доступ к неверному адресу или попытка запустить привилегированную инструкцию), ОС уничтожит ваш процесс. Поскольку вы не будете делать более ранние системные вызовы для настройки обработчика или чего-то еще.
Так что нет, не Hello World; все, что вы можете сделать без системных вызовов, — это изменить свою собственную память; основные операционные системы не запускают процессы с какими-либо специальными областями памяти, отображаемыми в памяти на что-либо, видимое извне.
Вы можете обмениваться данными (извлекать данные) через побочный канал, такой как температура процессора, счетчики производительности или влияние производительности на другие процессы (например, пропускная способность памяти или скорость удаления данных из общего кэша L3, либо зацикливание на большом объеме пространства стека). или нет). Различные комбинации команд нагревают ЦП больше, чем меньше; у вас нет возможности перевести процесс в спящий режим (для этого потребуется системный вызов), но цикл, выполняющий инструкции x86 pause, будет потреблять гораздо меньше энергии, чем блок инструкций vmulpd ymm0, ymm1, ymm2. (256-битные умножения с плавающей запятой или FMA, вероятно, еще более энергоемкие.)
Это, конечно, не может заставить «Hello World» появиться в консоли, если вы не запускаете другой процесс, который часто проверяет температуру процессора и собирает вывод по несколько бит за раз. Эта программа, конечно, должна будет выполнять системные вызовы.
Но если вы не заботитесь о том, чтобы «Hello World» попало в консоль, и просто хотите передать данные какому-то другому процессу, который также не выполняет никаких системных вызовов, он, возможно, мог бы запускать временные тесты, рассчитанные с использованием x86 rdtsc или что-то, возможно, микробенчмарк, который зависит от попаданий в кэш L3, и производитель данных может либо запустить цикл, который записывает большой кусок пространства стека, либо не затрагивает память. Может быть, тратить 1 миллиард тиков TSC на бит вывода, чтобы дать читателю время увидеть, что он отправляет. (Вы можете придумать протоколы для любых данных, которые вы действительно хотите отправить. Эта идея будет работать для любой пары ядер ЦП, которые совместно используют кеш L3, поэтому большинство ЦП для настольных ПК / ноутбуков, за исключением некоторых Ryzen, с более чем одним «ядерным кластером» (ССХ))
Спасибо за очень точный ответ, именно то, что я хотел. Я не мог бы спросить больше.
Сам исполняемый формат отличается. Но шелл-код, распаковывающий полезную нагрузку, можно использовать, например, без модификации. Вообще говоря, вам нужен системный вызов, чтобы напечатать что-то в окне консоли. Наконец, разные ОС создают разные среды, и даже переносимые языки высокого уровня (например, Java, C#, Python) не всегда полностью переносимы из-за этого.