Shared libraries et modinfo
S'il est relativement simple d'ajouter des métadonnées aux dll dans Windows par le truchement des fichiers resource.h et <project>.rc, il n'y a pas de telle solution «standardisée» sous Linux pour les librairies partagées (shared libraries).
Pourtant, les modules du noyau Linux contiennent bien des métadonnées pouvant être extraites en utilisant l'utilitaire modinfo.
$ modinfo ipv6.ko
filename: ipv6.ko
alias: net-pf-10
license: GPL
description: IPv6 protocol stack for Linux
author: Cast of dozens
srcversion: 0D921E6BFEEC960D6AB10EB
depends:
vermagic: 2.6.35.13-91.fc14.i686 SMP mod_unload 686
parm: disable:Disable IPv6 module such that it is non-functional (int)
parm: disable_ipv6:Disable IPv6 on all interfaces (int)
parm: autoconf:Enable IPv6 address autoconfiguration on all interfaces (int)
Les kernel objects renferment en effet une section particulière, nommée modinfo, contenant des chaînes de caractères associant des clés à des valeurs (un peu à la manière d'un fichier ini).
Le contenu de cette section peut aisément être inspecté à l'aide de l'utilitaire objdump.
$ objdump --full-contents --section .modinfo ipv6.ko
ipv6.ko: file format elf32-i386
Contents of section .modinfo:
0000 616c6961 733d6e65 742d7066 2d313000 alias=net-pf-10.
0010 7061726d 3d617574 6f636f6e 663a456e parm=autoconf:En
0020 61626c65 20495076 36206164 64726573 able IPv6 addres
0030 73206175 746f636f 6e666967 75726174 s autoconfigurat
0040 696f6e20 6f6e2061 6c6c2069 6e746572 ion on all inter
0050 66616365 73000000 7061726d 74797065 faces...parmtype
0060 3d617574 6f636f6e 663a696e 74000000 =autoconf:int...
0070 7061726d 3d646973 61626c65 5f697076 parm=disable_ipv
0080 363a4469 7361626c 65204950 7636206f 6:Disable IPv6 o
0090 6e20616c 6c20696e 74657266 61636573 n all interfaces
00a0 00000000 7061726d 74797065 3d646973 ....parmtype=dis
00b0 61626c65 5f697076 363a696e 74000000 able_ipv6:int...
00c0 7061726d 3d646973 61626c65 3a446973 parm=disable:Dis
00d0 61626c65 20495076 36206d6f 64756c65 able IPv6 module
00e0 20737563 68207468 61742069 74206973 such that it is
00f0 206e6f6e 2d66756e 6374696f 6e616c00 non-functional.
0100 7061726d 74797065 3d646973 61626c65 parmtype=disable
0110 3a696e74 00000000 6c696365 6e73653d :int....license=
0120 47504c00 64657363 72697074 696f6e3d GPL.description=
0130 49507636 2070726f 746f636f 6c207374 IPv6 protocol st
0140 61636b20 666f7220 4c696e75 78000000 ack for Linux...
0150 61757468 6f723d43 61737420 6f662064 author=Cast of d
0160 6f7a656e 73000000 73726376 65727369 ozens...srcversi
0170 6f6e3d30 44393231 45364246 45454339 on=0D921E6BFEEC9
0180 36304436 41423130 45420000 64657065 60D6AB10EB..depe
0190 6e64733d 00000000 7665726d 61676963 nds=....vermagic
01a0 3d322e36 2e33352e 31332d39 312e6663 =2.6.35.13-91.fc
01b0 31342e69 36383620 534d5020 6d6f645f 14.i686 SMP mod_
01c0 756e6c6f 61642036 38362000 unload 686 .
Il est d'ailleurs possible de retracer les symboles correspondants à ces chaînes de caractères.
$ nm ipv6.ko | grep "author"
00000150 r __mod_author66
$ nm ipv6.ko | grep "alias"
00000000 r __mod_alias1315
$ nm ipv6.ko | grep "description"
00000124 r __mod_description67
$ nm ipv6.ko | grep "vermagic"
00000198 r __mod_vermagic5
$ nm ipv6.ko | grep "license"
00000118 r __mod_license68
La macro permettant de placer des symboles dans la section modinfo des modules du noyau Linux est définie à la ligne 22 du fichier moduleparam.h. Voici une version épurée de cette dernière.
#define _CAT(a, b) a ## b
#define CAT(a, b) _CAT(a, b)
#define MODINFO(tag, info) \
static const char CAT(key, __LINE__)[] \
__attribute__((section(".modinfo"))) \
__attribute__((__used__)) \
__attribute__((unused)) \
__attribute__((aligned(1))) \
= tag "=" info
Le premier attribut indique à GCC de placer cette variable statique dans la section modinfo. L'attribut __used__ indique à GCC de ne pas retirer le symbole même si celui-ci n'est pas utilisé alors que l'attribut unused indique à GCC de ne pas générer d'avertissement même si la variable est inutilisée. Finalement, l'attribut aligned(1) indique à GCC de ne pas aligner la variable.
En supposant que la macro est placée dans un en-tête nommé modinfo.h, cette dernière peut être utilisée de la manière suivante — autant au sein d'une librairie que d'un exécutable:
#include <stdio.h>
#include "library.h"
#include "modinfo.h"
MODINFO("VERSION", "1.0");
MODINFO("NAME", "Foo");
MODINFO("COMPILER", __VERSION__);
void foo() {
printf("Hello !\n");
}
Une fois ce fichier compilé, il est possible d'en extraire les informations placées dans la section modinfo de la même manière que pour les modules du noyau Linux.
$ gcc -fPIC -shared -o library.so library.c
$ modinfo library.so
filename: library.so
VERSION: 1.0
NAME: Foo
COMPILER: 4.5.1 20100924 (Red Hat 4.5.1-4)
Bref, une manière somme toute élégante et, surtout, robuste d'incorporer des métadonnées à l'intérieur d'une librairie partagée ou même d'un exécutable.