Vous pouvez également utiliser le file
commande (et objdump
pourrait également être utile).
Vérifiez s'il a un en-tête de programme de type INTERP
Au niveau inférieur, un exécutable est statique s'il n'a pas d'en-tête de programme de type :
Elf32_Phd.p_type == PT_INTERP
Ceci est mentionné dans la spécification System V ABI.
Rappelez-vous que les en-têtes de programme déterminent les segments ELF, y compris ceux de type PT_LOAD
qui sera chargé en mémoire et exécuté.
Si cet en-tête est présent, son contenu correspond exactement au chemin du chargeur dynamique.
readelf
vérifier
Nous pouvons observer cela avec readelf
. Commencez par compiler dynamiquement un monde C hello :
gcc -o main.out main.c
puis :
readelf --program-headers --wide main.out
sorties :
Elf file type is DYN (Shared object file)
Entry point 0x1050
There are 11 program headers, starting at offset 64
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
PHDR 0x000040 0x0000000000000040 0x0000000000000040 0x000268 0x000268 R 0x8
INTERP 0x0002a8 0x00000000000002a8 0x00000000000002a8 0x00001c 0x00001c R 0x1
[Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
LOAD 0x000000 0x0000000000000000 0x0000000000000000 0x000560 0x000560 R 0x1000
LOAD 0x001000 0x0000000000001000 0x0000000000001000 0x0001bd 0x0001bd R E 0x1000
LOAD 0x002000 0x0000000000002000 0x0000000000002000 0x000150 0x000150 R 0x1000
LOAD 0x002db8 0x0000000000003db8 0x0000000000003db8 0x000258 0x000260 RW 0x1000
DYNAMIC 0x002dc8 0x0000000000003dc8 0x0000000000003dc8 0x0001f0 0x0001f0 RW 0x8
NOTE 0x0002c4 0x00000000000002c4 0x00000000000002c4 0x000044 0x000044 R 0x4
GNU_EH_FRAME 0x00200c 0x000000000000200c 0x000000000000200c 0x00003c 0x00003c R 0x4
GNU_STACK 0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000 RW 0x10
GNU_RELRO 0x002db8 0x0000000000003db8 0x0000000000003db8 0x000248 0x000248 R 0x1
Section to Segment mapping:
Segment Sections...
00
01 .interp
02 .interp .note.ABI-tag .note.gnu.build-id .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt
03 .init .plt .plt.got .text .fini
04 .rodata .eh_frame_hdr .eh_frame
05 .init_array .fini_array .dynamic .got .data .bss
06 .dynamic
07 .note.ABI-tag .note.gnu.build-id
08 .eh_frame_hdr
09
10 .init_array .fini_array .dynamic .got
alors notez le INTERP
header est là, et il est si important que readelf
a même donné un aperçu rapide de son court contenu de 28 octets (0x1c) :/lib64/ld-linux-x86-64.so.2
, qui est le chemin vers le chargeur dynamique (27 octets de long + 1 pour \0
).
Notez comment cela réside côte à côte avec les autres segments, y compris par ex. ceux qui sont réellement chargés en mémoire tels que :.text
.
Nous pouvons ensuite extraire plus directement ces octets sans l'aperçu avec :
readelf -x .interp main.out
ce qui donne :
Hex dump of section '.interp':
0x000002a8 2f6c6962 36342f6c 642d6c69 6e75782d /lib64/ld-linux-
0x000002b8 7838362d 36342e73 6f2e3200 x86-64.so.2.
comme expliqué à :Comment puis-je examiner le contenu d'une section de données d'un fichier ELF sous Linux ?
file
code source
file
5.36 Les commentaires du code source sur src/readelf.c affirment qu'il vérifie également PT_INTERP
:
/*
* Look through the program headers of an executable image, searching
* for a PT_INTERP section; if one is found, it's dynamically linked,
* otherwise it's statically linked.
*/
private int
dophn_exec(struct magic_set *ms, int clazz, int swap, int fd, off_t off,
int num, size_t size, off_t fsize, int sh_num, int *flags,
uint16_t *notecount)
{
Elf32_Phdr ph32;
Elf64_Phdr ph64;
const char *linking_style = "statically";
trouvé avec git grep statically
du message main.out: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, not stripped
.
Cependant, ce commentaire semble être obsolète par rapport au code, qui vérifie à la place PT_DYNAMIC
:
case PT_DYNAMIC:
linking_style = "dynamically";
doread = 1;
break;
Je ne sais pas pourquoi cela est fait, et j'ai la flemme de creuser dans git log
à présent. En particulier, cela m'a un peu dérouté lorsque j'ai essayé de créer un exécutable PIE lié statiquement avec --no-dynamic-linker
comme indiqué sur:Comment créer un ELF exécutable indépendant de la position lié statiquement sous Linux? qui n'a pas PT_INTERP
mais a PT_DYNAMIC
, et que je ne m'attends pas à utiliser le chargeur dynamique.
J'ai fini par faire une analyse de source plus approfondie pour -fPIE
at :Pourquoi GCC crée-t-il un objet partagé au lieu d'un binaire exécutable en fonction du fichier ? la réponse s'y trouve probablement aussi.
Code source du noyau Linux
Le noyau Linux 5.0 lit le fichier ELF lors de l'appel système exec à fs/binfmt_elf.c comme expliqué à :Comment le noyau obtient-il un fichier binaire exécutable fonctionnant sous linux ?
Le noyau boucle sur les en-têtes de programme à load_elf_binary
for (i = 0; i < loc->elf_ex.e_phnum; i++) {
if (elf_ppnt->p_type == PT_INTERP) {
/* This is the program interpreter used for
* shared libraries - for now assume that this
* is an a.out format binary
*/
Je n'ai pas entièrement lu le code, mais je m'attendrais alors à ce qu'il n'utilise le chargeur dynamique que si INTERP
est trouvé, sinon quel chemin doit-il utiliser ?
PT_DYNAMIC
n'est pas utilisé dans ce fichier.
Bonus :vérifiez si -pie
a été utilisé
J'ai expliqué cela en détail sur :Pourquoi GCC crée-t-il un objet partagé au lieu d'un binaire exécutable en fonction du fichier ?
ldd /path/to/binary
ne doit pas répertorier les bibliothèques partagées si le binaire est compilé statiquement.