Ich habe eine Bibliothek, die ich baue. Alle meine Objekte werden nacheinander kompiliert und verknüpft, wenn ich eines der folgenden ausführe:
ar rcs lib/libryftts.a $^
gcc -shared $^ -o lib/libryftts.so
in meinem Makefile. Ich bin auch in der Lage, sie erfolgreich in zu installieren /usr/local/lib
Wenn ich die Datei mit nm teste, sind alle Funktionen da. Mein Problem ist, dass wenn ich laufe gcc testing/test.c -lryftts -o test && file ./test
oder gcc testing/test.c lib/libryftts.a -o test && file ./test
es sagt:
test: ELF 64-bit LSB shared object
Anstatt von test: ELF 64-bit LSB executable
wie ich es erwarten würde. Was mache ich falsch?
file
5.36 sagt es deutlich
file
5.36 gibt tatsächlich deutlich aus, ob die ausführbare Datei PIE ist oder nicht, wie unter: https://unix.stackexchange.com/questions/89211/how-to-test-ob-a-linux-binary-was-compiled-as-position-independent-code/435038#435038
Beispielsweise wird eine ausführbare PIE-Datei wie folgt angezeigt:
main.out: ELF 64-Bit LSB Pie ausführbar, x86-64, Version 1 (SYSV), dynamisch gelinkt, nicht entfernt
und ein Nicht-PIE als:
main.out: ELF 64-Bit LSB ausführbar, x86-64, Version 1 (SYSV), statisch gelinkt, nicht entfernt
Die Funktion wurde in 5.33 eingeführt, hat aber nur eine einfache Funktion chmod +x
überprüfen. Davor wurde nur gedruckt shared object
für TORTE.
In 5.34 sollte man anfangen, die spezialisierteren zu prüfen DF_1_PIE
ELF-Metadaten, aber aufgrund eines Fehlers in der Implementierung beim Commit 9109a696f3289ba00eaa222fd432755ec4287e28 es hat tatsächlich Dinge kaputt gemacht und ausführbare GCC PIE-Dateien als angezeigt shared objects
.
Der Fehler wurde in 5.36 beim Commit behoben 03084b161cf888b5286dbbcd964c31ccad4f64d9.
Der Fehler ist insbesondere in Ubuntu 18.10 vorhanden, das hat file
5.34.
Es manifestiert sich nicht beim Verknüpfen von Assemblycode mit ld -pie
wegen einem Zufall.
Die Aufschlüsselung des Quellcodes wird im “file
5.36 Quellcodeanalyse” Abschnitt dieser Antwort.
Der Linux-Kernel 5.0 bestimmt, ob ASLR basierend auf verwendet werden kann ET_DYN
Die eigentliche Ursache der file
“Verwirrung” besteht darin, dass sowohl ausführbare PIE-Dateien als auch gemeinsam genutzte Bibliotheken positionsunabhängig sind und an randomisierten Speicherorten platziert werden können.
Bei fs/binfmt_elf.c Der Kernel akzeptiert nur diese beiden Arten von ELF-Dateien:
/* First of all, some simple consistency checks */
if (interp_elf_ex->e_type != ET_EXEC &&
interp_elf_ex->e_type != ET_DYN)
goto out;
Dann nur für ET_DYN
setzt es das load_bias
zu etwas, das nicht Null ist. Das load_bias
ist dann das, was den ELF-Offset bestimmt: Wie wird die Adresse des Textabschnitts einer ausführbaren PIE-Datei unter Linux bestimmt?
/*
* 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;
Ich bestätige dies experimentell unter: Was ist die Option -fPIE für positionsunabhängige ausführbare Dateien in gcc und ld?
file
5.36 Verhaltenszusammenbruch
Nach dem Studium wie file
Werke aus seiner Quelle. Wir werden daraus schließen:
- wenn
Elf32_Ehdr.e_type == ET_EXEC
- sonst wenn
Elf32_Ehdr.e_type == ET_DYN
- wenn
DT_FLAGS_1
dynamischer Abschnittseintrag vorhanden ist
- wenn
DF_1_PIE
ist eingelassen DT_FLAGS_1
:
- anders
- anders
- ob Datei von Benutzer, Gruppe oder anderen ausführbar ist
- anders
Und hier sind einige Experimente, die das bestätigen:
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
Getestet in Ubuntu 18.10, GCC 8.2.0, Binutils 2.31.1.
Das vollständige Testbeispiel für jede Art von Experiment ist beschrieben unter:
ELF type
und DF_1_PIE
werden jeweils bestimmt mit:
readelf --file-header main.out | grep Type
readelf --dynamic main.out | grep FLAGS_1
file
5.36 Quellcodeanalyse
Die zu analysierende Schlüsseldatei ist Magie/Magdir/Elf.
Dieses magische Format bestimmt Dateitypen nur in Abhängigkeit von den Werten der Bytes an festen Positionen.
Das Format selbst ist dokumentiert unter:
man 5 magic
An dieser Stelle sollten Sie also die folgenden Dokumente griffbereit haben:
Gegen Ende der Datei sehen wir:
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
sind die 4 magischen Bytes am Anfang jeder ELF-Datei. \177
ist das Oktal für 0x7F
.
Dann durch den Vergleich mit der Elf32_Ehdr
struct aus dem Standard sehen wir, dass Byte 4 (das 5. Byte, das erste nach der magischen Kennung) die ELF-Klasse bestimmt:
e_ident[EI_CLASSELFCLASS]
und einige der möglichen Werte sind:
ELFCLASS32 1
ELFCLASS64 2
Im file
Quelle dann haben wir:
1 32-bit
2 64-bit
und 32-bit
und 64-bit
sind die Saiten, die file
gibt auf stdout aus!
Also suchen wir jetzt shared object
in dieser Datei, und wir werden geführt zu:
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},
Also das elf-le
ist eine Art Kennung, die im vorherigen Teil des Codes enthalten ist.
Byte 16 ist genau der ELF-Typ:
Elf32_Ehdr.e_type
und einige seiner Werte sind:
ET_EXEC 2
ET_DYN 3
Deswegen, ET_EXEC
wird immer als gedruckt executable
.
ET_DYN
hat aber zwei möglichkeiten je nach ${x
:
pie executable
shared object
${x
fragt: ist die Datei von Benutzer, Gruppe oder anderen ausführbar oder nicht? Wenn ja, zeigen pie executable
anders shared object
.
Diese Erweiterung erfolgt in der varexpand
Funktion ein 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;
Es gibt jedoch noch einen Hack! Im src/readelf.c
Funktion dodynamic
wenn die DT_FLAGS_1
flags Eintrag des dynamischen Abschnitts (PT_DYNAMIC
) vorhanden ist, dann die Berechtigungen in st->mode
werden durch das Vorhandensein oder Fehlen von außer Kraft gesetzt DF_1_PIE
Flagge:
case DT_FLAGS_1:
if (xdh_val & DF_1_PIE)
ms->mode |= 0111;
else
ms->mode &= ~0111;
break;
Der Fehler in 5.34 ist, dass der ursprüngliche Code wie folgt geschrieben wurde:
if (xdh_val == DF_1_PIE)
was bedeutet, dass wenn ein anderes Flag gesetzt wurde, was GCC standardmäßig durchführt DF_1_NOW
die ausführbare Datei wurde als angezeigt shared object
.
Das DT_FLAGS_1
Der Eintrag flags ist im ELF-Standard nicht beschrieben, daher muss es sich um eine Binutils-Erweiterung handeln.
Dieses Flag hat im Linux-Kernel 5.0 oder glibc 2.27 keine Verwendung, daher scheine ich nur informativ zu sein, um anzuzeigen, ob eine Datei PIE ist oder nicht.
Ist dein
test.c
enthältmain
?– Eugen Sch.
29. Dezember 2015 um 21:48 Uhr
@EugenSch. Ja, es enthält main.
– Lukas Smith
29. Dezember 2015 um 21:50 Uhr
Hört sich komisch an. Aber wahrscheinlich brauchen Sie mehr Informationen, um das Problem zu diagnostizieren. Schlagen Sie vor, dass Sie einen minimalen Testfall erstellen und den genauen Code und die Build-Befehle posten, die zur Erzeugung des Problems verwendet wurden. Außerdem möchten Sie vielleicht die letzte Kompilierungszeile mit ausführen
gcc -v
um genau zu sehen, welche Befehle und Optionen gcc tatsächlich verwendet.– Kaylum
29. Dezember 2015 um 21:58 Uhr
Verwandte: Absolute 32-Bit-Adressen sind in x86-64-Linux nicht mehr zulässig?
– Peter Cordes
10. Oktober 2017 um 9:06 Uhr