Windows Subsystem for Linux

From Free Pascal wiki

Microsoft introduced a certain compatibility functionality in order to run ELF binaries under Windows 10.

WSL1

WSL1 is implemented as Microsoft developed Linux-kernel-compatible execution environment. This is not an actual Linux kernel, but only its substitute, which provides the most common functionality.

The functionality is ENOUGH to run FPC with out modifications.

Installing WSL and FPC

Microsoft provides a number of Linux distros through Microsoft Store application. The following steps are using Ubuntu 16.4 LTS distro:

  • Install WSL
    • you can either install WSL through Powershell (as suggested in Microsoft documentation OR you can install via "Turn Windows features on or off")
You can run control panel (press Win+R type in "control" and press enter) and search for "windows features" in order to bring the dialog up. The link is also available in "Add and Remove programs" section of control panel).

wsl enable marked.png

Note: WSL1 only requires "Windows subsystem for Linux" to be installed.

"Virtual Machine Platform" is only requires for WSL2.

  • Install and Run Ubuntu distro
  • Update the system
Run the following commands on Linux command-line
sudo apt-get update
sudo apt-get upgrade

Running the test app from Windows

The "wsl" utility can be used to run Linux (x86_64) targets on Windows. If you used the example above to compile the application, then you should be able to run as following, from Windows command-line

cd c:\testrun
wsl ./testrun
output
hello world

Running (linux) FPC from Windows

Similar to the sample application, FPC itself can also be ran from Windows.

Here's an example create a folder in Windows, and create a file there test.pas. Edit the file (i.e. using notepad) and put the following code:

procedure RunLoop;
var
  i : integer;
begin
  for i:=1 to 10 do
    write(i,' ');
  writeln;
end;

begin 
  {$ifdef mswindows}
  writeln('windows');
  {$endif}
  {$ifdef linux}
  writeln('linux');
  {$endif}
  writeln('hello world');
  writeln(sizeof(Pointer));
  RunLoop;
end.

Run the following commands from command line. Note, that the use of "WSL" is critical (otherwise you'll get a windows binary). (Running the command-line requires both WSL installed and FPC for the distro installed)

wsl fpc ./test
wsl ./test

You should see in command-line the following output:

linux
hello world
8
1 2 3 4 5 6 7 8 9 10

Indicating the binary was built for linux 64-bit system

Running (linux) Gdb from Windows

WSL1 provides enough functionality in order for GDB to run.

However, GDB itself is not installed by default (on Ubuntu 16.4 distro) and needs to be installed prior to use

wsl sudo apt-get install gdb

Recompile the existing sample program with Dwarf2 enabled.

wsl fpc -gw2 test.pas

(you should see the size of the executable increasing)

Run GDB for the compiledexecutable.

wsl gdb test

you should see:

C:\testbuild>wsl gdb test
GNU gdb (Ubuntu 7.11.1-0ubuntu1~16.5) 7.11.1
Copyright (C) 2016 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from test...done.

at the initial-load place a breakpoint within the runloop procedure:

(gdb) b test.pas:6

resulting in:

Breakpoint 1 at 0x4001e6: file test.pas, line 6.

and proceed with the execution:

(gdb) r
Starting program: /mnt/c/testbuild/test
linux
hello world
8

Breakpoint 1, RUNLOOP () at test.pas:6
6           write(i,' ');
(gdb) 

at this point you can do all typical Gdb actions you need to do. I.e. researching stack:

(gdb) bt
#0  RUNLOOP () at test.pas:6
#1  0x00000000004002fa in main () at test.pas:19

or terminating the exectuion

(gdb) q

WSL2

WSL2 is a different approach, that's implemented using an actual Linux kernel running in a virtual machine. Namely the entire Linux kernel functionality is available (... and the sky is limit!). Microsoft also addressed some performance issues when writing files to NTFS.

Installing

  • You need to have both "Windows Subsystem for Linux" and "Virtual Machine Platform" installed
  • (Summer 2019) you need to be subscribed (it's free) for Windows Insider Preview program and use "FAST" (instead of "Slow) option of getting updates.
    • That will allow you to install the "preview" edition of Windows with WSL2 available.
  • Install and initialize a desired Linux distro from Microsoft Online store
  • Switch the distro to use version 2, by running the following command-line:
wsl --set-version Ubuntu-16.04 2
if you running into troubles switching to Version 2, see below.
The process of setting a version takes about a couple of minutes so be patient.

Unable to switch to version 2

If you're getting this error

The requested operation could not be completed due to a virtual disk system limitation. 
Virtual hard disk files must be uncompressed and unencrypted and must not be sparse.

There are 2 ways you should be able to resolve this:

  • Either manually remove "Compressed" flag from the distro folder, which usually resides at:
%localappdata%\Packages\CanonicalGroupLimited.UbuntuonWindows_xxxxxxx

wsl compression mark.png

  • or try to disable compression and encryption for the entire disk. The following commands must be ran in "Administrator" mode (and would require a reboot)
fsutil behavior set disableencryption 1
fsutil behavior set disablecompression 1

See more at: https://github.com/microsoft/WSL/issues/4103

If you're getting an error of Timeout - try to restart the PC.

Using FPC

The process of installation and use of FPC is identical to the described in WSL1. (The installation isidentical to an actual Linux or Linux running in a 3d party Virtual Machine)

No additional changes are needed for FPC itself in order to run and compile from WSL2.

Using GDB

In general the installation of GDB is similar to WSL1.

It seems like there were changes in pipe handling in Gdb, compared to WSL1.

Thus running

 wsl gdb application_name

would still work, but sometimes pipe communication might fail and you would not see a GDB prompt.

On the other hand, if a user tries to use gdb directly from wsl.exe shell, it works just fine.

Running ELF binaries from Windows app

Any windows application can run ELF binary using "wsl.exe".

Windows itself would not recognize ELF executable as an executable. Thus an attempt to open it would make Windows prompt the user for the host application to be specified. There's not special Windows API yet to run them. So far the only way of running an ELF binary is using "wsl.exe".

That means that a desired Linux distro(s) needs to be installed. (and preferably one of them selected as default)

Using WSL.exe

Running ELF binary with WSL.exe requires an explicit call to "wsl.exe" and passing ELF binary as a parameter, as well as any additional parameters.

That typically involves creating a process with StdOut/StrErr redirected to pipes (either by using WinAPI directory or TProcess).

Caveat: there seems to be some special handling of Pipe-redirected output in WSL itself. The launched WSL might not terminate until it receives something on the input buffer. It might also skip some output, if the launching application is "sleeping" for too long. Thus the typical wait for output period should be minimal.

  p := TProcess.Create(nil);
  try
    p.Executable := 'wsl.exe'; 
    p.Parameters.Add('ls'); // running ls utility 
    p.Parameters.Add('-l'); // with -l parameter
    p.Options := [poUsePipes];

    while (p.Running) or (p.Output.NumBytesAvailable > 0) do
    begin
      if p.Running and (p.Output.NumBytesAvailable = 0) then
      begin
        s:=#0; // writing anything to the input
        p.Input.Write(s, length(s));
        Sleep(1); // the shortest sleep possible
      end;

      sz := p.Output.NumBytesAvailable;
      if sz>0 then begin
        SetLength(s, sz);
        sz := p.Output.Read(s[1], sz);
        if sz < length(s) then SetLength(s, sz);
        write(s);
      end;
   end;

  finally
    p.Free;
  end;

It also doesn't seem that WSL is returning the exit code back.


One can refer to the full code example of "wslwrap" application: https://github.com/skalogryz/wslwrap

Lazarus

Using Lazarus from WSL

WSL itself comes without graphical support by default. Yet it should be possible to install X-Windows server on Windows 10 machine itself, and configure WSL distro to use Windows 10 as its windows server.

That also should open an option for developing and debugging Linux GUI applications using WSL.

Using WSL FPC and GDB from Windows

As long as Lazarus is capable of "prefixing" every FPC (and Gdb) commands with "wsl" - forcing the Windows Subsystem for Linux to execute, it should be capable of producing native (command-line) executables.

It's likely Lazarus on Windows would also need to substitute the proper file path when dealing with WSL-ed projects.

Quick and Dirty - Using WSL FPC from Windows

Dislaimer: the proper solution might require additional work on IDE side. While the compiler doesn't need to be changed at all. It's possible to create a WSL compiler in order to by-pass Lazarus limitations, without a need of building a special cross-compiler.

  • before begin, make sure you've FPC installed on WSL (linux) side.
  • Build wslwrap application (https://github.com/skalogryz/wslwrap)
  • Rename wslwrap to ppcwsl_fpc.exe (a name can also follow FPC cross-compiler name convention, in this case the result might be even better)
The such name has two purposes:
1) it starts with "ppc". Lazarus verifies the compiler executable to start with either "ppc" or "fpc", otherwise it would produce (a lot of annoying) warnings
2) "_fpc" part enforces the use of "fpc" executable on WSL side (this is "wslwrap" feature)
  • Place the executable to your lazarus fpc configuration folder. (next to the current fpc.exe)
  • In Lazarus IDE options change "Compiler executable" to the new ppcwsl_fpc.exe.
The IDE would complain about "fpc.cfg" missing - ignore it.

wsl fakefpc ideoption.png

  • Start a new (non GUI) project, i.e. with the following code
begin 
  writeln('hello world');
end.
  • Change project settings to:
Target OS = Linux
Target CPI = x86_64
  • Compile the project
  • In the project folder you should see the ELF executable showing up. You should be able to execute it using "wsl.exe"

wsl widows compiled.png

In order to rollback, simply restore the original FPC configuration in the IDE option.

See Also