J'ai une bibliothèque que je construis. Tous mes objets se compilent et se lient successivement lorsque j'exécute l'un des deux: ar rcs lib/libryftts.a $^
gcc -shared $^ -o lib/libryftts.so
dans mon Makefile. Je peux également les installer avec succès dans /usr/local/lib
Lorsque je teste le fichier avec nm, toutes les fonctions sont là. Mon problème est que lorsque j'exécute gcc testing/test.c -lryftts -o test && file ./test
ou gcc testing/test.c lib/libryftts.a -o test && file ./test
ça dit:
test: ELF 64-bit LSB shared object
au lieu de test: ELF 64-bit LSB executable
comme je m'y attendais. Qu'est-ce que je fais mal?
Qu'est-ce que je fais mal?
Rien.
Il semble que votre GCC soit configuré pour construire -pie
binaires par défaut. Ces binaires sont vraiment sont des bibliothèques partagées (de type ET_DYN
), sauf qu'ils fonctionnent comme un exécutable normal.
Donc, vous devez simplement exécuter votre binaire, et (si cela fonctionne) ne vous inquiétez pas.
Ou vous pouvez lier votre binaire avec gcc -no-pie ...
et cela devrait produire un exécutable nonPIE
de type ET_EXEC
, pour lequel file
dira ELF 64-bit LSB executable
.
file
5.36 le dit clairement
file
5.36 l'imprime clairement si l'exécutable est PIE ou non comme indiqué sur: https://unix.stackexchange.com/questions/89211/how-to-test-whether-a- linux-binary-was-compiled-as-position-indépendant-code/435038 # 435038
Par exemple, un exécutable PIE s'affiche comme suit:
main.out: exécutable ELF 64-bit pie tarte, x86-64, version 1 (SYSV), lié dynamiquement, non supprimé
et un non-TARTE comme:
main.out: exécutable LSF 64 bits ELF, x86-64, version 1 (SYSV), lié statiquement, non supprimé
La fonctionnalité a été introduite dans la version 5.33, mais elle n'a fait qu'une simple vérification chmod +x
. Avant cela, il venait juste d'imprimer shared object
Pour PIE.
Dans 5.34, il était censé commencer à vérifier les métadonnées ELF DF_1_PIE
Plus spécialisées, mais en raison d'un bogue dans l'implémentation lors de la validation 9109a696f3289ba00eaa222fd432755ec4287e28 il a en fait cassé les choses et montré les exécutables GCC PIE comme shared objects
.
Le bogue a été corrigé dans 5.36 lors de la validation 084b161cf888b5286dbbcd964c31ccad4f64d9 .
Le bogue est présent notamment dans Ubuntu 18.10 qui a file
5.34.
Il ne se manifeste pas lors de la liaison du code Assembly avec ld -pie
En raison d'une coïncidence.
La ventilation du code source est indiquée dans la section "file
5.36 analyse du code source" de cette réponse.
Le noyau Linux 5.0 détermine si ASLR peut être utilisé en fonction de ET_DYN
La cause première de la "confusion" file
est que les bibliothèques exécutables PIE et partagées sont indépendantes de la position et peuvent être placées dans des emplacements de mémoire aléatoires.
A fs/binfmt_elf.c le noyau accepte uniquement ces deux types de fichiers ELF:
/* First of all, some simple consistency checks */
if (interp_elf_ex->e_type != ET_EXEC &&
interp_elf_ex->e_type != ET_DYN)
goto out;
Ensuite, ce n'est que pour ET_DYN
Que le paramètre load_bias
Est différent de zéro. Le load_bias
Est alors ce qui détermine le décalage ELF: Comment l'adresse de la section de texte d'un exécutable PIE est-elle déterminée sous Linux?
/*
* If we are loading ET_EXEC or we have already performed
* the ET_DYN load_addr calculations, proceed normally.
*/
if (loc->elf_ex.e_type == ET_EXEC || load_addr_set) {
elf_flags |= elf_fixed;
} else if (loc->elf_ex.e_type == ET_DYN) {
/*
* This logic is run once for the first LOAD Program
* Header for ET_DYN binaries to calculate the
* randomization (load_bias) for all the LOAD
* Program Headers, and to calculate the entire
* size of the ELF mapping (total_size). (Note that
* load_addr_set is set to true later once the
* initial mapping is performed.)
*
* There are effectively two types of ET_DYN
* binaries: programs (i.e. PIE: ET_DYN with INTERP)
* and loaders (ET_DYN without INTERP, since they
* _are_ the ELF interpreter). The loaders must
* be loaded away from programs since the program
* may otherwise collide with the loader (especially
* for ET_EXEC which does not have a randomized
* position). For example to handle invocations of
* "./ld.so someprog" to test out a new version of
* the loader, the subsequent program that the
* loader loads must avoid the loader itself, so
* they cannot share the same load range. Sufficient
* room for the brk must be allocated with the
* loader as well, since brk must be available with
* the loader.
*
* Therefore, programs are loaded offset from
* ELF_ET_DYN_BASE and loaders are loaded into the
* independently randomized mmap region (0 load_bias
* without MAP_FIXED).
*/
if (elf_interpreter) {
load_bias = ELF_ET_DYN_BASE;
if (current->flags & PF_RANDOMIZE)
load_bias += Arch_mmap_rnd();
elf_flags |= elf_fixed;
} else
load_bias = 0;
Je confirme cela expérimentalement à: Quelle est l'option -fPIE pour les exécutables indépendants de la position dans gcc et ld?
file
5.36 répartition des comportements
Après avoir étudié le fonctionnement de file
à partir de sa source. Nous conclurons que:
Elf32_Ehdr.e_type == ET_EXEC
executable
Elf32_Ehdr.e_type == ET_DYN
DT_FLAGS_1
l'entrée de section dynamique est présente DF_1_PIE
est défini dans DT_FLAGS_1
: pie executable
shared object
pie executable
shared object
Et voici quelques expériences qui confirment que:
Executable generation ELF type DT_FLAGS_1 DF_1_PIE chdmod +x file 5.36
--------------------------- -------- ---------- -------- -------------- --------------
gcc -fpie -pie ET_DYN y y y pie executable
gcc -fno-pie -no-pie ET_EXEC n n y executable
gcc -shared ET_DYN n n y pie executable
gcc -shared ET_DYN n n n shared object
ld ET_EXEC n n y executable
ld -pie --dynamic-linker ET_DYN y y y pie executable
ld -pie --no-dynamic-linker ET_DYN y y y pie executable
Testé dans Ubuntu 18.10, GCC 8.2.0, Binutils 2.31.1.
L'exemple de test complet pour chaque type d'expérience est décrit à l'adresse suivante:
gcc -pie
Et gcc -no-pie
: Quelle est l'option -fPIE pour les exécutables indépendants de la position dans gcc et ld?
Gardez à l'esprit que -pie
Est activé par défaut depuis Ubuntu 17.10, lié: les adresses absolues 32 bits ne sont plus autorisées sous Linux x86-64?
gcc -shared
(.so
Bibliothèque partagée): https://github.com/cirosantilli/cpp-cheat/tree/b80ccb4a842db52d719a16d3716b02b684ebbf11/shared_library/basic
ld
expériences: Comment créer un ELF exécutable indépendant de position lié statiquement sous Linux?ELF type
Et DF_1_PIE
Sont déterminés respectivement avec:
readelf --file-header main.out | grep Type
readelf --dynamic main.out | grep FLAGS_1
file
5.36 analyse du code source
Le fichier clé à analyser est magic/Magdir/elf .
Ce format magique détermine les types de fichiers en fonction uniquement des valeurs d'octets à des positions fixes.
Le format lui-même est documenté à:
man 5 magic
Donc, à ce stade, vous voudrez avoir les documents suivants à portée de main:
Vers la fin du dossier, on voit:
0 string \177ELF ELF
!:strength *2
>4 byte 0 invalid class
>4 byte 1 32-bit
>4 byte 2 64-bit
>5 byte 0 invalid byte order
>5 byte 1 LSB
>>0 use elf-le
>5 byte 2 MSB
>>0 use \^elf-le
\177ELF
Sont les 4 octets magiques au début de chaque fichier ELF. \177
Est l'octal de 0x7F
.
Ensuite, en comparant avec la structure Elf32_Ehdr
Du standard, nous voyons que l'octet 4 (le 5ème octet, le premier après l'identifiant magique), détermine la classe ELF:
e_ident[EI_CLASSELFCLASS]
et certaines de ses valeurs possibles sont:
ELFCLASS32 1
ELFCLASS64 2
Dans file
source alors, nous avons:
1 32-bit
2 64-bit
et 32-bit
et 64-bit
sont les chaînes que file
sort vers stdout!
Alors maintenant, nous recherchons shared object
Dans ce fichier, et nous sommes amenés à:
0 name elf-le
>16 leshort 0 no file type,
!:mime application/octet-stream
>16 leshort 1 relocatable,
!:mime application/x-object
>16 leshort 2 executable,
!:mime application/x-executable
>16 leshort 3 ${x?pie executable:shared object},
Donc, elf-le
Est une sorte d'identifiant qui est inclus dans la partie précédente du code.
L'octet 16 est exactement le type ELF:
Elf32_Ehdr.e_type
et certaines de ses valeurs sont:
ET_EXEC 2
ET_DYN 3
Par conséquent, ET_EXEC
Est toujours imprimé sous la forme executable
.
ET_DYN
A cependant deux possibilités selon ${x
:
pie executable
shared object
${x
Demande: le fichier est-il exécutable ou non par un utilisateur, un groupe ou autre? Si oui, affichez pie executable
, Sinon shared object
.
Cette expansion se fait dans la fonction varexpand
dans src/softmagic.c
:
static int
varexpand(struct magic_set *ms, char *buf, size_t len, const char *str)
{
[...]
case 'x':
if (ms->mode & 0111) {
ptr = t;
l = et - t;
} else {
ptr = e;
l = ee - e;
}
break;
Il y a cependant un hack de plus! Dans src/readelf.c
fonction dodynamic
, si DT_FLAGS_1
Marque l'entrée de la section dynamique (PT_DYNAMIC
) Est présente, alors les autorisations dans st->mode
sont remplacés par la présence ou l'absence du drapeau DF_1_PIE
:
case DT_FLAGS_1:
if (xdh_val & DF_1_PIE)
ms->mode |= 0111;
else
ms->mode &= ~0111;
break;
Le bogue de 5.34 est que le code initial a été écrit comme suit:
if (xdh_val == DF_1_PIE)
ce qui signifie que si un autre indicateur a été défini, ce que GCC fait par défaut en raison de DF_1_NOW
, l'exécutable s'affiche comme shared object
.
L'entrée de drapeaux DT_FLAGS_1
N'est pas décrite dans la norme ELF, elle doit donc être une extension Binutils.
Ce drapeau n'a aucune utilité dans le noyau Linux 5.0 ou la glibc 2.27, donc je semble être purement informatif pour indiquer qu'un fichier est PIE ou non.