embedded - software - arm developer
Proceso para reducir el tamaƱo de un ejecutable (8)
Lista general:
- Asegúrese de que tiene desactivadas las opciones de depuración del compilador y el enlazador
- Compilar y vincular con todas las opciones de tamaño activadas (-Os en gcc)
- Ejecutar
strip
en el ejecutable - Genere un archivo de mapa y verifique los tamaños de su función. Puede obtener su enlazador para generar su archivo de mapa (
-M
cuando usa ld), o puede usar objdump en el ejecutable final (tenga en cuenta que esto solo funcionará en un ejecutable sin trincar). Esto realmente no solucionará el problema, pero te hará saber de los peores delincuentes. - Use
nm
para investigar los símbolos que se invocan desde cada uno de los archivos de objeto. Esto debería ayudar a encontrar las funciones de llamada a las que no desea llamar.
En la pregunta original había una pregunta secundaria sobre incluir solo funciones relevantes. gcc
incluirá todas las funciones dentro de cada archivo de objeto que se use. Para decirlo de otra manera, si tiene un archivo de objeto que contiene 10 funciones, las 10 funciones están incluidas en su ejecutable, incluso si realmente se llama a uno 1.
Las bibliotecas estándar (por ejemplo, libc) dividirán las funciones en muchos archivos de objetos separados, que luego se archivan. El ejecutable se vincula luego al archivo. Al dividir en muchos archivos de objetos, el vinculador solo puede incluir las funciones que realmente se llaman. (esto supone que estás enlazando estáticamente)
No hay ninguna razón por la cual no puedas hacer el mismo truco. Por supuesto, podría argumentar que si las funciones no se llaman así, probablemente pueda eliminarlas usted mismo.
Si está enlazando estáticamente con otras bibliotecas, puede ejecutar las herramientas enumeradas anteriormente sobre ellas para asegurarse de que sigan reglas similares.
Estoy produciendo un archivo hexadecimal para ejecutar en un procesador ARM que quiero mantener por debajo de 32 K. Actualmente es mucho más grande que eso y me preguntaba si alguien podría tener algún consejo sobre cuál es el mejor enfoque para adelgazarlo.
Esto es lo que hice hasta ahora
- Así que he ejecutado ''tamaño'' para determinar qué tan grande es el archivo hexadecimal.
- Luego ''tamaño'' de nuevo para ver qué tan grande es cada uno de los archivos del objeto para crear los archivos hexadecimales. Parece que la mayoría del tamaño proviene de bibliotecas externas.
- Luego usé ''readelf'' para ver qué funciones ocupan más memoria.
- Busqué en el código para ver si podía llamar a esas funciones.
Aquí es donde me quedo atascado, hay algunas funciones a las que no llamo directamente (por ejemplo, _vfprintf) y no puedo encontrar lo que se llama para poder eliminar la llamada (ya que creo que no la necesito).
Así que cuales son los siguientes pasos?
gracias por tu ayuda.
Respuesta a las respuestas:
- Como puedo ver, se llaman funciones que ocupan mucha memoria. No obstante, no puedo encontrar lo que se llama.
- ¡Quiero omitir esas funciones (si es posible) pero no puedo encontrar lo que las llama! Podría ser llamado desde cualquier número de funciones de la biblioteca, supongo.
- El enlazador funciona como se desea, creo, solo incluye los archivos de biblioteca relevantes. ¿Cómo se sabe si solo se están incluyendo las funciones relevantes? ¿Puedes establecer una bandera o algo por eso?
- Estoy usando GCC
Ok, al final simplemente reduje el proyecto a su forma más simple, y poco a poco agregué los archivos uno por uno hasta que la función que quería eliminar aparecía en el archivo ''readelf''. Luego, cuando tuve el archivo, comenté todo y volví a agregar cosas lentamente hasta que la función apareció nuevamente. Así que al final descubrí qué lo llamaba y quité todas esas llamadas ... Ahora funciona como se desea ... ¡dulce!
Sin embargo, debe ser una mejor manera de hacerlo.
Otra optimización que podría ahorrarle trabajo es -secciones de funciones, -Wl, -gc-sections, suponiendo que esté usando GCC. Sin embargo, no será necesario contar una buena cadena de herramientas.
Explicación: GNU ld vincula secciones, y GCC emite una sección por unidad de traducción a menos que usted indique lo contrario. Pero en C ++, los nodos en el gráfico de dependencia son objetos y funciones.
Podrías mirar algo así como la compresión ejecutable .
Solo para verificar y documentar para referencia futura, pero ¿usa las instrucciones de Thumb? Son versiones de 16 bits de las instrucciones normales. A veces puede necesitar 2 instrucciones de 16 bits, por lo que no ahorrará el 50% en el espacio del código.
Un enlazador decente debería tomar solo las funciones necesarias. Sin embargo, es posible que necesite configuraciones de compilador y linke para las funciones del paquete para el enlace individual.
En proyectos profundamente integrados siempre trato de evitar el uso de cualquier función de biblioteca estándar. Incluso funciones simples como "strtol ()" explotan el tamaño binario. De ser posible, simplemente evite esas llamadas.
En la mayoría de los proyectos profundamente integrados no es necesario un "printf ()" versátil o asignación de memoria dinámica (muchos controladores tienen 32kb o menos de RAM).
En lugar de simplemente usar "printf ()" uso un "printf ()" personalizado muy simple, esta función solo puede imprimir números en formato hexadecimal o decimal no más. La mayoría de las estructuras de datos son preasignadas en tiempo de compilación.
Andrew EdgeCombe tiene una gran lista, pero si realmente quieres raspar hasta el último byte, sstrip es una buena herramienta que falta en la lista y puede ralentizar algunos kB más.
Por ejemplo, cuando se ejecuta en la strip
, puede reducir ~ 2kB .
De un antiguo archivo README (vea los comentarios en la parte superior de este archivo fuente indirecto):
sstrip es una pequeña utilidad que elimina los contenidos al final de un archivo ELF que no forman parte de la imagen de memoria del programa.
La mayoría de los ejecutables ELF se crean con una tabla de encabezado de programa y una tabla de encabezado de sección. Sin embargo, solo se requiere el primero para que el sistema operativo cargue, enlace y ejecute un programa. sstrip intenta extraer el encabezado ELF, la tabla de encabezado del programa y su contenido, dejando todo lo demás en el segmento de bits. Solo puede eliminar partes del archivo que se producen al final, después de las partes que se guardarán. Sin embargo, esto casi siempre incluye la tabla de encabezado de sección y ocasionalmente algunas secciones aleatorias que no se usan cuando se ejecuta un programa.
Tenga en cuenta que debido a la información que elimina, se rumorea que un ejecutable sstrip''d tiene problemas con algunas herramientas. Esto se discute más en los comentarios de la fuente.
Además ... para una lectura entretenida / loca sobre cómo hacer el ejecutable más pequeño posible, vale la pena leer este artículo .
Para responder a esta necesidad específica:
• ¡Quiero omitir esas funciones (si es posible) pero no puedo encontrar lo que las llama! Podría ser llamado desde cualquier número de funciones de la biblioteca, supongo.
Si desea analizar su base de código para ver quién llama qué, por quién se llama una función determinada y cosas por el estilo, hay una gran herramienta llamada "Entender C" proporcionada por SciTools.
Lo he usado muy a menudo en el pasado para realizar análisis de código estático. Realmente puede ayudar a determinar el árbol de dependencia de la biblioteca. Permite navegar fácilmente hacia arriba y hacia abajo en el árbol de llamadas, entre otras cosas.
Proporcionan una evaluación de tiempo limitado, luego debe comprar una licencia.