Bonsoir,
j'ai eu une idée de projet tout récemment qui nécéssite la création de binaires auto-extractibles.
J'avais déjà réalisé ce type de binaire, mais mon algorithme est limité aux petits fichiers (en fait je créé un code source C à la volée, avec dedans tous le nécéssaire pour extraire octet par octet un des fichiers archivés, et une fois le code généré je compile tout via gcc)
problème : bien que lors de l'extraction ce soit quasi-instantanné, lors de la compilation ca peut devenir très vite bougrement long car j'ai des fichiers de 20Mo à inclure dans le binaire et mon code a compiler fait donc lui aussi 20Mo (une succession de fwrite sur 15km :)
Donc, je voulais repenser mon algo... Quelqu'un a des idées ?
Comment font les installshield, wise, & co ? sous linux c'est généralement des scripts shell de la mort, on les réalise comment ?
Merci
# Dégrossissons le problème
Posté par Miair Patreau . Évalué à 2.
Dans un programme auto-extractible binaire, je suppose que le problème se situe dans le fait d'inclure un bloc de données arbitraire et arbitrairement grand, la taille de ce bloc de données, et le moyen d'accéder à ces données.
A priori, je me contenterais de définir deux variables importées dans le fichier C qui contient la fonction main() et l'algorithme de décompression :
extern unsigned int code_size;
extern void* code;
Ainsi, l'algorithme de décompression n'a qu'à se reposer dessus et le fichier compile sans accroc.
Reste à créer un fichier .o qui exporte ces variables avec le contenu correct.
Pour ça je ne peux que suggérer de lire la doc expliquant le format de fichier ELF ( http://en.wikipedia.org/wiki/Executable_and_Linkable_Format ), et d'apprendre à utiliser libelf ( http://www.mr511.de/software/english.html ).
Il n'y a pas besoin de savoir faire grand-chose : juste créer un fichier ELF relocalisable (.o) qui contient des données en lecture seule, exportées sous les noms "code_size" et "code".
Une fois un tel fichier créé, il se lie sans problème avec le fichier qui contient l'algo de décompression, et si l'algo est correct, ça devrait marcher sans problème.
Je suggère de faire des essais avec des problèmes triviaux d'abord.
[^] # Re: Dégrossissons le problème
Posté par cho7 (site web personnel) . Évalué à 2.
Sinon c'est interressant la libelf, j'vais p'tetre approfondir là dedans.
Merci.
[^] # Re: Dégrossissons le problème
Posté par Miair Patreau . Évalué à 2.
Concernant la taille d'un tableau, certes c'est limité, mais je ne suggère pas d'utiliser un tableau, je suggère un pointeur.
[^] # Re: Dégrossissons le problème
Posté par cho7 (site web personnel) . Évalué à 2.
oui désolé j'avais pas noté. ta solution me semble la plus pertinente pour faire ce que je veux, faut juste que je me documente sur cette fameuse libelf et que je saisisse bien le principe : le .o contenant le fichier de 15Mo, on va le créé a la volée via libelf, sans passer par le compilateur, et il aura le même résultat que si j'avais ecris un code à la main, contenant les 2 fameuses variables, et que j'aurai ensuite compiler ? mais ce code que j'aurai pu ecrire à la main, il ressemblerai à quoi ?
un truc genre :
int taille=30;
unsigned char*="ceci est un fichier autogénéré";
(et plus généralement avec le char* contenant le fichier en base64 par exemple ?)
Enfin bon, j'verrai ca ce soir de toute facon, mais j'pense que tu m'as mis sur une bonne piste :)
Merci.
[^] # Re: Dégrossissons le problème
Posté par Miair Patreau . Évalué à 1.
L'équivalent en source serait quelque chose du genre :
unsigned int taille = 15*1024*1024;
char * contenu =
{
'\044', '\353', '\150', '\220', '\000', '\274', '\175', '\574',
'\003', '\520', '\017', '\520', '\137', '\374', '\076', '\033',
... // Et ainsi de suite pour représenter 15 Mo
};
C'est tout de même assez lourd à faire en source, et je suis même pas certain que gcc l'accepte (tiens, je vais essayer :).)
[^] # Re: Dégrossissons le problème
Posté par Miair Patreau . Évalué à 1.
Ce serait plutôt
char * contenu =
"\044\353\150\220\000\274\175\574"
"\003\520\017\520\137\374\076\033"
...
;
ce qui génère un octet nul à la fin, mais voilà, quoi.
[^] # Re: Dégrossissons le problème
Posté par cho7 (site web personnel) . Évalué à 2.
Avec un fichier de 15ko ca génère un code C autonome qui se compile en 0.50 secondes et qui une fois executé recréé le fichier d'origine. Parcontre pour celui de 15Mo là c'est meeeggggaaaaaa long et ca tue mon processeur !
Donc j'aimerai bien le compiler une fois pour toute en .o et ensuite le lier quand j'en ai besoin a un code C qui accéderait aux variables taille et contenu, c'est possible ca ? (j'suis pas très calé sur certains points du C, notamment sur ces histoires de linkage)
J'ai essayé vite fait, mais gcc me dit que j'ai pas de main dans mon .o :(
[^] # Re: Dégrossissons le problème
Posté par Miair Patreau . Évalué à 1.
Le mieux est de réaliser un petit programme qui, à partir d'un fichier qui contient les données à inclure, fabrique directement un .o contenant ces données et un symbole exporté de pointeur vers ces données. Autrement dit qui fabrique le .o équivalent à ton fichier source géant mais sans avoir à générer ce fichier source et à le faire transformer en .o par un compilateur.
Donc j'aimerai bien le compiler une fois pour toute en .o et ensuite le lier quand j'en ai besoin a un code C qui accéderait aux variables taille et contenu, c'est possible ca ?
Moi je te dis de laisser tomber cette méthode, mais c'est possible, oui. En admettant que ton fichier .c géant se nomme donnees.c, tu les compiles avec :
gcc -c donnees.c
ce qui crée le fichier donnees.o qui exporte les symboles voulus et peut être lié à tout code qui en a besoin (ou pas). En admettant que ton code qui se base sur la taille et le pointeur vers les données s'appelle main.c, il suffira de faire :
gcc donnees.o main.c -o main
pour créer un fichier exécutable "main" complet. Voilà voilà.
[^] # Re: Dégrossissons le problème
Posté par David Decotigny (site web personnel) . Évalué à 2.
Dans le prog final :
- Une section elf ".embedded_prog" avec des symboles _b_embed et _e_embed pour la localiser
- Dans cette section, le binaire du programme a charger /decompresser
- Le programme de chargement decompression
Le programme de chargemement/decompression faisant les choses suivantes :
- ecriture des donnees entre les symboles _b_embed et _e_embed dans un fichier temporaire (decompression a la volee ou a posteriori)
- fork() puis exec() du fichier temporaire
Et voila ! Pour ce qui est de comment inclure un fichier dans une section, man objcopy ou voir SOS dans le linux magazine de Mars dernier (tiens ca me fait penser que le code de n'est pas encore en ligne).
[^] # Re: Dégrossissons le problème
Posté par cho7 (site web personnel) . Évalué à 2.
D'après ce que j'ai compris, avec objcopy on peut inserer dans un binaire existant un fichier quelquonque, que l'on balisera avec par exemple _debut_prog et _fin_prog.
Mais comment dit-on, en C, que notre (nos) fichier(s) se trouve(nt) dans soi-même, a l'adresse _debut_prog et se termine à _fin_prog ?
Et supposons que j'ai une fichier plop.bin et une image plop.jpg, je stock plop.jpg dans plop.bin via objcopy. A quoi ressemblerai la ligne de commande ? (oui désolé j'ai pas linuxmag de mars et le man page est pas super clair vu d'ici (j'suis au taf, jpeux rien tester, pas de gcc, snif...)
Merci
[^] # Re: Dégrossissons le problème
Posté par Miair Patreau . Évalué à 1.
L'idée, c'est que _debug_prog et _fin_prog soient des symboles exportés. Autrement dit, en lexique C, des variables externes.
Il suffirait de déclarer dans le fichier C :
extern void * _debut_prog, * _fin_prog;
Puis de s'en servir tel quel. Je n'ai pas suggéré cette méthode parce que je ne connaissais pas objcopy (et ne sais toujours pas comment m'en servir.)
[^] # Re: Dégrossissons le problème
Posté par David Decotigny (site web personnel) . Évalué à 2.
On veut caser toute un tar.gz dans un executable, le decompresser a la volee dans un repertoire temporaire, et executer le programme nommé "run" contenu dans cette archive.
Pour ca, on commence a fabriquer son tar.gz avec son executable "run" a faire demarrer une fois que la decompression sera faite : binaire ou script shell. Ensuite on fabrique un .o qui contient cette archive dans la section .rodata, et on repere les donnees ainsi enfouies par les symboles "__embed" et "_e_embed". Enfin, on lie ce .o avec un autre .o qui contient le code de decompression.
Bon, finalement c'est peut-etre plus simple avec le code sous les yeux.
- Le fichier qui decompresse l'archive integree dans le binaire et qui lance le programme "run" de cette archive :
- Le Makefile qui fait l'archive a compresser avec le fichier run (un script de base) a demarrer une fois que tout sera decompresse. On en profite pour mettre d'autres trucs bidon dans l'archive. Au final, le make construit le binaire avec l'archive enfouie :
Et voila ! Maintenant je mets l'executable "demo" n'importe ou, je l'execute, et, dans le /tmp/.embedXYZTUV, ce decompressera tout et ca lancera le script "run" !
Note : le "XYZTUV" est genere par mkdtemp, le script "run" s'occupera de l'afficher.
Note 2 : le code a l'air de marcher, il y a peut-etre quelques optimisations a faire (regarder du cote de fwrite).
[^] # Re: Dégrossissons le problème
Posté par David Decotigny (site web personnel) . Évalué à 1.
[^] # Re: Dégrossissons le problème
Posté par cho7 (site web personnel) . Évalué à 2.
En plus le code est très clair, sauf peut-être le script lds, dont j'ai quand même une vague idée, mais aurais-tu une chtite explication a me donner s'il te plait ?
Encore merci
[^] # Re: Dégrossissons le problème
Posté par cho7 (site web personnel) . Évalué à 2.
Et encore merci à toi
# Voir les sharutils...
Posté par Christophe Merlet (site web personnel) . Évalué à 5.
[^] # Re: Voir les sharutils...
Posté par KiKouN . Évalué à 1.
Les autoextratibles d'ID software comme celui d'enemy-territory sont-ils réalisés avec cette technique ?
[^] # Re: Voir les sharutils...
Posté par cho7 (site web personnel) . Évalué à 2.
Merci
# Pour les scripts,
Posté par Christophe Chailloleau-Leclerc . Évalué à 2.
"here doc".
Exemple (sans test, hein, à adapter) :
Tu as un répertoire X avec 3 fichiers A, B et C
Tu fais un "tar cf Temp.tar X", suivi d'un "compress Temp.tar".
Tu as maintenant un fichier "Temp.tar.Z" qui contient ton répertoire compressé.
Tu fais un script Install.sh contenant quelquechose comme
"cat << LA_FIN | uncompress | tar xf - "
Puis tu fais un "cat Temp.tar.Z >> Install.sh
Puis tu ajoutes en fin de script, sur la première ligne après les données binaires venant de ton Temp.tar.Z "LA_FIN"
Il faut sans doute utiliser d'autres commandes au passage pour des problèmes d'encodage, ton script devant être de l'ascii...
Evidemment, compress / uncompress et tar peuvent être remplacés par du zip, bzip, ..., et "LA_FIN" est un mot clé dont le choix est libre, mais qui ne doit pas pouvoir se trouver dans les données que tu veux extraire.
Bonne chance.
[^] # Re: Pour les scripts,
Posté par cho7 (site web personnel) . Évalué à 2.
Merci.
P.S : Autrement, comme dit précédemment, je serai quand même intérressé par la méthode C pour stocker un fichier volumineux dans le code source, donc si quelqu'un a une idée, le thread n'est pas clos :)
Suivre le flux des commentaires
Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.