Als Fedora-User war RPM Fusion mein Repository der Wahl für Grafiktreiber, Codecs und andere Software, die nicht im offiziellen Fedora-Repo vorhanden war. Nach meinem Wechsel auf EL7 (erst Scientific Linux, dann CentOS) musste ich mir eine neue Quelle dafür suchen, denn RPM Fusion hat EL7 nicht unterstützt. Doch jetzt, mehr als 2 Jahre nach dem Erscheinen von RHEL7, ist RPM Fusion auch für EL7 verfügbar. Aber noch nicht so ganz, denn die Infrastruktur besteht zwar bereits, die Anzahl der Pakete ist hingegen sehr überschaubar. Das dürfte sich aber in den nächsten Wochen, Monaten oder Jahrhunderten hoffentlich ändern.
Okay, genug gelästert. Ich danke allen Beteiligten für ihre Arbeit.
Oracle hat eine neue Roadmap für SPARC und Solaris veröffentlicht, und wie erwartet kommt dort kein Solaris 12 mehr vor. Stattdessen setzt Oracle auf ein Continuous Delivery Modell, so dass neue Features direkt in Solaris 11 landen.
Das ist zwar einerseits ein gutes Konzept, andererseits will Oracle sicherlich auch Entwicklungskosten drücken. Es bleibt abzuwarten ob das nächste Solaris 11 Release die Neuerungen erhält, die Oracle bisher für Solaris 12 schon angekündigt hatte.
Ich hab mich schon öfter gefragt, was man alles tun muss, um einen JIT-Compiler zu schreiben. Oder anders gefragt, wie kann man zur Laufzeit Maschinencode generieren und ausführen.
Zunächst einmal benötigt man Speicher, der es überhaupt erlaubt, dass davon Code ausgeführt werden kann. Wenn man mit malloc Speicher alloziert, ist dieser nicht ausführbar. Intern verwendet malloc mmap und das kann auch einfach direkt genutzt werden. Dabei kann man direkt die Zugriffsrechte für den Speicher festlegen. Man könnte sie allerdings auch im nachhinein mit mprotect ändern. Mit mmap werden auch Dateien in den Speicher gemapped, durch das Flag MAP_ANONYMOUS liefert der Kernel aber ganz ohne Datei den gewünschten Speicher.
void *execmem = mmap(NULL, len, PROT_EXEC | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
Jetzt haben wir ausführbaren Speicher. Wenn dort Instructions reingeladen werden, können diese ausgeführt werden. Mein erster Versuch war einfach mit memcpy von einem Function-Pointer dort ein paar Bytes rein zu kopieren. Das hat zwar funktioniert, auch wenn ich nicht wusste wie viele Bytes die Funktion eigentlich groß ist und ich einfach eine größere Menge kopiert habe, aber das wäre für diesen ohnehin schon etwas hackigen Blogartikel etwas zu unsauber.
Glücklicherweise hat Intel kürzlich eine Bibliothek für das decoden und encoden von x86-Maschinencode veröffentlicht. Damit ist es mir gelungen nur die Instructions der Funktion zu kopieren.
Um den Maschinencode zu decoden braucht man erstmal einen Pointer auf den Code, in meinem Fall einen Funktions-Pointer.
const xed_uint8_t *inst = (const xed_uint8_t*)func;
Einen Befehl decoden macht folgender Code:
xed_decoded_inst_t dec;
xed_error_enum_t error;
// init xed_decoded_inst_t struct
memset(&dec, '\0', sizeof(xed_decoded_inst_t));
xed_decoded_inst_set_mode(&dec, XED_MACHINE_MODE_LONG_64, XED_ADDRESS_WIDTH_64b);
// decode instruction
error = xed_decode(&dec, inst, 15);
Dies liest 15 Bytes (die Maximalgröße einer Instruction) und dekodiert die Instruction. Wie groß diese dann ist, kann mit xed_decoded_inst_get_length abgefragt werden. Mit der Länge kann man dann zur nächsten Instruction springen.
Man kann diese auch als Assembler-String formatieren:
// print instruction
xed_format_context(XED_SYNTAX_ATT, &dec, buffer, 1024, 0, 0, 0);
printf("%s\n", buffer);
Hier ist das fertige Beispielprogramm. Kompiliere ich das ohne Optimierung und führe es aus ist die Ausgabe:
$ ./x86dec
copy function code:
pushq %rbp
mov %rsp, %rbp
movl %edi, -0x4(%rbp)
movl %esi, -0x8(%rbp)
movl -0x8(%rbp), %eax
movl -0x4(%rbp), %edx
add %edx, %eax
popq %rbp
retq
execute new code:
f(10, 50) = 60
Das ganze erfüllt jetzt natürlich keinen Zweck. Spannend wird es erst, wenn man eigenen Code generiert und den dann ausführt.
Was man noch erwähnen sollte ist, dass CPUs separate Data- und Instruction-Caches haben. Glücklicherweise muss man sich bei x86-CPUs keine Sorgen darüber machen, da dort erkannt wird, wenn Speicher modifiziert wird, der gerade auch im Instruction-Cache ist. Hingegen bei RISC-Architekturen, z.B. ARM, muss meistens der Instruction-Cache manuell aktualisiert werden.
Siehe auch: Self-Modifying Code and the Cache on Intel x86
Wenn man ein Verzeichnis liest und von allen enthaltenen Dateien die Extended Attributes erhalten will, gibt es zwei Möglichkeiten:
- Man fügt zum Verzeichnispfad den Dateinamen hinzu und nutzt den neu erhaltenen Pfad mit den Syscalls
listxattr oder getxattr.
- Mit dem Filedescriptor des Verzeichnisses und
openat öffnet man die Dateien und nutzt dann flistxattr und getxattr.
Ich hab mich gefragt was schneller ist. Dazu habe ich ein kleines Testprogramm geschrieben. Dieses kann mit unterschiedlichen Preprocessor-Optionen kompiliert werden. So habe ich 4 Testprogramme erstellt. Für getxattr und fgetxattr jeweils ein Programm, das ein Attribut liest und eines das 32 Attribute liest.
Bei einem Verzeichnis mit 128.000 Dateien hab ich folgende Werte erhalten:
getxattr:1 getxattr:32 fgetattr:1 fgetattr:32
------------------------------------------------
246100055 654704421 456172044 749849574
230183311 663632162 457183706 769223423
247109480 654775136 440397212 743349119
Die Datei erst zu öffnen um dann fgetxattr zu nutzen ist also langsamer. Erst als ich das Programm so modifiziert habe, dass es mehrere hundert Attribute liest, war es etwas schneller. Das ist jedoch ein eher unrealistisches Szenario. Allerdings war das ganze generell sehr schnell, so dass es eigentlich egal ist, welche Methode man anwendet.
Kommentare
dev | Artikel: Datei ver- und entschlüsseln mit openssl - kompatibel mit dav
Andreas | Artikel: Datenanalyse in der Shell Teil 1: Basis-Tools
Einfach und cool!
Danke Andreas
Rudi | Artikel: Raspberry Pi1 vs Raspberry Pi4 vs Fujitsu s920 vs Sun Ultra 45
Peter | Artikel: XNEdit - Mein NEdit-Fork mit Unicode-Support
Damit wird Nedit durch XNedit ersetzt.
Danke!
Olaf | Artikel: XNEdit - Mein NEdit-Fork mit Unicode-Support
Anti-Aliasing hängt von der Schriftart ab. Mit einem bitmap font sollte die Schrift klassisch wie in nedit aussehen.
Einfach unter Preferences -> Default Settings -> Text Fonts nach einer passenden Schriftart suchen.
Peter | Artikel: XNEdit - Mein NEdit-Fork mit Unicode-Support
Mettigel | Artikel: Raspberry Pi1 vs Raspberry Pi4 vs Fujitsu s920 vs Sun Ultra 45
Ich hatte gedacht, dass der GX-415 im s920 deutlich mehr Dampf hat als der Raspi4.
Mein Thinclient verbraucht mit 16 GB RAM ~11 W idle, das ist das Dreifache vom RP4. Das muss man dem kleinen echt lassen... Sparsam ist er.
Olaf | Artikel: Raspberry Pi1 vs Raspberry Pi4 vs Fujitsu s920 vs Sun Ultra 45
Ergebnisse von der Ultra 80 wären natürlich interessant, insbesondere im Vergleich mit dem rpi1.