Sowohl GCC als auch Clang haben UndefinedBehaviorSanitizer eingebaut. Eine dieser Prüfungen, alignment
, kann mit -fsanitize=alignment
aktiviert werden . Es wird Code ausgeben, um die Zeigerausrichtung zur Laufzeit zu prüfen und abzubrechen, wenn nicht ausgerichtete Zeiger dereferenziert werden.
Siehe Online-Dokumentation unter:
- https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html
Es ist knifflig und ich habe es noch nicht persönlich gemacht, aber ich denke, Sie können es auf folgende Weise machen:
x86_64-CPUs (insbesondere habe ich Intel Corei7 überprüft, aber ich denke auch andere) haben einen Leistungszähler MISALIGN_MEM_REF, der falsch ausgerichteten Speicherreferenzen entgegenwirkt.
Sie können also zunächst Ihr Programm ausführen und das "perf"-Tool unter Linux verwenden, um die Anzahl der falsch ausgerichteten Zugriffe zu ermitteln, die Ihr Code ausgeführt hat.
Ein kniffligerer und interessanterer Hack wäre, ein Kernelmodul zu schreiben, das den Leistungszähler so programmiert, dass er bei Überlauf einen Interrupt generiert und ihn dazu bringt, beim ersten nicht ausgerichteten Laden/Speichern überzulaufen. Reagieren Sie auf diesen Interrupt in Ihrem Kernel-Modul, aber senden Sie ein Signal an Ihren Prozess.
Dies wird den x86_64 tatsächlich in einen Kern verwandeln, der keinen nicht ausgerichteten Zugriff unterstützt.
Dies wird jedoch nicht einfach sein - neben Ihrem Code verwenden die Systembibliotheken auch nicht ausgerichtete Zugriffe, daher wird es schwierig sein, sie von Ihrem eigenen Code zu trennen.
Ich habe gerade die Frage gelesen. Verursacht ein nicht ausgerichteter Speicherzugriff immer Busfehler? die auf den Wikipedia-Artikel Segmentierungsfehler verlinkt ist.
In dem Artikel gibt es eine wunderbare Erinnerung an eher ungewöhnliche Intel-Prozessor-Flags AC alias Alignment Check.
Und hier ist, wie man es aktiviert (aus Wikipedias Bus Error-Beispiel, mit einem Red-Zone-Clobber-Bug, der für x86-64 System V behoben wurde, damit dies unter Linux und MacOS sicher ist, und von Basic asm konvertiert wird, was innerhalb von Funktionen nie eine gute Idee ist:Sie möchten, dass Änderungen an AC bezüglich Speicherzugriffen angeordnet werden.
#if defined(__GNUC__)
# if defined(__i386__)
/* Enable Alignment Checking on x86 */
__asm__("pushf\n orl $0x40000,(%%esp)\n popf" ::: "memory");
# elif defined(__x86_64__)
/* Enable Alignment Checking on x86_64 */
__asm__("add $-128, %%rsp \n" // skip past the red-zone, in case there is one and the compiler has local vars there.
"pushf\n"
"orl $0x40000,(%%rsp)\n"
"popf \n"
"sub $-128, %%rsp" // and restore the stack pointer.
::: "memory"); // ordered wrt. other mem access
# endif
#endif
Einmal aktiviert, funktioniert es ähnlich wie die ARM-Ausrichtungseinstellungen in /proc/cpu/alignment
, siehe Antwort How to trap unaligned memory access? für Beispiele.
Wenn Sie GCC verwenden, schlage ich außerdem vor, dass Sie -Wcast-align
aktivieren Warnungen. Beim Erstellen für ein Ziel mit strengen Ausrichtungsanforderungen (z. B. ARM) meldet GCC Stellen, die zu einem nicht ausgerichteten Speicherzugriff führen könnten.
Beachten Sie jedoch, dass das handgeschriebene asm von libc für memcpy und andere Funktionen weiterhin nicht ausgerichtete Zugriffe durchführt, sodass das Setzen von AC auf x86 (einschließlich x86-64) oft nicht praktikabel ist. GCC gibt manchmal asm aus, das nicht ausgerichtete Zugriffe macht, auch wenn Ihre Quelle dies nicht tut, z. als Optimierung, um zwei benachbarte Array-Elemente oder Strukturmitglieder gleichzeitig zu kopieren oder zu nullen.