2010-07-18 16 views
33

Estoy tratando de averiguar si puedo compilar una biblioteca estática que oculte todos sus objetos y funciones internas, etc., excepto las interfaces que deseo exportar. Estoy experimentando con Xcode (gcc 4.2).Ocultación de símbolos en bibliotecas estáticas compiladas con Xcode/gcc

He utilizado el atributo __attribute__((visibility("hidden"))) en algunas clases de C++ por this documentation. También definí pequeñas funciones de C auxiliares como locales de archivo (estáticas), etc.

Sin embargo, cuando ejecuto strings en el archivo de biblioteca .a resultante, incluso cuando se compila en la configuración de Release, aún veo los nombres de mis clases ostensiblemente ocultas, con sus nombres de métodos, e incluso los nombres de las funciones locales de archivos esparcidos por allí también.

He añadido -fvisibility=hidden e incluso -fno-rtti a las banderas gcc. Si bien esto reduce algunas de las cadenas, los nombres de las clases, los nombres de los métodos y los nombres de las funciones estáticas siguen estando ahí en forma simple o mutilada, pero legible.

¿Hay alguna manera confiable de hacer que el compilador construya esto sin tener los nombres de las cadenas de todo el material interno emitido en el binario? No debería ser necesario tenerlo para ningún cliente externo que se vincule.

(Para aclarar: estoy preguntando acerca de la ofuscación de nomenclatura interna, frente a las necesidades de enlace de exportación literales. Estoy desconcertado de que todo el funcionamiento interno sea visible a través del comando strings, independientemente de si estos símbolos se exportan formalmente o no).

Gracias.

+2

Si RTTI está activado, todos los nombres de las clases van a ser incorporados sin importar que, con el fin de hacer 'type_info :: nombre)' trabajo (. Puedes intentar recompilar con '-fno-rtti' y ver si hace la diferencia. Por supuesto, si está utilizando 'dynamic_cast' en cualquier lugar, esto no funcionará. – Thomas

+0

Gracias por la pista. Intenté esto, pero todos los nombres todavía están allí en forma destrozada. –

+0

Realmente debería pensar en volver a adjudicar la respuesta, ya que la de Bleater no requiere ningún engaño. No solo eliminó la mayoría de mis nombres, sino que redujo en gran medida el tamaño de la biblioteca que necesito compartir. –

Respuesta

36

Ocultar nombres internos requiere algunas configuraciones simples de compilación de Xcode, y generalmente no es necesario modificar la fuente o cambiar el tipo de producto creado.

  1. Elimine cualquier símbolo interno requerido entre módulos mediante la realización de un enlace previo de un solo objeto. Establezca la configuración de compilación de Xcode llamada "Realizar prelink de un solo objeto" en Sí (GENERATE_MASTER_OBJECT_FILE = YES). Esto hace que ld se ejecute con el indicador "-r".
  2. Asegúrese de que la configuración "Estilo de banda" esté configurada como "Símbolos no globales" (STRIP_STYLE = no global), esto pasa "-x" a ld.
  3. Stripping solo se realiza realmente en bibliotecas estáticas si está habilitado el postprocesamiento (y este no es el predeterminado). Establezca la configuración de compilación de Xcode "Deptoyment Postprocessing" en yes. (DEPLOYMENT_POSTPROCESSING = YES). También asegúrese de que "Usar tira separada" esté configurado a Sí (no siempre el predeterminado) (SEPARATE_STRIP = YES).
  4. Si, además de los símbolos locales, si necesita eliminar algunos de los símbolos globales, puede proporcionar opciones adicionales al comando strip, bajo la configuración de construcción de Xcode "Banderas de bandas adicionales". P.ej. Normalmente utilizo la opción strip "-R somefile" para proporcionar un archivo con una lista adicional de símbolos que deseo eliminar de la tabla de símbolos global.
+0

¡Excelente! Acabo de hacer lo que sugeriste con Xcode 5.1 Beta, y funcionó perfectamente. Justo lo que necesitaba. –

+6

Sugeriría agregar 5. a la lista anterior: Establezca "SymbolsHiddenByDefult" en YES. (Está en la sección Generación de código). Es equivalente a -fvisibilidad = oculto. El paso 1..4 me ayudó a eliminar cadenas, pero el paso 5. también oculta los métodos no públicos, las variables estáticas y los búferes. pregunta – DanielHsH

+0

@DanielHsH del OP indicó que él ya se había controlado la visibilidad símbolo, pero eso sí, su sugerencia es una buena idea para otros casos en los que el control explícito aún no ha sido puesto en marcha. Sin embargo, vale la pena probar que el producto final se puede vincular correctamente (en una sección dinámica o ejecutable), ya que existe la posibilidad de que las personas que llaman puedan estar inadvertidamente vinculadas a símbolos privados. – bleater

12

El truco principal en la ocultación de símbolos dentro de las bibliotecas estáticas es generar un archivo de objeto reubicable (a diferencia de un archivo de biblioteca estática que simplemente consiste en una colección de archivos .o individuales). Para crear un archivo de objeto reubicable, debe elegir su destino en XCode como paquete (a diferencia de "Biblioteca estática Cocoa Touch"). El objetivo Bundle aparece debajo de las plantillas OS X, y puede establecer su destino en iOS en la configuración de compilación si está compilando para iOS.

Una vez que haya configurado su destino correctamente, los siguientes pasos le ayudarán a obtener el símbolo correcto ocultar:

  1. Establecer los "símbolos ocultos por defecto" opción en Sí en la configuración de generación. Esto asegura que todos los símbolos compilados en los archivos estén marcados como privados.

  2. Como se trata de una biblioteca, es necesario mantener algunos símbolos públicos. Debe poner código para las funciones que desea mantener visibles públicamente en archivos separados y compilar esos archivos con el indicador -fvisibility=default (puede establecer este indicador para archivos individuales "Fases de compilación> Compilar fuentes> - Indicadores del compilador" en Xcode). Alternativamente, puede poner un prefijo al nombre de la función/clase que desea que sea visible con la directiva __attribute__((visibility("default"))).

  3. En la configuración de enlace en el proyecto de código X, configure el tipo de Mach-O en "Archivo de objetos reubicables". Lo que esto significa es que todos los archivos .o se volverán a vincular para generar un solo archivo de objeto. Este es el paso que ayuda a marcar todos los símbolos como privados cuando los archivos .o están vinculados en un solo archivo. Si construye una biblioteca estática (es decir, un archivo .a), este paso de vinculación no se realiza para que los símbolos nunca se oculten. Por lo tanto, elegir un archivo objeto reubicable como su objetivo es fundamental.

  4. Incluso después de marcar los símbolos como privados todavía aparecen en el archivo .o.Debe habilitar la extracción para deshacerse de los símbolos privados. Esto puede hacerse configurando la configuración "Producto vinculado eliminado" en Sí en la configuración de compilación. Al establecer esta opción, se ejecuta el comando strip -x en el archivo del objeto que elimina los símbolos privados del archivo del objeto.

  5. Comprueba nuevamente que todos los símbolos internos hayan desaparecido ejecutando el comando nm en el último archivo de objeto reubicable generado por el proceso de compilación.

Los pasos anteriores le ayudarán a deshacerse de los nombres de símbolos del comando nm. Todavía verá algunos nombres de funciones y archivos si ejecuta el comando strings en su archivo de objeto (debido a algunas cadenas y nombres de objetos compilados en excepciones). Uno de mis colleagues tiene un script que cambia el nombre de algunos de estos símbolos mirando en las secciones binarias y renombrando esas cadenas. Lo publiqué aquí para que lo use: https://gist.github.com/varungulshan/6198167. Puede agregar esta secuencia de comandos como un paso de construcción adicional en Xcode.

+0

Gracias por la respuesta, lo hicimos, pero ahora no podemos ejecutarlo en un simulador, no enlaza. solo funciona en el dispositivo. El error que estamos consiguiendo es: LD: Error de aserción: (átomo-> fixupCount() == 1), la función targetClassName, presente /SourceCache/ld64/ld64-136/src/ld/parsers/macho_relocatable_file.cpp, línea 4973. clang: error: el comando del enlazador falló con el código de salida 1 (use -v para ver la invocación). ¿Encontraste este problema? Gracias – Yoav

+0

Hola Yoav, Perdón por la respuesta tardía. No, nunca probé esto en el simulador, por lo que es posible que se rompa el enlace allí. Uno de nuestros ingenieros ha ideado un método aún mejor (escribió al script para eliminar todos los símbolos visibles localmente escaneando las secciones binarias). Lo publicaré pronto. –

+0

OK, ¿puedes publicarlo o enviarlo a mi correo? Todavía tenemos problemas con eso ... – Yoav

0

que es un poco claro para mí cómo ocultar los símbolos en las bibliotecas estáticas desde el entorno de línea de comandos de Linux basado en las respuestas anteriores así que voy a publicar aquí mi solución para la posteridad (dado que esto es una de las los mejores resultados en google para esa pregunta).

Digamos que usted tiene estos dos archivos .c:

// f1.c 
const char *get_english_greeting(void) 
{ 
    return "hello"; 
} 

__attribute__((visibility("default"))) 
const char *get_greeting(void) 
{ 
    return get_english_greeting(); 
} 

y

// f2.c 
#include <stdio.h> 
const char *get_english_greeting(void); 

__attribute__((visibility("default"))) 
void print_greeting(void) 
{ 
    puts(get_english_greeting()); 
} 

Desea convertir estos dos archivos en una biblioteca estática exportar tanto get_greeting y print_greeting pero no get_english_greeting que se No quiero hacer estática ya que te gustaría usarla en toda tu biblioteca.

Éstos son los pasos para lograr que:

gcc -fvisibility=hidden -c f1.c f2.c 
ld -r f1.o f2.o -o libf.o 
objcopy --localize-hidden libf.o 
ar rcs libf.a libf.o 

Ahora bien, esto funciona:

// gcc -L. main.c -lf 
void get_greeting(void); 
void print_greeting(void); 
int main(void) 
{ 
    get_greeting(); 
    print_greeting(); 
    return 0; 
} 

Y esto no es así:

// gcc -L. main.c -lf 
const char *get_english_greeting(void); 
int main(void) 
{ 
    get_english_greeting(); 
    return 0; 
} 

Para este último obtiene este error :

/tmp/ccmfg54F.o: In function `main': 
main.c:(.text+0x8): undefined reference to `get_english_greeting' 
collect2: error: ld returned 1 exit status 

que es lo que queremos.

Tenga en cuenta que los nombres de los símbolos ocultos son todavía visibles en la biblioteca estática, sino que el enlazador se niegan a enlazar con ellos fuera de dicha biblioteca estática. Para eliminar por completo los nombres de los símbolos, deberá quitarlos y ofuscarlos.

Cuestiones relacionadas