Может ли компилятор создавать независимые от платформы бинарные файлы без системных вызовов и библиотечных вызовов?

Я мало что знаю о низкоуровневых вещах. Но, насколько я понимаю, компиляторы производят бинарники с системными и библиотечными вызовами внутри.

Если вы не делаете никаких системных вызовов и не вызываете какие-либо библиотеки, являются ли двоичные файлы просто необработанным машинным кодом, исполняемым без каких-либо изменений, когда вы находитесь на той же машине, но в другой ОС? Что еще связывает бинарники с их ОС? Можно ли написать кросс-платформенную ASM-программу «Hello world» без какого-либо системного вызова? Потребуется ли доступ к пространству ядра, следовательно, это невозможно?

Сам исполняемый формат отличается. Но шелл-код, распаковывающий полезную нагрузку, можно использовать, например, без модификации. Вообще говоря, вам нужен системный вызов, чтобы напечатать что-то в окне консоли. Наконец, разные ОС создают разные среды, и даже переносимые языки высокого уровня (например, Java, C#, Python) не всегда полностью переносимы из-за этого.

Margaret Bloom 29.04.2023 15:52

Вы знаете, почему программы выполняют системные и библиотечные вызовы?

fuz 29.04.2023 18:33

См. superuser.com/questions/1343849/… «двоичные файлы — это просто необработанная сборка» — нет, сборка по-прежнему является исходным кодом, а не исполняемым машинным кодом.

sawdust 30.04.2023 09:21

@sawdust Насколько я знаю, я исправил свой вопрос. Это было в основном неправильное использование языка

Arnaud Feldmann 30.04.2023 12:24
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
4
87
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Нет.

Исполняемые файлы содержат метаданные, а не только исходный машинный код, и разные ОС используют разные форматы. (Если у вас нет исполняемого файла 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, с более чем одним «ядерным кластером» (ССХ))

Спасибо за очень точный ответ, именно то, что я хотел. Я не мог бы спросить больше.

Arnaud Feldmann 29.04.2023 19:06

Другие вопросы по теме