Al construir el firmware de los sistemas integrados para que se ejecute directamente desde la ROM, a menudo evitaré nombrar el punto de entrada main()
para enfatizar a un revisor de código la naturaleza especial del código. En estos casos, estoy suministrando una versión personalizada del módulo de inicio C runtime, por lo que es fácil reemplazar su llamada a main()
con otro nombre como BootLoader()
.
I (o mi proveedor) casi siempre tenemos que personalizar el inicio del tiempo de ejecución de C en estos sistemas porque no es inusual que la RAM requiera un código de inicialización para que comience a funcionar correctamente. Por ejemplo, los chips DRAM típicos requieren una cantidad sorprendente de configuración de su hardware de control, y a menudo requieren un retraso sustancial (miles de ciclos de bus) antes de que sean útiles.Hasta que eso se complete, puede que ni siquiera haya un lugar para colocar la pila de llamadas, por lo que es posible que el código de inicio no pueda llamar a ninguna función. Incluso si los dispositivos RAM están operativos al encenderse, casi siempre hay cierta cantidad de hardware de selección de chips o un FPGA o dos que requieren inicialización antes de que sea seguro dejar que el tiempo de ejecución de C inicie su inicialización.
Cuando un programa escrito en C carga e inicia, algunos componentes son responsables de que exista el entorno en el que se llama main()
. En Unix, Linux, Windows y otros entornos interactivos, gran parte de ese esfuerzo es una consecuencia natural del componente del sistema operativo que carga el programa. Sin embargo, incluso en estos entornos hay una cierta cantidad de trabajo de inicialización antes de llamar al main()
. Si el código es realmente C++, puede haber una cantidad sustancial de trabajo que incluye llamar a los constructores para todas las instancias de objetos globales.
Los detalles de todo esto son manejados por el enlazador y sus archivos de configuración y control. El enlazador ld (1) tiene un archivo de control muy elaborado que le dice exactamente qué segmentos incluir en la salida, en qué direcciones y en qué orden. Encontrar el archivo de control del enlazador que está usando implícitamente para su cadena de herramientas y leerlo puede ser instructivo, al igual que el manual de referencia para el enlazador mismo y el estándar ABI que sus ejecutables deben seguir para poder ejecutar.
Editar: Para responder más directamente a la pregunta como se pregunta en un contexto más común: "¿Se puede llamar a foo en lugar de main?" La respuesta es "Tal vez, pero solo por ser complicado".
En Windows, un archivo ejecutable y una DLL tienen casi el mismo formato de archivo. Es posible escribir un programa que carga una DLL arbitraria nombrada en tiempo de ejecución, y localiza una función arbitraria dentro de ella, y la llama. Uno de estos programas en realidad se envía como parte de una distribución estándar de Windows: rundll32.exe
.
Dado que un archivo .EXE puede cargarse e inspeccionarse mediante las mismas API que manejan archivos .DLL, en principio, si .EXE tiene una sección de EXPORTACIONES que nombra la función foo
, se podría escribir una utilidad similar para cargar y invocarlo. No necesita hacer nada especial con main
, por supuesto, ya que ese será el punto de entrada natural. Por supuesto, el tiempo de ejecución de C que se inicializó en su utilidad puede no ser el mismo tiempo de ejecución de C que se vinculó con su ejecutable. (Google para "DLL Hell" para la sugerencia.) En ese caso, su utilidad podría necesitar ser más inteligente. Por ejemplo, podría actuar como un depurador, cargar el EXE con un punto de interrupción en main
, ejecutar hasta ese punto de interrupción, luego cambiar la PC para apuntar ao en foo
y continuar desde allí.
Algún tipo de engaño similar podría ser posible en Linux ya que los archivos .so también son similares en algunos aspectos a los ejecutables verdaderos. Ciertamente, el enfoque de actuar como un depurador podría funcionar.
¿Por qué necesitarías hacer eso alguna vez? – Oded
En C++, un ctor de un objeto estático global puede ejecutarse antes que main(). – seand
Para reescribir la pregunta de Oded: díganos qué quiere * lograr * y le diremos cómo lograrlo, probablemente sin eludir 'principal'. (Más específicamente: algo así como. Mi falta de conocimiento en C me impide ayudarlo.) – MvanGeest