Diskussion Bug bei C und dem Windows Subsystem für Linux (WSL)

german

Aktives Mitglied
devCommunity-Experte
Ich habe ein paar Probleme festgestellt, wenn ich ELF Executables in WSL ausführe. Einige C Standardfunktionen scheinen unterschiedliche Wertebereiche bzw. unterschiedliche Auflösungen zurückzugeben. Ich vermute sie kompilieren zu Prozessorinstruktionen die halt bereits unterschiedliche Werte erzeugen, je nachdem für welches OS der Prozessor ausgelegt ist. Und im Fall von WSL ist das nun mal Windows.

Zwei Beispiele:

- clock()
Auf Windows is CLOCKS_PER_SEC mit 10.000 definiert. Auf Linux mit 1.000.000 (POSIX Anforderung soweit ich gelesen habe). Alle Programme die für Linux compiliert sind, CLOCKS_PER_SEC im Code haben und unter WSL laufen, werden Mist produzieren. Unter WSL ist die Auflösung 10.000 ¯\_(ツ)_/¯

- rand()
RAND_MAX ist 32.767 auf Windows und 2.147.483.647 auf Linux. Allerdings gibt rand() auch auf WSL nur Werte 0..32.767 zurück. Das geht fürchterlich in die Hose.

Nein, ich will hier keine Diskussion über die Sinnhaftigkeit der Verwendung von clock() und rand() anzetteln. Mir geht es darum, dass sich ELF Executables in WSL eben offenbar nicht "nativ" verhalten. WSL verspricht das aber. Das wiederum bedeutet, dass man Programme eben nicht für WSL compilieren sollte. Was aber dann? Das einzige was mir bislang dazu einfällt, ist eine Prüfung zur Laufzeit à la ...
C:
#ifdef _WIN32
const int clocks_per_sec = CLOCKS_PER_SEC;
#else // ! _WIN32
#include <string.h>
#include <sys/utsname.h>
struct utsname sys_inf_buf = {0};
uname(&sys_inf_buf);
const int clocks_per_sec = strstr(sys_inf_buf.release, "microsoft") ? 10000 : CLOCKS_PER_SEC;
#endif // ! _WIN32
... mit 10000 als Magic Number, da so etwas wie ein CLOCKS_PER_SEC_ON_WSL logischerweise in keiner Implementierung definiert ist.
Aber alle ursprünglich für Linux compilierten Programme die so eine Prüfung nicht beinhalten, können potenziell an irgendeiner Stelle unerwartetes Verhalten zeigen wenn sie im WSL ausgeführt werden.

Und an dem Punkt würde ich gern irgendwo einen Bugreport aufmachen. Aber wo?
  • Den Compilerbauern würde ich keinen Vorwurf machen. Wie oben zu sehen, wäre eine Prüfung zur Laufzeit erforderlich.
  • Das WSL Team behauptet, dass ELF Programme nativ ausführbar sind. Scheinbar aber nicht ohne Einschränkung. Ich hab aber keine Ahnung inwiefern sie tatsächlich was ändern könnten (nach dem Motto "wenn Prozessorinstruktion xyz gelesen wird, gehe in ein Workaround") oder inwiefern sogar am Linux Kernel geschraubt werden müsste, den WSL2 nutzt.
  • Am Ende vielleicht sogar in Richtung Prozessorentwickler?
Was denkt ihr?
 
Zuletzt bearbeitet:
WSL2. Ich denke zwar das unter WSL schon gesehen zu haben, macht aber sicher keinen Sinn sich darüber noch Gedanken zu machen.
 
Je mehr ich drüber nachdenke ...
Wenn ich einen C Code schreibe und C Funktionen verwende, dann sind die ja bereits in einer C Laufzeitbibliothek implementiert auf die verlinkt wird. Und diese Implementierung müsste auf die Plattform angepasst sein, auf der sie läuft, oder?

Was ist das dann unter Linux, crt0.o? (Keine Ahnung, ich bin viel zu weit weg von irgendeinem Verständnis von Linux ...)
 
Nicht das ich davon Ahnung hätte, aber ich war neugierig und mal Google bemüht, anscheinend zumindest gibt es zu RAND_MAX nur eins, auf das man sich verlassen kann - es ist mindestens 32767.


Von daher würde ich zumindest das verhalten nicht als "falsch" ansehen, weil niemand garantiert das es mehr sein muss. Sprich, auch auf Linux wäre ein C-Lib die 32767 da zurück gibt, nicht falsch (aber vermutlich ziemlich unerwartet)
 
Das ist lediglich der durch den Standard definierte Mindestwert.
Wenn du den ersten Satz in der verlinkten Referenz liest ...
Expands to an integer constant expression equal to the maximum value returned by the function std::rand.
... wird klar was falsch ist, wenn du dir meine Beschreibung oben noch mal anschaust. Wenn ich ein ELF Executable kompiliere und im Code ein RAND_MAX habe, dann wird dieses Macro vom Preprocessor durch 2147483647 ersetzt. Unter WSL ist aber 32767 der größte Wert den die rand() Funktion zurückgibt.
Beispiel:
C:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main(void) {
  srand((unsigned)time(NULL));

  // print 30 random numbers in range 0..99
  for (int i = 0; i < 30; ++i)
    printf("%d\n", rand() * 100 / RAND_MAX);

  return 0;
}

Als PE Executable für Windows sieht die Ausgabe so aus:
Win32.png


Das ELF Executable im WSL ergibt:
WSL2.png


Auf einem Ubuntu würde ich aber mit demselben compilierten Programm (nicht nur mit demselben Sourcecode) die erwarteten Zufallszahlen bekommen. Das ist der Bug von dem ich rede.
 
Stimmt. Ich hätte genauer lesen sollen.

Damit ist meiner Ansicht nach ein Bug in der durch WSL2 emulierten/Zur Verfügung gestellten Standard-Bibliothek
 
Japp. Sehe ich mittlerweile ähnlich. Muss mal sehen welche Backgroundinformationen über die Implementierung von WSL2 zu finden sind, damit ich das auch verständlich ausformulieren kann.

BTW: Wäre schon mal hilfreich wenn mir jemand erzählen kann wie Linux hier tickt. Gibt's da was Vergleichbares wie dynamisch gelinkte Bibliotheken (DLL unter Windows)?
 
OK, ich rudere jetzt einmal komplett zurück.

Was rand() angeht glaube ich zwar, dass ich ein echtes Problem zumindest unter WSL1 gesehen habe. Vielleicht habe ich mich aber auch da schon selbst ausgetrickst. Ich meine, was sonst als ein arithmetischer Überlauf sollte denn passieren wenn ich ints, die gegen 2147483647 (aka INT_MAX) laufen können, mit 100 multipliziere 🤦🏻‍♂️

Ich schau jetzt erst mal genauer nach möglichen anderen Ursachen für komische Ausgaben bei der Verwendung von clock(), bevor ich noch mehr Unsinn schreibe ...
 
Zurück
Oben Unten