Cosa fa ENABLE_BITCODE in xcode 7?


262

Ho un problema con il termine bitcode incorporato.
Che cos'è il bitcode incorporato?
Quando abilitare, ENABLE_BITCODEnel nuovo Xcode?
Cosa succede al binario quando abilitato, ENABLE_BITCODEin Xcode 7?

Risposte:


312

Bitcode si riferisce al tipo di codice: "LLVM Bitcode" che viene inviato a iTunes Connect. Ciò consente ad Apple di utilizzare determinati calcoli per riottimizzare ulteriormente le app (ad esempio: eventualmente ridimensionare le dimensioni degli eseguibili). Se Apple deve modificare il tuo eseguibile, può farlo senza caricare una nuova build.

Ciò differisce da: Slicing che è il processo di ottimizzazione della tua app per il dispositivo di un utente in base alla risoluzione e all'architettura del dispositivo. L'affettatura non richiede Bitcode. (Es: includendo solo @ 2x immagini su un 5s)

Il diradamento delle app è la combinazione di risorse di taglio, bitcode e su richiesta

Bitcode è una rappresentazione intermedia di un programma compilato. Le app caricate su iTunes Connect che contengono bitcode verranno compilate e collegate su App Store. L'inclusione di bitcode consentirà ad Apple di ottimizzare nuovamente il file binario della tua app in futuro senza la necessità di inviare una nuova versione della tua app allo store.

Documentazione Apple sul diradamento delle app


Nulla di ciò che hai citato afferma che l'abilitazione del bitcode riduce le dimensioni dell'app sul dispositivo dell'utente. Bitcode non ha nulla a che fare con risorse come 3x o 2x.
user102008

1
Ancora una volta, le risorse non hanno nulla a che fare con Bitcode, che riguarda il codice. Il download da parte degli utenti di solo alcune architetture di codice e determinate versioni di risorse è Slicing, che non ha nulla a che fare con Bitcode.
user102008

7
Non sono d'accordo sul fatto che consente ad Apple di ridurre le dimensioni della tua app. Da nessuna parte lo dice. Dice "consentirà ad Apple di riottimizzare il file binario della tua app in futuro senza la necessità di inviare una nuova versione della tua app allo store", che intendo significare che consente a Apple di ricompilare la tua app per una nuova architettura se esce un nuovo dispositivo con una nuova architettura, senza che sia necessario inviare una nuova versione che include questa architettura.
user102008

2
No, il slicing sta separando le risorse delle app in gruppi per dispositivi particolari. Bitcode è ciò che consente ad Apple di generare un eseguibile per una particolare architettura.
Jon Shier,

2
@JonShier Apple afferma che "L'affettatura è il processo di creazione e consegna di varianti del pacchetto di app per diversi dispositivi di destinazione. Una variante contiene solo l'architettura eseguibile e le risorse necessarie per il dispositivo di destinazione" Quindi, per tagliare è solo il codice eseguibile E le risorse per un determinato dispositivo.
keji,

80

Che cos'è il bitcode incorporato?

Secondo i documenti :

Bitcode è una rappresentazione intermedia di un programma compilato. Le app caricate su iTunes Connect che contengono bitcode verranno compilate e collegate su App Store. L'inclusione di bitcode consentirà ad Apple di ottimizzare nuovamente il file binario della tua app in futuro senza la necessità di inviare una nuova versione della tua app allo store.

Aggiornamento: Questa frase in "Le nuove funzionalità di Xcode 7" mi ha fatto pensare per lungo tempo che codice binario che è necessario per affettare per ridurre le dimensioni di app:

Quando archivi per l'invio all'App Store, Xcode compilerà la tua app in una rappresentazione intermedia. L'App Store quindi compilerà il bitcode negli eseguibili a 64 o 32 bit, se necessario.

Tuttavia questo non è vero, codice binario che e affettare lavoro indipendentemente: Sfogliatrici tratta di ridurre dimensioni e generando app app fascio varianti, e codice binario che è circa ottimizzazioni determinate binari. L'ho verificato controllando le architetture incluse negli eseguibili di app non bitcode e trovando che includono solo quelle necessarie.

Bitcode consente ad un altro componente di assottigliamento app chiamato Slicing di generare varianti di bundle di app con particolari eseguibili per architetture particolari, ad esempio la variante iPhone 5S includerà solo eseguibile arm64, iPad Mini armv7 e così via.

Quando abilitare ENABLE_BITCODE nel nuovo Xcode?

Per le app iOS, il codice di accesso è predefinito, ma facoltativo. Se fornisci bitcode, tutte le app e i framework nel bundle dell'app devono includere bitcode. Per le app watchOS e tvOS, è richiesto bitcode.

Cosa succede al binario quando ENABLE_BITCODE è abilitato nel nuovo Xcode?

Dal riferimento Xcode 7:

L'attivazione di questa impostazione indica che la destinazione o il progetto devono generare bitcode durante la compilazione per piattaforme e architetture che lo supportano. Per le build di archivio, il codice bit verrà generato nel file binario collegato per l'invio all'app store. Per altre build, il compilatore e il linker verificheranno se il codice è conforme ai requisiti per la generazione di bitcode, ma non genererà bitcode effettivo.

Ecco un paio di link che aiuteranno a comprendere meglio Bitcode :


Il bitcode verrà incluso se ho ENABLE_BITCODE ma deseleziono "Incluso bitcode" prima di inviarlo all'App Store?
Allaire,

"Per le app iOS, il codice di accesso è predefinito, ma opzionale." Eh ..? Vieni di nuovo..? È o non è facoltativo ..?
NpC0mpl3t3,

@ NpC0mpl3t3, come indicato nella risposta, è facoltativo per le app iOS, ma richiesto per le app watchOS e tvOS.
Maxim Pavlov,

Ottimo aiuto! Questa risposta qui mostra come codice binario che disable: stackoverflow.com/a/41418824/9190
Guerry

20

Dato che la domanda esatta è "cosa abilita il bitcode", vorrei fornire alcuni piccoli dettagli tecnici che ho capito finora. La maggior parte di questo è praticamente impossibile da capire con certezza al 100% fino a quando Apple non rilascia il codice sorgente per questo compilatore

Innanzitutto, il bitcode di Apple non sembra essere la stessa cosa del bytecode LLVM. Almeno, non sono stato in grado di capire alcuna somiglianza tra loro. Sembra avere un'intestazione proprietaria (inizia sempre con "xar!") E probabilmente qualche magia di riferimento del tempo di collegamento che impedisce la duplicazione dei dati. Se si scrive una stringa codificata, questa stringa verrà inserita nei dati solo una volta, anziché due volte come ci si aspetterebbe se fosse un normale codice byte LLVM.

In secondo luogo, il bitcode non viene realmente distribuito nell'archivio binario come un'architettura separata, come ci si potrebbe aspettare. Non viene spedito nello stesso modo in cui si dice che x86 e ARM sono messi in un binario (archivio FAT). Invece, usano una sezione speciale nel binario MachO specifico per l'architettura denominata "__LLVM" che viene spedita con ogni architettura supportata (cioè duplicata). Presumo che questo sia un problema con il loro sistema di compilazione e potrebbe essere risolto in futuro per evitare la duplicazione.

Codice C (compilato con clang -fembed-bitcode hi.c -S -emit-llvm):

#include <stdio.h>

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

Uscita 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)"}

L'array di dati che si trova nell'IR cambia anche in base all'ottimizzazione e alle altre impostazioni di generazione del codice di clang. Mi è del tutto ignoto in quale formato o cosa sia.

MODIFICARE:

Seguendo il suggerimento su Twitter, ho deciso di rivisitare questo e di confermarlo. Ho seguito questo post del blog e ho usato il suo strumento di estrazione di bitcode per estrarre il file binario di Apple Archive dall'eseguibile MachO. E dopo aver estratto l'archivio Apple con l'utilità xar, ho ottenuto questo (convertito in testo con llvm-dis ovviamente)

; 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)"}

L'unica differenza notevole tra IR senza bitcode e IR con bitcode è che i nomi dei file sono stati ridotti a solo 1, 2, ecc. Per ogni architettura.

Ho anche confermato che il bitcode incorporato in un file binario viene generato dopo le ottimizzazioni. Se compili con -O3 ed estrai il bitcode, sarà diverso da se compili con -O0.

E solo per ottenere credito extra, ho anche confermato che Apple non spedisce bitcode ai dispositivi quando scarichi un'app iOS 9. Includono una serie di altre strane sezioni che non ho riconosciuto come __LINKEDIT, ma non includono il bundle __LLVM .__, e quindi non sembrano includere bitcode nell'ultimo binario che gira su un dispositivo. Stranamente, tuttavia, Apple spedisce ancora binari grassi con codice separato 32 / 64bit ai dispositivi iOS 8.


Il bitcode di Apple può essere decompilato? Cioè, ora Apple può vedere il nostro codice sorgente?
Malhal,

@malcolmhall se è simile al codice LLVM, solo un po '. Il bytecode LLVM contiene informazioni sul tipo e altri suggerimenti che possono rendere la decompilazione molto più semplice e utile. Tuttavia, non so cosa ci sia nel bitcode di Apple. Probabilmente è almeno un po 'più utile, ma a questo punto non si sa quanto sarà utile. Ad ogni modo, dubito fortemente che sarà altrettanto forte di informazioni come dire come .NET IL consente una decompilazione quasi perfetta per il codice C #
Earlz,

Hai visto il formato file Bitcode di LLVM ? Il numero magico è diverso, ma Apple suggerisce fortemente che questo è il formato bitcode.
Jeffery Thomas,

"che viene fornito con tutte le architetture supportate (ovvero duplicate)" non è duplicato, perché il bitcode è diverso per ogni fetta di mach-o
AlexDenisov

2
Secondo twitter.com/mistydemeo/status/644555663373307904 , xar!è il formato del file di archivio di Apple.
Riking

14

Bitcode (iOS, watchOS)

Bitcode è una rappresentazione intermedia di un programma compilato. Le app caricate su iTunes Connect che contengono bitcode verranno compilate e collegate su App Store. L'inclusione di bitcode consentirà ad Apple di ottimizzare nuovamente il file binario della tua app in futuro senza la necessità di inviare una nuova versione della tua app allo store.


Fondamentalmente questo concetto è in qualche modo simile a Java in cui il codice byte viene eseguito su diverse JVM e in questo caso il bitcode viene inserito nell'archivio iTune e invece di fornire il codice intermedio a piattaforme (dispositivi) diversi fornisce il codice compilato che non necessita qualsiasi macchina virtuale da eseguire.

Quindi dobbiamo creare il bitcode una volta e sarà disponibile per i dispositivi esistenti o futuri. È il mal di testa di Apple compilare e renderlo compatibile con ogni piattaforma che hanno.

Gli sviluppatori non devono apportare modifiche e inviare nuovamente l'app per supportare nuove piattaforme.

Facciamo l'esempio di iPhone 5s quando Apple ha introdotto il x64chip al suo interno. Sebbene le x86app siano totalmente compatibili con l' x64architettura ma per utilizzare appieno la x64piattaforma, lo sviluppatore deve modificare l'architettura o un po 'di codice. Al termine, l'app viene inviata all'app store per la revisione.

Se questo concetto di bitcode è stato lanciato in precedenza, allora gli sviluppatori non devono apportare modifiche per supportare l' x64architettura bit.


@ user102008 L'affettatura è il risultato dell'abilitazione di Bitcode
keji,

@kdogisthebest: No non lo è. Da nessuna parte lo dice. E ho visto il video del WWDC su slicing, e non menziona affatto l'abilitazione di Bitcode.
user102008

Inder Kumar Rathore quando si tratta di Enterprise App Store Come gestisce? l'app store aziendale supporta questa funzione?
dannazione,

@damithH Non esiste un app store aziendale, dobbiamo mantenere le app sui nostri server. Non sono sicuro se il diradamento delle app funzionerà o meno sulle app aziendali. Ma per quanto ne so non dovrebbe essere lì per le app aziendali
Inder Kumar Rathore

Aggiorna l'immagine nella tua risposta, non è correlata a Bitcode.
hsafarya,

5

Aggiornare

Apple ha chiarito che lo slicing si verifica indipendentemente dall'abilitazione del bitcode. L'ho osservato anche in pratica dove un'app abilitata senza bitcode verrà scaricata solo come architettura appropriata per il dispositivo di destinazione.

Originale

Più specificamente :

Codice binario che. Archivia l'app per l'invio all'App Store in una rappresentazione intermedia, che viene compilata in eseguibili a 64 o 32 bit per i dispositivi di destinazione al momento della consegna.

Affettare. Le opere d'arte incorporate nel Catalogo risorse e contrassegnate per una piattaforma consentono all'App Store di fornire solo ciò che è necessario per l'installazione.

Il modo in cui leggo questo, se supporti bitcode, i downloader della tua app otterrà solo l'architettura compilata necessaria per il proprio dispositivo.


Nella guida al diradamento delle app ( developer.apple.com/library/prerelease/ios/documentation/IDEs/… ) "L'affettatura è il processo di creazione e consegna di varianti del pacchetto di app per diversi dispositivi di destinazione. Una variante contiene solo l'architettura eseguibile e risorse necessarie per il dispositivo di destinazione. " I downloader della tua app che ottengono solo la loro architettura fanno parte di Slicing.
user102008
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.