Los archivos ejecutables pueden codificarse a mano en lenguaje máquina, aunque es mucho más conveniente desarrollar software como código fuente en un lenguaje de alto nivel que pueda ser fácilmente comprendido por los humanos. En algunos casos, el código fuente puede especificarse en lenguaje ensamblador, que permanece legible para el ser humano mientras está estrechamente asociado con instrucciones de código máquina.
El lenguaje de alto nivel se compila en un archivo de código máquina ejecutable o en un archivo de objeto de código máquina no ejecutable de algún tipo; el proceso equivalente en el código fuente del lenguaje ensamblador se llama ensamblado. Varios archivos objeto están vinculados para crear el ejecutable. Los archivos objeto, ejecutables o no, generalmente se almacenan en un formato contenedor, como el Formato Ejecutable y Enlazable (ELF) o el Ejecutable portátil (PE), que es específico del Sistema Operativo. Esto da estructura al código de máquina generado, por ejemplo, dividiéndolo en secciones como.texto (código ejecutable), .datos (variables globales y estáticas inicializadas), y .rodata (datos de solo lectura, como constantes y cadenas).
Los archivos ejecutables normalmente también incluyen un sistema de tiempo de ejecución, que implementa características de lenguaje de tiempo de ejecución (como programación de tareas, manejo de excepciones, llamadas a constructores y destructores estáticos, etc.).) e interacciones con el sistema operativo, en particular pasar argumentos, entorno y devolver un estado de salida, junto con otras características de inicio y apagado, como liberar recursos como manejadores de archivos. Para C, esto se hace enlazando en el objeto crt0, que contiene el punto de entrada real y configura y apaga llamando a la biblioteca de tiempo de ejecución.
Por lo tanto, los archivos ejecutables normalmente contienen código de máquina adicional significativo más allá del generado directamente a partir del código fuente específico. En algunos casos, es deseable omitir esto, por ejemplo para el desarrollo de sistemas embebidos, o simplemente para entender cómo funcionan la compilación, el enlace y la carga. En C, esto se puede hacer omitiendo el tiempo de ejecución habitual, y en su lugar especificando explícitamente un script enlazador, que genera el punto de entrada y maneja el inicio y el apagado, como llamar a main
para iniciar y devolver el estado de salida al núcleo al final.