Présentation…
Linux consomme beaucoup de ressources, et il en consomme tellement qu’il ne laisse paradoxalement même pas une chance de créer des applications alternatives dans un environnement restreint. Un environnement traditionnel, même sans passer par GCC, nécessite LD. Mais LD nécessite GLibC… qui est une librairie lourde. Cette page vous présente un moyen de créer des fichiers ELF sans même employer un lieur comme LD. Ce sera aussi je l’espère une occasion de vous donner l’envie de découvrir le format de fichier ELF. Accessoirement, la solution présentée vous permettra même de créer des binaires Linux depuis Windows sans environnement de compilation croisée ( « cross-compilation » ).
Dans cette page …
Qu’est-ce que le format ELF ?
ELF est un sigle signifiant Executable and Linkable Format. Sans artifices et 100% utile, ce format
est employé par plusieurs systèmes, notamment BSD, UNIX
SystemV et Linux. Ce format a remplacé maintenant
depuis quelque temps l’ancien format a.out, qui posait
trop de problèmes techniques ( la liaison dynamique avec le
format a.out nécessitait littéralement des hacks, et cela n’était
plus tolérable pour une architecture système ). Le format
ELF version « 1.2
»
existe depuis 1997, et c’est la version la plus récente à cette
année 2007. ( la version ELF « 1.1
» date de 1995 ). Vous pourrez consulter cette référence
dans ce fichier PDF : TIS ELF
« 1.2 » « specifications »
( datée de 1997 ) . La référence est maintenue
par l’organisation TIS ( Tool Interface Standard ).
Windows ne connais pas le format de fichier ELF, et même les Windows à noyau « NT » utilisent encore le format COFF pourtant obsolète. Les autres version de Windows ( c’est-à-dire non-« NT » ) utilisent le format PE et ME. Cette page ne concerne donc que la création de binaires ELF pour BSD, UNIX et Linux.
ELF et la GLibC
Le format ELF a rendu les systèmes plus propres et plus fiables en permettant de se passer de certains hackings ( littéralement « Bricolage » ) officiels. Malheureusement, sous Linux, l’intégration du format ELF a été accompagnée d’une mise à jour, non seulement du noyau Linux, mais également de la GLibC. La GLibC 2, encore appelé LibC 6 est connue pour être celle qui a introduit le support ELF ; vous pourrez vois à ce sujet, la page Liaison dynamique . Pour être exact, la LibC 5 était le première à supporter ELF, mais GLibC 1 qui était alors un fork ( littéralement « Une branche divergente » ), n’a rejoins la LibC qu’avec LibC 6 ( GLibC 1 passait alors GLibC 2 ). L’ennui est que la LibC 5 était déjà trop volumineuse pour que Linux puisse honnêtement se targuer d’être un système utilisable par les configurations légères… et avec la LibC 6 alias GLibC 2, les chose se sont encore nettement aggravé : on a jamais vu une LibC être aussi lourde.
En conséquence, les systèmes Linux supportant ELF sont des systèmes lourds. Cependant, il n’est pas obligatoire d’avoir une LibC installée pour exécuter des applications au format ELF. En effet, c’est le noyau qui a en charge le chargement de l’application, et celui ci ne fait qu’invoquer le lieur dynamique ( ld.so.2 pour LibC 6 alias GLibC 2 ) habituellement associé au format ELF. Pour aller plus loin encore, notez que rien n’impose d’employer la LibC 6 ( ou GLibC 2 ) pour exécuter des applications au format ELF. En effet, le format prévoit un champs spécifiant un chemin vers un interpréteur, qui n’est en fait sous Linux, rien d’autre que le chemin vers le lieur dynamique. Ainsi, il est en théorie tout à fait possible de créer des applications au format ELF, qui serait dynamiquement liées avec ld.so.1 et LibC 5 ( ou même encore LibC 4 ou même encore antérieure ).
Mais en pratique, là ou il y a ELF, il y a GLibC 2 alias LibC 6.
Le problème
Le problème que cela pose est assez ennuyeux. Vous avez un Linux trop lourd pour être utilisable sur les anciens PC ( contrairement à ce qu’il prétend ), et cela vous empêche même de créer vos applications légère dans un environnement restreint. Car la création de fichier ELF passe habituellement par GNU LD, et GNU LD requière GLibC 2, et la taille de cette librairie interdit de l’installer sur un système modeste de base. Mais remarquons au passage que la librairie n’est pas la seule en cause, et que le lieur est lui même un peu trop volumineux.
Une solution
Ceux et celles d’entre vous qui ont connu l’âge d’or du MS-DOS,
une époque bénis ou on programmais joyeusement avec un système accessible
( entendez par là, compréhensible dans ses détails )
à tous et toutes, et bien documenté, se souviennent des joies qu’ils
et elles ont eu en découvrant à quelle point il était facile de
créer un fichier « *.COM
» . Il n’y avait d’ailleurs même pas besoin d’environnement
de développement, puisque le programme DEBUG ( très
utile et très léger, il a fait des heureux-ses ), fourni en
standard avec MS-DOS, permettait d’enregistrer des
fichiers binaires.
La solution qui vous est proposée ressemble tout à fait dans le principe à celle employée sous MS-DOS à l’époque. Et elle est même encore bien plus confortable et efficace.
Nous allons créer des fichiers assembleur ( fichiers sources persistant, donc plus pratique qu’avec DEBUG ), qui seront compilés avec NASM ( qui reconnais un jeux d’instruction bien plus vaste que DEBUG ), et au début desquels nous placerons une structure de donnée caractéristique des fichiers ELF. Cette structure comprendra l’entête du fichier, ainsi que deux entrées de descripteur de segment : une pour le binaire exécutable, et une pour les données en accès lecture et écriture ( plus évolué que les fichiers COM du DOS ).
Avantages de cette solution
Les versions statiques de NASM sont légères ( entre 150 KB et 200 KB environ ). Il existe un éditeur en console que je vous invite à découvrir, qui se nomme E3. Il est beaucoup plus commode à utiliser que VI ou VIM, et même s’il n’a pas encore à la hauteur de l’ergonomie du programme EDIT de MS-DOS, il est encore plus léger : 15 KB. Quelques documentations au format texte pèseront au plus 20 KB. Pour un total de de 185 KB à 235 KB environ, soit même pas le quart d’une disquette 1.44 M, vous avez un environnement de développement parfaitement fonctionnel. Ceci vous permettra de créer des applications légères en environnement restreint, vous autorisant à court-circuiter la lourdeur des installations Linux habituelles. Je vous recommande, de vous documentez sur les Syscalls, qui sont un moyen d’accéder aux fonctions systèmes, sans même employer la moindre librairie ( ne pensez même pas aux LibC… les dernières ne tiendraient même pas sur un petit disque dur ).
Le bonus compilation-croisée ( « cross-compilation » )
Comme il existe une version Windows de NASM, cette méthode vous permettra même de faire de la cross compilation, et de compiler sous Windows, des binaires ELF qui pourront fonctionner sous Linux. Ce bonus sera très appréciable si vous travailler à la création d’une installation légère de Linux : vous pourrez concevoir et compiler vos programme sous Windows, sans même avoir besoin que votre installation Linux soit achevée pour se faire.
À-propos du code
Concrètement, vous emploierez un pattern, que vous éditerez pour simplement y ajouter votre code et vos données. Vous avez le choix entre deux patterns : un pour les programmes ayant un segment de code et un segment de données en lecture et écriture, et un autre pour les programmes n’ayant besoin de rien de plus qu’un segment de code exécutable. Notez que pour les programme ne comprenant que des données en lecture seule, il est plus astucieux d’utiliser la version avec un segment de code seul ( on peut placer des données en lecture seule dans un segment de code, sans que cela ne pose la moindre soucis ).
Une petite remarque sur le code des structures ELF : j’ai utilisé un alignement sur 4 octets pour les segments. Bien que la taille des pages mémoire sous Linux soit de 4096 octets, il ne semble pas obligatoire que les segment soit alignés sur les pages, et une page peut donc apparemment chevaucher deux segments ( la gestion des pages et la gestion des segments sont transparentes l’une à l’autre, aussi bien pour le noyau Linux que pour les processeurs i80x86 ). L’alignement sur 4 octets est nécessaire, car si mes souvenirs sont bons, les processeurs Intel exigent que les adresses de base des segments soient alignées sur 4 octets ( à vérifier, mais trop de prudence ici ne nuira pas ). Il existe un drapeau ( flag ) dans les descripteurs de page, sur les processeurs Intel, qui commande le déclenchement d’une exception en cas d’accès à des données non alignées sur 4 octets, mais ceci est indépendant de la question qui nous intéresse, car c’est de l’alignement des adresses de base des segments qu’il s’agit ici.
La version avec segments de code et de données
Cette version sera à utiliser pour créer un fichier ELF
avec un segment de code et un segment de données en lecture et écriture.
Pour l’utiliser, éditez simplement les sections de code et de donnée
entre les marqueurs BEGIN et END, en commentaires dans chacun des deux segments.
Vous ne devez pas modifier le reste du code, sauf si vous savez
vraiment avec quoi vous jouez ;-). Finalement vous compilerez le
code avec la commande « nasm -f bin [-O2] votrecode.asm
». L’option « -f bin
» crée un format binaire plat, c’est à dire sans format
particulier ( le format du fichier est alors simplement celui
produit par le contenu du fichier assembleur ). L’option « -O2
» est optionnelle, mais elle est
souvent nécessaire pour les codes contenant des sauts conditionnels
par exemple ; référez vous à la documentation de NASM pour plus
d’informations si nécessaire.
Pensez à consulter la deuxième version simplifiée également ( plus loin ), qui sera a préféré pour les codes ne contenant pas de données en écriture.
Vous pourrez également utiliser ce code comme une introduction au format ELF, si vous désirez l’étudier un jours ; vous en tirerez sûrement du bonheur, et pour étudier ce format en profondeur, vous pourrez consulter la référence citée en début de ce document.
Vous pouvez consulter ce code ici en ligne, ou le télécharger
: elf-with-data.asm
( texte au format MS-DOS « CR-LF
» ).
; (hint:tabulation size is 3) ; (date-dd-mm-yyyy:03/01/2007) ; Creating a fully functional ELF file without even GNU/ld ; --------------------------------------------------------- ; ; This a pattern file which allow you to create ELF executable without ; even a linker in hands. This is useful for custom installation with ; low resource, such as old PC. Indeed, the usual way is to use GNU/ld, ; but unfortunatly, ld rely on glibc, which is very-very hugh en ; outrageously resource consuming. ; ; You can use it with nasm (the syntax used here is the one of nasm). ; A static version of nasm if very light (between 150 and 200KB). So ; with a static version of e3 (which is no more than 13KB), and ; a few text files documentations about syscalls and some other good ; stuff, you can have a real developpement environement under less ; than the quarter of an 1.44M floppy disk. ; ; Please visit http://www.les-ziboux.rasama.org/elf-without-ld.html ; for updates. In the futur, I will mirror a very small static version ; of nasm (102KB), a static version of e3, and some useful texte ; documentation. ; ; You can use this pattern in any project… it is not mandatory, but this ; would be very nice of you to put this url on top of your product sources : ; http://www.les-ziboux.rasama.org/elf-without-ld.html ; Thank you :) ; ; To use this pattern, just edit the place between the BEGIN and END mark, ; both in code and data segment. ; ; If you code only adress read-only data, then you can use the other ; "elf-simple.asm", beceause is there are only read-only datas, it is ; best to put them in code segment. ; ; Finaly you have to compile your code with "nasm -f bin [-O2] yourcode.asm" ; (the -O parameter is optional, alghtough often requited because of ; short-jumps and some stuff like that). ; This file is full of comments… you may remove them after reading ;) ; ; Comments are welcome : les-ziboux@rasama.org (fr/en) ; You wanna learn Arabic ? -> http://www.les-ziboux.rasama.org (fr) cpu 386 bits 32 org 0x08048000 ; Linux applications are loaded at this virtual address. ; === Header ================================================================ elf_header: .start db 0x7F, "ELF" ; ELF signature -- constant db 1 ; Architecture(1) -- 1 = 32 bits db 1 ; Data enconding -- 1 = LSB-First db 1 ; File version -- 1 = v1 db 0,0,0,0,0,0,0,0,0 ; 9 bytes padding -- should be zero dw 2 ; Type -- 2 = executable dw 3 ; Architecture(2) -- 3 = i386 dd 1 ; ELF Version -- 1 = ELF-v1 dd _start ; Entry point adress in memory -- virtual adress dd segments_table - $$ ; Segments table offset in file dd 0 ; Sections table offset in file -- 0 = none) dd 0 ; File's flags dw elf_header.size ; ELF Header's size dw 32 ; Segments table entries's size dw 2 ; Number of segment descriptors dw 0 ; Sections table entries's size -- 0 = none dw 0 ; Number of sections descriptor -- 0 = none dw 0 ; String table index -- 0 = none .size equ $ - .start segments_table: code_segment_descriptor: .start: dd 1 ; Type -- 1 = loadable into memory dd 0 ; Offset in file -- include ELF header and table dd $$ ; Virtual address in memory dd 0 ; Physical adress -- 0 = no physical address dd code_size ; Size in file dd code_size ; Size in memory dd 5 ; Permission flags -- 0x4 + 0x1 = read and execute dd 0x4 ; Alignment in memory (and in file) .size equ $ - .start data_segment_descriptor: .start: dd 1 ; Type -- 1 = loadable into memory dd _data - $$ ; Offset in file dd _data ; Virtual adress in memory dd 0 ; Physical adress -- 0 = no physical address dd data_size ; Size in file dd data_size ; Size in memory dd 6 ; Permission flags -- 0x4 + 0x2 = read and write dd 0x4 ; Alignment in memory (and in file) .size equ $ - .start ; === Code ================================================================== _code : _start: ; You can move the start label where-ever you want in code segment. ; ----- BEGIN of your code ----- ; Here is a sample code which simply say good bye and terminate. ; It give an example of usage of both code segment and data ; segment. Although this example does write to data segment, ; the data segment is still fully writable. Note that read-only ; datas can also reside in code segment (you should use ; the pattern "elf-simple.asm" for this purpose). ; You can put your own code between the BEGIN and END mark. ; Do not modify something else, unless you're sure about what ; you are playing with. mov eax, 4 ; function id for sys_write mov ebx, 1 ; descriptor of standard output mov ecx, message ; offset of bytes to write mov edx, message.length ; number of bytes to write int 0x80 ; syscall mov eax, 1 ; function id for sys_exit mov ebx, 0 ; return code from the program (0 mean Ok) int 0x80 ; syscall ; ----- END of your code ----- align 4 code_size equ $ - $$ ; === Data ================================================================== align 4 _data: ; ----- BEGIN of your datas ----- ; This is a simple data example. This data segment is writable. ; Note that you can put read-only data in the code segment. If you ; only have read-only data, and no writable data in you application, ; then putting them in code segmet, avoid the creation of a data ; segment, reducing a bit the size of the executable. For this ; purpose, use the other patten name "elf-simple.asm". ; You can put your own datas between the BEGIN and END mark. ; Do not modify something else, unless you're sure about what ; you are playing with. message: db "Bye, Au-revoir, Ma'a as-salama, Bisbald… :)", 10 .length equ $ - message ; ----- END of your datas ----- align 4 data_size equ $ - _data
La version avec segment de code seul
Cette version du code est a utiliser pour les applications n’ayant qu’un segment de code, et pas de donnée en écriture ; les données constantes, en lecture seule, peuvent être placées dans le segment de code.
Vous pouvez consulter ce code ici en ligne, ou le télécharger
: elf-simple.asm ( texte
au format MS-DOS « CR-LF
» ).
; (hint:tabulation size is 3) ; (date-dd-mm-yyyy:03/01/2007) ; Creating a fully functional ELF file without even GNU/ld ; --------------------------------------------------------- ; ; There are no explanations here : read "elf-with-data.asm" for ; informations on usage and copyright. ; ; Compile: nasm -f bin [-O2] yourcode.asm ; Updates : http://www.les-ziboux.rasama.org/elf-without-ld.html ; Comments are welcome : les-ziboux@rasama.org (fr/en) ; You wanna learn Arabic ? -> http://www.les-ziboux.rasama.org (fr) cpu 386 bits 32 org 0x08048000 ; Linux applications are loaded at this virtual address. ; === Header ================================================================ elf_header: .start db 0x7F, "ELF" ; ELF signature -- constant db 1 ; Architecture(1) -- 1 = 32 bits db 1 ; Data enconding -- 1 = LSB-First db 1 ; File version -- 1 = v1 db 0,0,0,0,0,0,0,0,0 ; 9 bytes padding -- should be zero dw 2 ; Type -- 2 = executable dw 3 ; Architecture(2) -- 3 = i386 dd 1 ; ELF Version -- 1 = ELF-v1 dd _start ; Entry point adress in memory -- virtual adress dd segments_table - $$ ; Segments table offset in file dd 0 ; Sections table offset in file -- 0 = none) dd 0 ; File's flags dw elf_header.size ; ELF Header's size dw 32 ; Segments table entries's size dw 1 ; Number of segment descriptors -- just one (code) dw 0 ; Sections table entries's size -- 0 = none dw 0 ; Number of sections descriptor -- 0 = none dw 0 ; String table index -- 0 = none .size equ $ - .start segments_table: code_segment_descriptor: .start: dd 1 ; Type -- 1 = loadable into memory dd 0 ; Offset in file -- include ELF header and table dd $$ ; Virtual address in memory dd 0 ; Physical adress -- 0 = no physical address dd code_size ; Size in file dd code_size ; Size in memory dd 5 ; Permission flags -- 0x4 + 0x1 = read and execute dd 0x4 ; Alignment in memory (and in file) .size equ $ - .start ; === Code ================================================================== _code : _start: ; You can move the start label where-ever you want in code segment. ; ----- BEGIN of your code ----- ; Here is a sample code which simply cleanly terminate :P mov eax, 1 ; function id for sys_exit mov ebx, 0 ; return code from the program (0 mean Ok) int 0x80 ; syscall ; ----- END of your code ----- ; You may have read-only data here as well ;) align 4 code_size equ $ - $$
Veuillez agréer Tous mes Vœux d’Inspiration…
La suite…
Sujet suivant : Dès que possible…