В информатике исполняемый код , исполняемый файл или исполняемая программа , иногда просто называемая исполняемым файлом или двоичным файлом , заставляет компьютер «выполнять указанные задачи в соответствии с закодированными инструкциями » [2], в отличие от файла данных , который должен быть интерпретирован ( анализирован ) интерпретатором , чтобы быть функциональным. [3]
Точная интерпретация зависит от использования. «Инструкции» традиционно означают инструкции машинного кода для физического процессора . [4] В некоторых контекстах файл, содержащий инструкции скрипта (например, байт-код ), также может считаться исполняемым.
Исполняемые файлы могут быть вручную закодированы на машинном языке, хотя гораздо удобнее разрабатывать программное обеспечение в виде исходного кода на языке высокого уровня , который может быть легко понятен людям. В некоторых случаях исходный код может быть указан на языке ассемблера , который остается понятным человеку, но тесно связан с инструкциями машинного кода.
Язык высокого уровня компилируется либо в исполняемый файл машинного кода, либо в неисполняемый машинный код — объектный файл некоторого вида; эквивалентный процесс в исходном коде на языке ассемблера называется сборкой . Несколько объектных файлов связываются для создания исполняемого файла. Объектные файлы — исполняемые или нет — обычно хранятся в контейнерном формате , таком как Executable and Linkable Format (ELF) или Portable Executable (PE), который является специфичным для операционной системы . [5] Это дает структуру сгенерированному машинному коду, например, разделяя его на разделы, такие как .text (исполняемый код), .data (инициализированные глобальные и статические переменные) и .rodata (данные только для чтения, такие как константы и строки).
Исполняемые файлы обычно также включают в себя систему времени выполнения , которая реализует функции языка времени выполнения (такие как планирование задач , обработка исключений , вызов статических конструкторов и деструкторов и т. д.) и взаимодействие с операционной системой, в частности, передачу аргументов, окружения и возврат статуса выхода , вместе с другими функциями запуска и завершения работы, такими как освобождение ресурсов, таких как дескрипторы файлов . Для C это делается путем присоединения объекта crt0 , который содержит фактическую точку входа и выполняет настройку и завершение работы путем вызова библиотеки времени выполнения . [6]
Исполняемые файлы, таким образом, обычно содержат значительный дополнительный машинный код, выходящий за рамки того, что напрямую сгенерирован из конкретного исходного кода. В некоторых случаях желательно опустить это, например, для разработки встраиваемых систем или просто для понимания того, как работают компиляция, компоновка и загрузка. В C это можно сделать, опустив обычную среду выполнения и вместо этого явно указав скрипт компоновщика, который генерирует точку входа и обрабатывает запуск и завершение работы, например, вызывая main
start и возвращая статус выхода ядру в конце. [7]
Для того, чтобы быть выполненным системой (например, операционной системой , прошивкой [ требуется цитата ] или загрузчиком ), исполняемый файл должен соответствовать двоичному интерфейсу приложения системы (ABI). В простых интерфейсах файл выполняется путем загрузки его в память и перехода к началу адресного пространства и выполнения оттуда. [8] В более сложных интерфейсах исполняемые файлы имеют дополнительные метаданные, указывающие отдельную точку входа . Например, в ELF точка входа определяется в e_entry
поле заголовка, которое указывает (виртуальный) адрес памяти, с которого следует начать выполнение. [9] В коллекции компиляторов GNU это поле устанавливается компоновщиком на основе _start
символа. [10]