Deaktiviere AVX-optimierte Funktionen in glibc (LD_HWCAP_MASK, /etc/ld.so.nohwcap) für valgrind & gdb record

9

Modernes x86_64-Linux mit glibc erkennt, dass die CPU die AVX-Erweiterung unterstützt und viele String-Funktionen von der generischen Implementierung in AVX-optimierte Version (mit Hilfe von ifunc Dispatchern: 1 , 2 ).

Diese Funktion kann für die Leistung gut sein, aber sie verhindert mehrere Tools wie Valgrind ( ältere libVEXs ) , bevor valgrind-3.8 ) und gdbs " target record " ( Umgekehrte Ausführung ) funktioniert nicht richtig (Ubuntu" Z "17.04 beta, gdb 7.12 .50.20170207-0ubuntu2, gcc 6.3.0- 8ubuntu1 20170221, Ubuntu GLIBC 2.24-7ubuntu2):

%Vor%

Es gibt die Fehlermeldung " Process record does not support instruction 0xc5 " aus der gdb-Implementierung von "target record", weil AVX-Anweisungen nicht von der Record / Replay-Engine unterstützt werden (manchmal wird das Problem an _dl_runtime_resolve_avx function erkannt): Ссылка "Einige AVX-Anweisungen werden von der Prozessaufzeichnung nicht unterstützt", Ссылка ,

Lösung vorgeschlagen in Ссылка "Sie können libc (also ld.so) neu kompilieren, oder hack __init_cpu_features und somit __cpu_features zur Laufzeit (siehe zB strcmp). " oder setze LD_BIND_NOW=1 , aber rekompilierte glibc hat immer noch AVX, und ld bind - hilft jetzt nicht.

Ich habe gehört, dass es in glibc /etc/ld.so.nohwcap und LD_HWCAP_MASK Konfigurationen gibt. Können sie verwendet werden, um die ifunc-Verteilung an AVX-optimierte String-Funktionen in glibc zu deaktivieren?

Wie erkennt glibc (rtld?) AVX, indem es cpuid , mit /proc/cpuinfo (wahrscheinlich nicht) oder HWCAP aux ( LD_SHOW_AUXV=1 /bin/echo |grep HWCAP gibt AT_HWCAP: bfebfbff )?

    
osgx 25.02.2017, 03:01
quelle

3 Antworten

2

Nicht die beste oder vollständige Lösung, nur ein kleiner Bit-Editing-Klud, um valgrind und gdb record für meine Aufgabe zuzulassen.

Lekensteyn fragt :

  

wie man AVX / SSE ausblendet, ohne glibc neu zu kompilieren

Ich habe den unmodifizierten glibc vollständig neu erstellt, was in debian und ubuntu ziemlich einfach ist: nur sudo apt-get source glibc , sudo apt-get build-dep glibc und cd glibc-*/; dpkg-buildpackage -us -uc ( manual , um die ld.so ohne entpackte Debugging-Informationen zu erhalten.

Dann habe ich ein binäres (Bit-) Patchen der Ausgabedatei ld.so in der von __get_cpu_features verwendeten Funktion durchgeführt. Zielfunktion wurde zusammengestellt von get_common_indeces der Quelldatei sysdeps/x86/cpu-features.c unter dem Namen get_common_indeces.constprop.1 (direkt nach dem __get_cpu_features im Binärcode). Es hat mehrere cpuids, erste ist cpuid eax=1 "Prozessor-Info und Feature-Bits" ; und später gibt es „jle 0x6“ überprüfen und abspringen um den Code " cpuid eax=7 ecx=0 Gesamt Features ", um den AVX2-Status zu erhalten. Da ist der Code, der in diese Logik übersetzt wurde:

%Vor%

Das cpu_features->max_cpuid wurde in init_cpu_features der die gleiche Datei in __cpuid (0, cpu_features->max_cpuid, ebx, ecx, edx); line. Es war einfacher, die if -Anweisung zu deaktivieren, indem jle nach cmp 0x6 durch jg (Byte 0x7e bis 0x7f) ersetzt wurde. (Tatsächlich wurde dieser Binär-Patch erneut manuell auf die Funktion __get_cpu_features des realen Systems ld-linux.so.2 angewendet - zuerst jle, bevor mov 7 eax; xor ecx,ecx; cpuid in jg geändert wurde.)

Das neu kompilierte Paket und das modifizierte ld.so wurden nicht im System installiert; Ich habe die Befehlszeilensyntax von ld.so ./my_program (oder mv ld.so /some/short/path.so und patchelf --set-interpreter ./my_program ) verwendet.

Andere mögliche Lösungen:

  • versuche, neuere valgrind & amp; gdb Record Tools
  • versuche, älteren glibc
  • zu verwenden
  • implementiert fehlende Anweisungsemulation in gdb-Datensatz, falls dies nicht bereits erfolgt ist
  • Quellcode um if (cpu_features->max_cpuid >= 7) in glibc patchen und
  • neu kompilieren
  • Machen Sie Quelltext um Avx2-fähige String-Funktionen in glibc patchen und kompilieren Sie
osgx 10.06.2017 00:59
quelle
2

Es scheint keine einfache Laufzeitmethode zum Patchen der Feature-Erkennung zu geben. Diese Erkennung erfolgt ziemlich früh im dynamischen Linker (ld.so).

Das binäre Patchen des Linkers scheint die einfachste Methode zu sein. @osgx beschrieb eine Methode, bei der ein Sprung überschrieben wird. Ein anderer Ansatz besteht darin, das CPU-Ergebnis zu fälschen. Normalerweise gibt cpuid(eax=0) die höchste unterstützte Funktion in eax zurück, während die Hersteller-IDs in den Registern ebx, ecx und edx zurückgegeben werden . Wir haben dieses Snippet in glibc 2.25 sysdeps/x86/cpu-features.c :

%Vor%

Die Zeile __cpuid übersetzt diese Anweisungen in /lib/ld-linux-x86-64.so.2 ( /lib/ld-2.25.so ):

%Vor%

Anstatt also Zweige zu patchen, könnten wir auch die cpuid in eine nop Anweisung ändern, was zum Aufruf des letzten else Zweigs führen würde (da die Register kein "GenuineIntel" enthalten). Da anfangs auch eax=0 , cpu_features->max_cpuid ebenfalls 0 sind, wird auch if (cpu_features->max_cpuid >= 7) umgangen.

Binäres Patchen cpuid(eax=0) by nop Dies kann mit diesem Dienstprogramm durchgeführt werden (funktioniert sowohl für x86 als auch für x86-64):

%Vor%

Das war der einfache Teil. Jetzt wollte ich den systemweiten dynamischen Linker nicht ersetzen, sondern nur ein bestimmtes Programm mit diesem Linker ausführen. Sicher, das kann mit ./ld-linux-x86-64-patched.so.2 ./a gemacht werden, aber die naiven gdb-Aufrufe konnten keine Haltepunkte setzen:

%Vor%

Eine manuelle Problemumgehung ist in beschrieben. Wie Programm mit benutzerdefinierten Elf Interpreter zu debuggen? Es funktioniert, aber es ist leider ein manuelle Aktion mit add-symbol-file . Es sollte möglich sein, es ein bisschen zu automatisieren, indem man GDB Catchpoints verwendet.

Ein alternativer Ansatz, der keine binäre Verknüpfung bietet, ist LD_PRELOAD in einer Bibliothek, die benutzerdefinierte Routinen für memcpy , memove usw. definiert. Diese hat dann Vorrang vor den glibc-Routinen. Die vollständige Liste der Funktionen finden Sie in sysdeps/x86_64/multiarch/ifunc-impl-list.c . Der aktuelle HEAD hat mehr Symbole im Vergleich zur glibc 2.25 Version ( grep -Po 'IFUNC_IMPL \(i, name, \K[^,]+' sysdeps/x86_64/multiarch/ifunc-impl-list.c ):

  

memchr,   memcmp,   __memove_chk,   behüte,   memrchr,   __memset_chk,   memset,   rawmemchr,   streichen,   strnlen,   stpncpy,   stpcpy,   strasecmp,   strcasecmp_l,   strcat,   strchr,   strchrnul,   strrchr,   strcmp,   strcpy,   strcspn,   strncasecmp,   strncasecmp_l,   strncat,   strncpy,   strpbrk,   strspn,   strstr,   wcschr,   wcsrchr,   wcscpy,   wcslen,   wcsnlen,   wmemchr,   wmemcmp,   wmemset,   __memcpy_chk,   memcpy,   __mempcpy_chk,   mempcpy,   strncmp,   __wmemset_chk,

    
Lekensteyn 11.06.2017 11:47
quelle
0
  

Ich habe gehört, dass es in glibc /etc/ld.so.nohwcap und LD_HWCAP_MASK Konfigurationen gibt. Können sie verwendet werden, um die ifunc-Verteilung an AVX-optimierte String-Funktionen in glibc zu deaktivieren?

Ja: Wenn Sie LD_HWCAP_MASK=0 einstellen, wird GLIBC so tun, als ob keine CPU-Fähigkeiten verfügbar sind. Code .

Wenn Sie die Maske auf 0 setzen, wird wahrscheinlich ein Fehler ausgelöst. Sie müssen wahrscheinlich das genaue Bit herausfinden, das AVX steuert, und nur dieses Bit maskieren.

    
Employed Russian 25.02.2017 16:45
quelle

Tags und Links