¿Qué hace ENABLE_BITCODE en xcode 7?


262

Tengo un problema con el término de código de bits incrustado.
¿Qué es el código de bits incrustado?
¿Cuándo habilitar, ENABLE_BITCODEen el nuevo Xcode?
¿Qué le sucede al binario cuando está habilitado, ENABLE_BITCODEen Xcode 7?

Respuestas:


312

Bitcode se refiere al tipo de código: "LLVM Bitcode" que se envía a iTunes Connect. Esto le permite a Apple usar ciertos cálculos para volver a optimizar las aplicaciones (por ejemplo: posiblemente reducir el tamaño de los archivos ejecutables). Si Apple necesita alterar su ejecutable, puede hacerlo sin que se cargue una nueva compilación.

Esto difiere de: Slicing, que es el proceso de Apple para optimizar su aplicación para el dispositivo de un usuario en función de la resolución y la arquitectura del dispositivo. Rebanar no requiere Bitcode. (Ej: solo incluye imágenes @ 2x en un 5s)

App Thinning es la combinación de corte, código de bits y recursos a pedido

Bitcode es una representación intermedia de un programa compilado. Las aplicaciones que cargue en iTunes Connect que contengan código de bits se compilarán y vincularán en la App Store. La inclusión de código de bits permitirá a Apple volver a optimizar el binario de su aplicación en el futuro sin la necesidad de enviar una nueva versión de su aplicación a la tienda.

Documentación de Apple sobre adelgazamiento de aplicaciones


Nada de lo que ha citado dice que habilitar el código de bits disminuye el tamaño de la aplicación en el dispositivo del usuario. Bitcode no tiene nada que ver con recursos como 3x o 2x.
user102008

1
Una vez más, los recursos no tienen nada que ver con Bitcode, que se trata del código. La descarga por parte de los usuarios de ciertas arquitecturas de código y ciertas versiones de recursos es Slicing, que no tiene nada que ver con Bitcode.
user102008

77
No estoy de acuerdo con que le permita a Apple reducir el tamaño de su aplicación. En ninguna parte dice eso. Dice "permitirá a Apple volver a optimizar su aplicación binaria en el futuro sin la necesidad de enviar una nueva versión de su aplicación a la tienda", lo que quiero decir que le permite a Apple volver a compilar su aplicación para una nueva arquitectura si sale un nuevo dispositivo con una nueva arquitectura, sin necesidad de enviar una nueva versión que incluya esta arquitectura.
user102008

2
No, segmentar es separar los recursos de sus aplicaciones en grupos para dispositivos particulares. Bitcode es lo que le permite a Apple generar un ejecutable para una arquitectura particular.
Jon Shier

2
@JonShier Apple dice "Rebanar es el proceso de crear y entregar variantes del paquete de la aplicación para diferentes dispositivos de destino. Una variante contiene solo la arquitectura ejecutable y los recursos que se necesitan para el dispositivo de destino". para cierto dispositivo.
keji

80

¿Qué es el código de bits incrustado?

Según los documentos :

Bitcode es una representación intermedia de un programa compilado. Las aplicaciones que cargue en iTunes Connect que contengan código de bits se compilarán y vincularán en la App Store. La inclusión de código de bits permitirá a Apple volver a optimizar el binario de su aplicación en el futuro sin la necesidad de enviar una nueva versión de su aplicación a la tienda.

Actualización: esta frase en "Nuevas características en Xcode 7" me hizo pensar durante mucho tiempo que Bitcode es necesario para cortar para reducir el tamaño de la aplicación:

Cuando archiva para enviar a la App Store, Xcode compilará su aplicación en una representación intermedia. La App Store compilará el código de bits en los ejecutables de 64 o 32 bits según sea necesario.

Sin embargo eso no es verdad, código binario y rebanar trabajo de forma independiente: rebanar se trata de reducir el tamaño de aplicación y la generación de paquete de aplicación variantes, y código binario es acerca de ciertas optimizaciones binarios. He verificado esto comprobando las arquitecturas incluidas en los ejecutables de aplicaciones que no son de código de bits y descubriendo que solo incluyen las necesarias.

Bitcode permite que otro componente de reducción de aplicaciones llamado Slicing genere variantes de paquetes de aplicaciones con ejecutables particulares para arquitecturas particulares, por ejemplo, la variante de iPhone 5S incluirá solo el ejecutable arm64, iPad Mini armv7, etc.

¿Cuándo habilitar ENABLE_BITCODE en el nuevo Xcode?

Para las aplicaciones de iOS, el código de bits es el predeterminado, pero opcional. Si proporciona código de bits, todas las aplicaciones y marcos en el paquete de aplicaciones deben incluir código de bits. Para las aplicaciones watchOS y tvOS, se requiere un código de bits.

¿Qué le sucede al binario cuando ENABLE_BITCODE está habilitado en el nuevo Xcode?

De la referencia Xcode 7:

La activación de esta configuración indica que el destino o proyecto debe generar código de bits durante la compilación para plataformas y arquitecturas que lo soportan. Para las compilaciones de archivo, el código de bits se generará en el binario vinculado para enviarlo a la tienda de aplicaciones. Para otras compilaciones, el compilador y el enlazador comprobarán si el código cumple con los requisitos para la generación de código de bits, pero no generará un código de bits real.

Aquí hay un par de enlaces que ayudarán a una comprensión más profunda de Bitcode :


¿Se incluirá el código de bits si tengo ENABLE_BITCODE pero desmarco "Incluir código de bits" antes de enviarlo a la App Store?
allaire

"Para las aplicaciones de iOS, el código de bits es el predeterminado, pero opcional". ¿Eh ..? Llegar de nuevo..? ¿Es O no es opcional?
NpC0mpl3t3

@ NpC0mpl3t3 como se indica en la respuesta, es opcional para las aplicaciones de iOS, pero se requiere para las aplicaciones watchOS y tvOS.
Maxim Pavlov

Excelente ayuda! Esta respuesta aquí muestra cómo deshabilitar el código de bits: stackoverflow.com/a/41418824/9190
Guerry

20

Dado que la pregunta exacta es "¿qué hace el código de bits habilitado", me gustaría dar algunos detalles técnicos que he descubierto hasta ahora. La mayor parte de esto es prácticamente imposible de entender con 100% de certeza hasta que Apple publique el código fuente de este compilador

Primero, el código de bits de Apple no parece ser lo mismo que el código de bytes LLVM. Al menos, no he podido encontrar ningún parecido entre ellos. Parece tener un encabezado propietario (siempre comienza con "xar!") Y probablemente algo de magia de referencia de tiempo de enlace que evita las duplicaciones de datos. Si escribe una cadena codificada, esta cadena solo se colocará en los datos una vez, en lugar de dos veces como se esperaría si fuera un código de bytes LLVM normal.

En segundo lugar, el código de bits no se envía realmente en el archivo binario como una arquitectura separada como podría esperarse. No se envía de la misma manera que digamos x86 y ARM se colocan en un binario (archivo FAT). En su lugar, usan una sección especial en el binario MachO específico de la arquitectura llamada "__LLVM" que se envía con cada arquitectura compatible (es decir, duplicada). Supongo que esto es un inconveniente con su sistema de compilación y puede corregirse en el futuro para evitar la duplicación.

Código C (compilado con clang -fembed-bitcode hi.c -S -emit-llvm):

#include <stdio.h>

int main() {
    printf("hi there!");
    return 0;
}

Salida IR LLVM:

; ModuleID = '/var/folders/rd/sv6v2_f50nzbrn4f64gnd4gh0000gq/T/hi-a8c16c.bc'
target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-apple-macosx10.10.0"

@.str = private unnamed_addr constant [10 x i8] c"hi there!\00", align 1
@llvm.embedded.module = appending constant [1600 x i8] c"\DE\C0\17\0B\00\00\00\00\14\00\00\00$\06\00\00\07\00\00\01BC\C0\DE!\0C\00\00\86\01\00\00\0B\82 \00\02\00\00\00\12\00\00\00\07\81#\91A\C8\04I\06\1029\92\01\84\0C%\05\08\19\1E\04\8Bb\80\10E\02B\92\0BB\84\102\148\08\18I\0A2D$H\0A\90!#\C4R\80\0C\19!r$\07\C8\08\11b\A8\A0\A8@\C6\F0\01\00\00\00Q\18\00\00\C7\00\00\00\1Bp$\F8\FF\FF\FF\FF\01\90\00\0D\08\03\82\1D\CAa\1E\E6\A1\0D\E0A\1E\CAa\1C\D2a\1E\CA\A1\0D\CC\01\1E\DA!\1C\C8\010\87p`\87y(\07\80p\87wh\03s\90\87ph\87rh\03xx\87tp\07z(\07yh\83r`\87th\07\80\1E\E4\A1\1E\CA\01\18\DC\E1\1D\DA\C0\1C\E4!\1C\DA\A1\1C\DA\00\1E\DE!\1D\DC\81\1E\CAA\1E\DA\A0\1C\D8!\1D\DA\A1\0D\DC\E1\1D\DC\A1\0D\D8\A1\1C\C2\C1\1C\00\C2\1D\DE\A1\0D\D2\C1\1D\CCa\1E\DA\C0\1C\E0\A1\0D\DA!\1C\E8\01\1D\00s\08\07v\98\87r\00\08wx\876p\87pp\87yh\03s\80\876h\87p\A0\07t\00\CC!\1C\D8a\1E\CA\01 \E6\81\1E\C2a\1C\D6\A1\0D\E0A\1E\DE\81\1E\CAa\1C\E8\E1\1D\E4\A1\0D\C4\A1\1E\CC\C1\1C\CAA\1E\DA`\1E\D2A\1F\CA\01\C0\03\80\A0\87p\90\87s(\07zh\83q\80\87z\00\C6\E1\1D\E4\A1\1C\E4\00 \E8!\1C\E4\E1\1C\CA\81\1E\DA\C0\1C\CA!\1C\E8\A1\1E\E4\A1\1C\E6\01X\83y\98\87y(\879`\835\18\07|\88\03;`\835\98\87y(\076X\83y\98\87r\90\036X\83y\98\87r\98\03\80\A8\07w\98\87p0\87rh\03s\80\876h\87p\A0\07t\00\CC!\1C\D8a\1E\CA\01 \EAa\1E\CA\A1\0D\E6\E1\1D\CC\81\1E\DA\C0\1C\D8\E1\1D\C2\81\1E\00s\08\07v\98\87r\006\C8\88\F0\FF\FF\FF\FF\03\C1\0E\E50\0F\F3\D0\06\F0 \0F\E50\0E\E90\0F\E5\D0\06\E6\00\0F\ED\10\0E\E4\00\98C8\B0\C3<\94\03@\B8\C3;\B4\819\C8C8\B4C9\B4\01<\BCC:\B8\03=\94\83<\B4A9\B0C:\B4\03@\0F\F2P\0F\E5\00\0C\EE\F0\0Em`\0E\F2\10\0E\EDP\0Em\00\0F\EF\90\0E\EE@\0F\E5 \0FmP\0E\EC\90\0E\ED\D0\06\EE\F0\0E\EE\D0\06\ECP\0E\E1`\0E\00\E1\0E\EF\D0\06\E9\E0\0E\E60\0Fm`\0E\F0\D0\06\ED\10\0E\F4\80\0E\809\84\03;\CCC9\00\84;\BCC\1B\B8C8\B8\C3<\B4\819\C0C\1B\B4C8\D0\03:\00\E6\10\0E\EC0\0F\E5\00\10\F3@\0F\E10\0E\EB\D0\06\F0 \0F\EF@\0F\E50\0E\F4\F0\0E\F2\D0\06\E2P\0F\E6`\0E\E5 \0Fm0\0F\E9\A0\0F\E5\00\E0\01@\D0C8\C8\C39\94\03=\B4\C18\C0C=\00\E3\F0\0E\F2P\0Er\00\10\F4\10\0E\F2p\0E\E5@\0Fm`\0E\E5\10\0E\F4P\0F\F2P\0E\F3\00\AC\C1<\CC\C3<\94\C3\1C\B0\C1\1A\8C\03>\C4\81\1D\B0\C1\1A\CC\C3<\94\03\1B\AC\C1<\CCC9\C8\01\1B\AC\C1<\CCC9\CC\01@\D4\83;\CCC8\98C9\B4\819\C0C\1B\B4C8\D0\03:\00\E6\10\0E\EC0\0F\E5\00\10\F50\0F\E5\D0\06\F3\F0\0E\E6@\0Fm`\0E\EC\F0\0E\E1@\0F\809\84\03;\CCC9\00\00I\18\00\00\02\00\00\00\13\82`B \00\00\00\89 \00\00\0D\00\00\002\22\08\09 d\85\04\13\22\A4\84\04\13\22\E3\84\A1\90\14\12L\88\8C\0B\84\84L\100s\04H*\00\C5\1C\01\18\94`\88\08\AA0F7\10@3\02\00\134|\C0\03;\F8\05;\A0\836\08\07x\80\07v(\876h\87p\18\87w\98\07|\88\038p\838\80\037\80\83\0DeP\0Em\D0\0Ez\F0\0Em\90\0Ev@\07z`\07t\D0\06\E6\80\07p\A0\07q \07x\D0\06\EE\80\07z\10\07v\A0\07s \07z`\07t\D0\06\B3\10\07r\80\07:\0FDH #EB\80\1D\8C\10\18I\00\00@\00\00\C0\10\A7\00\00 \00\00\00\00\00\00\00\868\08\10\00\02\00\00\00\00\00\00\90\05\02\00\00\08\00\00\002\1E\98\0C\19\11L\90\8C\09&G\C6\04C\9A\22(\01\0AM\D0i\10\1D]\96\97C\00\00\00y\18\00\00\1C\00\00\00\1A\03L\90F\02\134A\18\08&PIC Level\13\84a\D80\04\C2\C05\08\82\83c+\03ab\B2j\02\B1+\93\9BK{s\03\B9q\81q\81\01A\19c\0Bs;k\B9\81\81q\81q\A9\99q\99I\D9\10\14\8D\D8\D8\EC\DA\5C\DA\DE\C8\EA\D8\CA\5C\CC\D8\C2\CE\E6\A6\04C\1566\BB6\974\B227\BA)A\01\00y\18\00\002\00\00\003\08\80\1C\C4\E1\1Cf\14\01=\88C8\84\C3\8CB\80\07yx\07s\98q\0C\E6\00\0F\ED\10\0E\F4\80\0E3\0CB\1E\C2\C1\1D\CE\A1\1Cf0\05=\88C8\84\83\1B\CC\03=\C8C=\8C\03=\CCx\8Ctp\07{\08\07yH\87pp\07zp\03vx\87p \87\19\CC\11\0E\EC\90\0E\E10\0Fn0\0F\E3\F0\0E\F0P\0E3\10\C4\1D\DE!\1C\D8!\1D\C2a\1Ef0\89;\BC\83;\D0C9\B4\03<\BC\83<\84\03;\CC\F0\14v`\07{h\077h\87rh\077\80\87p\90\87p`\07v(\07v\F8\05vx\87w\80\87_\08\87q\18\87r\98\87y\98\81,\EE\F0\0E\EE\E0\0E\F5\C0\0E\EC\00q \00\00\05\00\00\00&`<\11\D2L\85\05\10\0C\804\06@\F8\D2\14\01\00\00a \00\00\0B\00\00\00\13\04A,\10\00\00\00\03\00\00\004#\00dC\19\020\18\83\01\003\11\CA@\0C\83\11\C1\00\00#\06\04\00\1CB\12\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00", section "__LLVM,__bitcode"
@llvm.cmdline = appending constant [67 x i8] c"-triple\00x86_64-apple-macosx10.10.0\00-emit-llvm\00-disable-llvm-optzns\00", section "__LLVM,__cmdline"

; Function Attrs: nounwind ssp uwtable
define i32 @main() #0 {
  %1 = alloca i32, align 4
  store i32 0, i32* %1
  %2 = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([10 x i8]* @.str, i32 0, i32 0))
  ret i32 0
}

declare i32 @printf(i8*, ...) #1

attributes #0 = { nounwind ssp uwtable "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="core2" "target-features"="+ssse3,+cx16,+sse,+sse2,+sse3" "unsafe-fp-math"="false" "use-soft-float"="false" }
attributes #1 = { "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="core2" "target-features"="+ssse3,+cx16,+sse,+sse2,+sse3" "unsafe-fp-math"="false" "use-soft-float"="false" }

!llvm.module.flags = !{!0}
!llvm.ident = !{!1}

!0 = !{i32 1, !"PIC Level", i32 2}
!1 = !{!"Apple LLVM version 7.0.0 (clang-700.0.53.3)"}

La matriz de datos que está en el IR también cambia según la optimización y otras configuraciones de generación de código de clang. Es completamente desconocido para mí en qué formato o cualquier cosa está esto.

EDITAR:

Siguiendo la pista en Twitter, decidí volver a visitar esto y confirmarlo. Seguí esta publicación de blog y usé su herramienta de extracción de código de bits para sacar el archivo binario de Apple Archive del ejecutable de MachO. Y después de extraer Apple Archive con la utilidad xar, obtuve esto (convertido a texto con llvm-dis, por supuesto)

; ModuleID = '1'
target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-apple-macosx10.10.0"

@.str = private unnamed_addr constant [10 x i8] c"hi there!\00", align 1

; Function Attrs: nounwind ssp uwtable
define i32 @main() #0 {
  %1 = alloca i32, align 4
  store i32 0, i32* %1
  %2 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([10 x i8], [10 x i8]* @.str, i32 0, i32 0))
  ret i32 0
}

declare i32 @printf(i8*, ...) #1

attributes #0 = { nounwind ssp uwtable "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="core2" "target-features"="+ssse3,+cx16,+sse,+sse2,+sse3" "unsafe-fp-math"="false" "use-soft-float"="false" }
attributes #1 = { "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="core2" "target-features"="+ssse3,+cx16,+sse,+sse2,+sse3" "unsafe-fp-math"="false" "use-soft-float"="false" }

!llvm.module.flags = !{!0}
!llvm.ident = !{!1}

!0 = !{i32 1, !"PIC Level", i32 2}
!1 = !{!"Apple LLVM version 7.0.0 (clang-700.1.76)"}

La única diferencia notable realmente entre el IR sin código de bits y el IR con código de bits es que los nombres de archivo se han eliminado a solo 1, 2, etc. para cada arquitectura.

También confirmó que el código de bits incrustado en un binario se genera después de las optimizaciones. Si compila con -O3 y extrae el código de bits, será diferente de si compila con -O0.

Y solo para obtener crédito adicional, también confirmó que Apple no envía código de bits a los dispositivos cuando descarga una aplicación iOS 9. Incluyen una serie de otras secciones extrañas que no reconocí como __LINKEDIT, pero no incluyen el paquete __LLVM .__ y, por lo tanto, no parecen incluir código de bits en el binario final que se ejecuta en un dispositivo. Por extraño que parezca, Apple todavía envía binarios gordos con código separado de 32/64 bits a dispositivos iOS 8.


¿Se puede descompilar el código de bits de Apple? Es decir, ¿ahora Apple puede ver nuestro código fuente?
malhal

@malcolmhall si es similar al código LLVM, entonces solo un poco. El bytecode de LLVM tiene información de tipo y otras sugerencias que pueden hacer que la descompilación sea mucho más fácil y más útil. Sin embargo, no sé qué hay en el código de bits de Apple. Probablemente sea al menos algo más útil, pero en este momento se desconoce qué tan útil será. De cualquier manera, dudo mucho que sea tan fuerte de información como decir cómo .NET IL permite una descompilación casi perfecta en código C #
Earlz

¿Has visto el formato de archivo de código de bits de LLVM ? El número mágico es diferente, pero Apple está dando a entender firmemente que este es el formato de código binario.
Jeffery Thomas

"que se envía con todas las arquitecturas compatibles (es decir, duplicadas)" no está duplicado, porque el código de bits es diferente para cada segmento de mach-o
AlexDenisov

2
De acuerdo con twitter.com/mistydemeo/status/644555663373307904 , xar!es el formato de archivo de Apple.
Riking

14

Código de bits (iOS, watchOS)

Bitcode es una representación intermedia de un programa compilado. Las aplicaciones que cargue en iTunes Connect que contengan código de bits se compilarán y vincularán en la App Store. La inclusión de código de bits permitirá a Apple volver a optimizar el binario de su aplicación en el futuro sin la necesidad de enviar una nueva versión de su aplicación a la tienda.


Básicamente, este concepto es algo similar a Java, donde el código de bytes se ejecuta en diferentes JVM y, en este caso, el código de bits se coloca en la tienda iTune y, en lugar de proporcionar el código intermedio a diferentes plataformas (dispositivos), proporciona el código compilado que no necesita cualquier máquina virtual para ejecutar.

Por lo tanto, necesitamos crear el código de bits una vez y estará disponible para dispositivos existentes o futuros. Es el dolor de cabeza de Apple compilar y hacer que sea compatible con cada plataforma que tengan.

Los desarrolladores no tienen que hacer cambios y enviar la aplicación nuevamente para admitir nuevas plataformas.

Tomemos el ejemplo del iPhone 5s cuando Apple introdujo el x64chip en él. Aunque las x86aplicaciones eran totalmente compatibles con la x64arquitectura, pero para utilizar completamente la x64plataforma, el desarrollador tiene que cambiar la arquitectura o algún código. Una vez que haya terminado, la aplicación se envía a la tienda de aplicaciones para su revisión.

Si este concepto de código de bits se lanzó anteriormente, entonces los desarrolladores no tenemos que hacer ningún cambio para admitir la x64arquitectura de bits.


@ user102008 El corte es el resultado de habilitar Bitcode
keji

@kdogisthebest: No, no lo es. En ninguna parte dice eso. Y he visto el video WWDC sobre el corte, y no siempre menciona habilitar Bitcode.
user102008

Inder Kumar Rathore cuando se trata de Enterprise App Store ¿Cómo se maneja? ¿La tienda de aplicaciones empresariales admite esta función?
damithH

@damithH No hay una tienda de aplicaciones empresariales, tenemos que mantener las aplicaciones en nuestros servidores. No estoy seguro de si el adelgazamiento de la aplicación funcionará en aplicaciones empresariales o no. Pero hasta donde yo sé, no debería estar allí para las aplicaciones empresariales
Inder Kumar Rathore

Actualice la imagen en su respuesta, no está relacionada con Bitcode.
hsafarya

5

Actualizar

Apple ha aclarado que el corte se produce independientemente de habilitar el código de bits. También he observado esto en la práctica, donde una aplicación sin código de bits solo se descargará como la arquitectura adecuada para el dispositivo de destino.

Original

Más específicamente :

Bitcode Archive su aplicación para enviarla a la App Store en una representación intermedia, que se compila en ejecutables de 64 o 32 bits para los dispositivos de destino cuando se entregan.

Rebanar Las ilustraciones incorporadas en el Catálogo de activos y etiquetadas para una plataforma permiten que la App Store entregue solo lo que se necesita para la instalación.

La forma en que leí esto, si admite código de bits, los descargadores de su aplicación solo obtendrán la arquitectura compilada necesaria para su propio dispositivo.


En la guía App Thinning ( developer.apple.com/library/prerelease/ios/documentation/IDEs/… ) "Rebanar es el proceso de crear y entregar variantes del paquete de aplicaciones para diferentes dispositivos de destino. Una variante contiene solo la arquitectura ejecutable y recursos necesarios para el dispositivo de destino ". Los descargadores de su aplicación que solo obtienen su arquitectura es parte de Slicing.
user102008
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.