• chevron_right

      Emulateur x86 WebAssembly pour lancer des vieux Windows / Linux / BSD…etc depuis votre browser

      news.movim.eu / Korben · Thursday, 21 July, 2022 - 07:00 · 1 minute

    Je ne me lasse pas de ces émulateurs x86 qui permettent au travers d’une page web de faire tourner d’anciens OS tels que Windows 95, Windows 2000 ou encore ReactOS. Mais également des trucs encore d’actualité comme FreeDOS, OpenBSD ou Haiku .

    Arch Linux Damn Small Linux Buildroot Linux ReactOS Windows 2000 Windows 98 Windows 95 Windows 1.01 MS-DOS FreeDOS FreeBSD OpenBSD 9front Haiku Oberon KolibriOS QNX

    Le projet v86 émule un processeur et du matériel compatible x86. Ainsi, le code machine est traduit en modules WebAssembly au moment de l’exécution ce qui permet d’obtenir des performances décentes.

    Sur le site copy.sh/v86/ vous pourrez ainsi lancer du DOS, du Windows ou encore du Linux et du BSD, directement à partir des images proposées ou charger vous-même votre propre image CD, disquette, disque dur…etc. Le matériel émulé est un CPU compatible x86 équivalent d’un Pentium III de l’époque avec le support SSE2 et d’autres choses comme un contrôleur de lecteur de disquette, un clavier PS2, une carte VGA ou encore une carte réseau PCI NE2000.

    Comme le projet est open source, vous pouvez le forker et déployer ça sur votre propre serveur si besoin. En tout cas, c’est une chouette initiative.

    • Nu chevron_right

      More DLL fun with w64devkit: Go, assembly, and Python

      pubsub.kikeriki.at / null_program · Tuesday, 29 June, 2021 - 21:50 · 31 minutes

    <p>My previous article explained <a href="/blog/2021/05/31/">how to work with dynamic-link libraries (DLLs) using w64devkit</a>. These techniques also apply to other circumstances, including with languages and ecosystems outside of C and C++. In particular, <a href="/blog/2020/05/15/">w64devkit</a> is a great complement to Go and reliably fullfills all the needs of <a href="https://golang.org/cmd/cgo/">cgo</a> — Go’s C interop — and can even bootstrap Go itself. As before, this article is in large part an exercise in capturing practical information I’ve picked up over time.</p> <h3 id="go-bootstrap-and-cgo">Go: bootstrap and cgo</h3> <p>The primary Go implementation, confusingly <a href="https://golang.org/doc/faq#What_compiler_technology_is_used_to_build_the_compilers">named “gc”</a>, is an <a href="/blog/2020/01/21/">incredible piece of software engineering</a>. This is apparent when building the Go toolchain itself, a process that is fast, reliable, easy, and simple. It was originally written in C, but was re-written in Go starting with Go 1.5. The C compiler in w64devkit can build the original C implementation which then can be used to bootstrap any more recent version. It’s so easy that I personally never use official binary releases and always bootstrap from source.</p> <p>You will need the Go 1.4 source, <a href="https://dl.google.com/go/go1.4-bootstrap-20171003.tar.gz">go1.4-bootstrap-20171003.tar.gz</a>. This “bootstrap” tarball is the last Go 1.4 release plus a few additional bugfixes. You will also need the source of the actual version of Go you want to use, such as Go 1.16.5 (latest version as of this writing).</p> <p>Start by building Go 1.4 using w64devkit. On Windows, Go is built using a batch script and no special build system is needed. Since it shouldn’t be invoked with the BusyBox ash shell, I use <a href="/blog/2021/02/08/"><code class="language-plaintext highlighter-rouge">cmd.exe</code></a> explicitly.</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ tar xf go1.4-bootstrap-20171003.tar.gz $ mv go/ bootstrap $ (cd bootstrap/src/ &amp;&amp; cmd /c make) </code></pre></div></div> <p>In about 30 seconds you’ll have a fully-working Go 1.4 toolchain. Next use it to build the desired toolchain. You can move this new toolchain after it’s built if necessary.</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ export GOROOT_BOOTSTRAP="$PWD/bootstrap" $ tar xf go1.16.5.src.tar.gz $ (cd go/src/ &amp;&amp; cmd /c make) </code></pre></div></div> <p>At this point you can delete the bootstrap toolchain. You probably also want to put Go on your PATH.</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ rm -rf bootstrap/ $ printf 'PATH="$PATH;%s/go/bin"\n' "$PWD" &gt;&gt;~/.profile $ source ~/.profile </code></pre></div></div> <p>Not only is Go now available, so is the full power of cgo. (Including <a href="https://dave.cheney.net/2016/01/18/cgo-is-not-go">its costs</a> if used.)</p> <h3 id="vim-suggestions">Vim suggestions</h3> <p>Since w64devkit is oriented so much around Vim, here’s my personal Vim configuration for Go. I don’t need or want fancy plugins, just access to <code class="language-plaintext highlighter-rouge">goimports</code> and a couple of corrections to Vim’s built-in Go support (<code class="language-plaintext highlighter-rouge">[[</code> and <code class="language-plaintext highlighter-rouge">]]</code> navigation). The included <code class="language-plaintext highlighter-rouge">ctags</code> understands Go, so tags navigation works the same as it does with C. <code class="language-plaintext highlighter-rouge">\i</code> saves the current buffer, runs <code class="language-plaintext highlighter-rouge">goimports</code>, and populates the quickfix list with any errors. Similarly <code class="language-plaintext highlighter-rouge">:make</code> invokes <code class="language-plaintext highlighter-rouge">go build</code> and, as expected, populates the quickfix list.</p> <div class="language-vim highlighter-rouge"><div class="highlight"><pre class="highlight"><code>autocmd <span class="nb">FileType</span> <span class="k">go</span> <span class="k">setlocal</span> <span class="nb">makeprg</span><span class="p">=</span><span class="k">go</span>\ build autocmd <span class="nb">FileType</span> <span class="k">go</span> <span class="nb">map</span> <span class="p">&lt;</span><span class="k">silent</span><span class="p">&gt;</span> <span class="p">&lt;</span><span class="k">buffer</span><span class="p">&gt;</span> <span class="p">&lt;</span>leader<span class="p">&gt;</span><span class="k">i</span> <span class="se"> \</span> <span class="p">:</span><span class="k">update</span> \<span class="p">|</span> <span class="se"> \</span> <span class="p">:</span><span class="k">cexpr</span> <span class="nb">system</span><span class="p">(</span><span class="s2">"goimports -w "</span> <span class="p">.</span> <span class="nb">expand</span><span class="p">(</span><span class="s2">"%"</span><span class="p">))</span> \<span class="p">|</span> <span class="se"> \</span> <span class="p">:</span><span class="k">silent</span> <span class="k">edit</span><span class="p">&lt;</span><span class="k">cr</span><span class="p">&gt;</span> autocmd <span class="nb">FileType</span> <span class="k">go</span> <span class="nb">map</span> <span class="p">&lt;</span><span class="k">buffer</span><span class="p">&gt;</span> <span class="p">[[</span> <span class="se"> \</span> ?^\<span class="p">(</span>func\\<span class="p">|</span>var\\<span class="p">|</span><span class="nb">type</span>\\<span class="p">|</span><span class="k">import</span>\\<span class="p">|</span>package\<span class="p">)</span>\<span class="p">&gt;&lt;</span><span class="k">cr</span><span class="p">&gt;</span> autocmd <span class="nb">FileType</span> <span class="k">go</span> <span class="nb">map</span> <span class="p">&lt;</span><span class="k">buffer</span><span class="p">&gt;</span> <span class="p">]]</span> <span class="se"> \</span> /^\<span class="p">(</span>func\\<span class="p">|</span>var\\<span class="p">|</span><span class="nb">type</span>\\<span class="p">|</span><span class="k">import</span>\\<span class="p">|</span>package\<span class="p">)</span>\<span class="p">&gt;&lt;</span><span class="k">cr</span><span class="p">&gt;</span> </code></pre></div></div> <p>Go only comes with <code class="language-plaintext highlighter-rouge">gofmt</code> but <code class="language-plaintext highlighter-rouge">goimports</code> is just one command away, so there’s little excuse not to have it:</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ go install golang.org/x/tools/cmd/goimports@latest </code></pre></div></div> <p>Thanks to GOPROXY, all Go dependencies are accessible without (or before) installing Git, so this tool installation works with nothing more than w64devkit and a bootstrapped Go toolchain.</p> <h3 id="cgo-dlls">cgo DLLs</h3> <p>The intricacies of cgo are beyond the scope of this article, but the gist is that a Go source file contains C source in a comment followed by <code class="language-plaintext highlighter-rouge">import "C"</code>. The imported <code class="language-plaintext highlighter-rouge">C</code> object provides access to C types and functions. Go functions marked with an <code class="language-plaintext highlighter-rouge">//export</code> comment, as well as the commented C code, are accessible to C. The latter means we can use Go to implement a C interface in a DLL, and the caller will have no idea they’re actually talking to Go.</p> <p>To illustrate, here’s an little C interface. To keep it simple, I’ve specifically sidestepped some more complicated issues, particularly involving memory management.</p> <div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Which DLL am I running?</span> <span class="kt">int</span> <span class="nf">version</span><span class="p">(</span><span class="kt">void</span><span class="p">);</span> <span class="c1">// Generate 64 bits from a CSPRNG.</span> <span class="kt">unsigned</span> <span class="kt">long</span> <span class="kt">long</span> <span class="nf">rand64</span><span class="p">(</span><span class="kt">void</span><span class="p">);</span> <span class="c1">// Compute the Euclidean norm.</span> <span class="kt">float</span> <span class="nf">dist</span><span class="p">(</span><span class="kt">float</span> <span class="n">x</span><span class="p">,</span> <span class="kt">float</span> <span class="n">y</span><span class="p">);</span> </code></pre></div></div> <p>Here’s a C implementation which I’m calling “version 1”.</p> <div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include &lt;math.h&gt; #include &lt;windows.h&gt; #include &lt;ntsecapi.h&gt; </span> <span class="kr">__declspec</span><span class="p">(</span><span class="n">dllexport</span><span class="p">)</span> <span class="kt">int</span> <span class="nf">version</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span> <span class="k">return</span> <span class="mi">1</span><span class="p">;</span> <span class="p">}</span> <span class="kr">__declspec</span><span class="p">(</span><span class="n">dllexport</span><span class="p">)</span> <span class="kt">unsigned</span> <span class="kt">long</span> <span class="kt">long</span> <span class="nf">rand64</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span> <span class="kt">unsigned</span> <span class="kt">long</span> <span class="kt">long</span> <span class="n">x</span><span class="p">;</span> <span class="n">RtlGenRandom</span><span class="p">(</span><span class="o">&amp;</span><span class="n">x</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">x</span><span class="p">));</span> <span class="k">return</span> <span class="n">x</span><span class="p">;</span> <span class="p">}</span> <span class="kr">__declspec</span><span class="p">(</span><span class="n">dllexport</span><span class="p">)</span> <span class="kt">float</span> <span class="nf">dist</span><span class="p">(</span><span class="kt">float</span> <span class="n">x</span><span class="p">,</span> <span class="kt">float</span> <span class="n">y</span><span class="p">)</span> <span class="p">{</span> <span class="k">return</span> <span class="n">sqrtf</span><span class="p">(</span><span class="n">x</span><span class="o">*</span><span class="n">x</span> <span class="o">+</span> <span class="n">y</span><span class="o">*</span><span class="n">y</span><span class="p">);</span> <span class="p">}</span> </code></pre></div></div> <p>As discussed in the previous article, each function is exported using <code class="language-plaintext highlighter-rouge">__declspec</code> so that they’re available for import. As before:</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ cc -shared -Os -s -o hello1.dll hello1.c </code></pre></div></div> <p>Side note: This could be trivially converted into a C++ implementation just by adding <code class="language-plaintext highlighter-rouge">extern "C"</code> to each declaration. It disables C++ features like name mangling, and follows the C ABI so that the C++ functions appear as C functions. Compiling the C++ DLL is exactly the same.</p> <p>Suppose we wanted to implement this in Go instead of C. We already have all the tools needed to do so. Here’s a Go implementation, “version 2”:</p> <div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">package</span> <span class="n">main</span> <span class="k">import</span> <span class="s">"C"</span> <span class="k">import</span> <span class="p">(</span> <span class="s">"crypto/rand"</span> <span class="s">"encoding/binary"</span> <span class="s">"math"</span> <span class="p">)</span> <span class="c">//export version</span> <span class="k">func</span> <span class="n">version</span><span class="p">()</span> <span class="n">C</span><span class="o">.</span><span class="kt">int</span> <span class="p">{</span> <span class="k">return</span> <span class="m">2</span> <span class="p">}</span> <span class="c">//export rand64</span> <span class="k">func</span> <span class="n">rand64</span><span class="p">()</span> <span class="n">C</span><span class="o">.</span><span class="n">ulonglong</span> <span class="p">{</span> <span class="k">var</span> <span class="n">buf</span> <span class="p">[</span><span class="m">8</span><span class="p">]</span><span class="kt">byte</span> <span class="n">rand</span><span class="o">.</span><span class="n">Read</span><span class="p">(</span><span class="n">buf</span><span class="p">[</span><span class="o">:</span><span class="p">])</span> <span class="n">r</span> <span class="o">:=</span> <span class="n">binary</span><span class="o">.</span><span class="n">LittleEndian</span><span class="o">.</span><span class="n">Uint64</span><span class="p">(</span><span class="n">buf</span><span class="p">[</span><span class="o">:</span><span class="p">])</span> <span class="k">return</span> <span class="n">C</span><span class="o">.</span><span class="n">ulonglong</span><span class="p">(</span><span class="n">r</span><span class="p">)</span> <span class="p">}</span> <span class="c">//export dist</span> <span class="k">func</span> <span class="n">dist</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span> <span class="n">C</span><span class="o">.</span><span class="n">float</span><span class="p">)</span> <span class="n">C</span><span class="o">.</span><span class="n">float</span> <span class="p">{</span> <span class="k">return</span> <span class="n">C</span><span class="o">.</span><span class="n">float</span><span class="p">(</span><span class="n">math</span><span class="o">.</span><span class="n">Sqrt</span><span class="p">(</span><span class="kt">float64</span><span class="p">(</span><span class="n">x</span><span class="o">*</span><span class="n">x</span> <span class="o">+</span> <span class="n">y</span><span class="o">*</span><span class="n">y</span><span class="p">)))</span> <span class="p">}</span> <span class="k">func</span> <span class="n">main</span><span class="p">()</span> <span class="p">{</span> <span class="p">}</span> </code></pre></div></div> <p>Note the use of C types for all arguments and return values. The <code class="language-plaintext highlighter-rouge">main</code> function is required since this is the main package, but it will never be called. The DLL is built like so:</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ go build -buildmode=c-shared -o hello2.dll hello2.go </code></pre></div></div> <p>Without the <code class="language-plaintext highlighter-rouge">-o</code> option, the DLL will lack an extension. This works fine since it’s mostly only convention on Windows, but it may be confusing without it.</p> <p>What if we need an import library? This will be required when linking with the MSVC toolchain. In the previous article we asked Binutils to generate one using <code class="language-plaintext highlighter-rouge">--out-implib</code>. For Go we have to handle this ourselves via <code class="language-plaintext highlighter-rouge">gendef</code> and <code class="language-plaintext highlighter-rouge">dlltool</code>.</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ gendef hello2.dll $ dlltool -l hello2.lib -d hello2.def </code></pre></div></div> <p>The only way anyone upgrading would know version 2 was implemented in Go is that the DLL is a lot bigger (a few MB vs. a few kB) since it now contains an entire Go runtime.</p> <h3 id="nasm-assembly-dll">NASM assembly DLL</h3> <p>We could also go the other direction and implement the DLL using plain assembly. It won’t even require linking against a C runtime.</p> <p>w64devkit includes two assemblers: GAS (Binutils) which is used by GCC, and NASM which has <a href="https://elronnd.net/writ/2021-02-13_att-asm.html">friendlier syntax</a>. I prefer the latter whenever possible — exactly why I included NASM in the distribution. So here’s how I implemented “version 3” in NASM assembly.</p> <div class="language-nasm highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nf">bits</span> <span class="mi">64</span> <span class="nf">section</span> <span class="nv">.text</span> <span class="nf">global</span> <span class="nb">Dl</span><span class="nv">lMainCRTStartup</span> <span class="nf">export</span> <span class="nb">Dl</span><span class="nv">lMainCRTStartup</span> <span class="nl">DllMainCRTStartup:</span> <span class="nf">mov</span> <span class="nb">eax</span><span class="p">,</span> <span class="mi">1</span> <span class="nf">ret</span> <span class="nf">global</span> <span class="nv">version</span> <span class="nf">export</span> <span class="nv">version</span> <span class="nl">version:</span> <span class="nf">mov</span> <span class="nb">eax</span><span class="p">,</span> <span class="mi">3</span> <span class="nf">ret</span> <span class="nf">global</span> <span class="nv">rand64</span> <span class="nf">export</span> <span class="nv">rand64</span> <span class="nl">rand64:</span> <span class="nf">rdrand</span> <span class="nb">rax</span> <span class="nf">ret</span> <span class="nf">global</span> <span class="nb">di</span><span class="nv">st</span> <span class="nf">export</span> <span class="nb">di</span><span class="nv">st</span> <span class="nl">dist:</span> <span class="nf">mulss</span> <span class="nv">xmm0</span><span class="p">,</span> <span class="nv">xmm0</span> <span class="nf">mulss</span> <span class="nv">xmm1</span><span class="p">,</span> <span class="nv">xmm1</span> <span class="nf">addss</span> <span class="nv">xmm0</span><span class="p">,</span> <span class="nv">xmm1</span> <span class="nf">sqrtss</span> <span class="nv">xmm0</span><span class="p">,</span> <span class="nv">xmm0</span> <span class="nf">ret</span> </code></pre></div></div> <p>The <code class="language-plaintext highlighter-rouge">global</code> directive is common in NASM assembly and causes the named symbol to have the external linkage needed when linking the DLL. The <code class="language-plaintext highlighter-rouge">export</code> directive is Windows-specific and is equivalent to <code class="language-plaintext highlighter-rouge">dllexport</code> in C.</p> <p>Every DLL must have an entrypoint, usually named <code class="language-plaintext highlighter-rouge">DllMainCRTStartup</code>. The return value indicates if the DLL successfully loaded. So far this has been handled automatically by the C implementation, but at this low level we must define it explicitly.</p> <p>Here’s how to assemble and link the DLL:</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ nasm -fwin64 -o hello3.o hello3.s $ ld -shared -s -o hello3.dll hello3.o </code></pre></div></div> <h3 id="call-the-dlls-from-python">Call the DLLs from Python</h3> <p>Python has a nice, built-in C interop, <code class="language-plaintext highlighter-rouge">ctypes</code>, that allows Python to call arbitrary C functions in shared libraries, including DLLs, without writing C to glue it together. To tie this all off, here’s a Python program that loads all of the DLLs above and invokes each of the functions:</p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">ctypes</span> <span class="k">def</span> <span class="nf">load</span><span class="p">(</span><span class="n">version</span><span class="p">):</span> <span class="n">hello</span> <span class="o">=</span> <span class="n">ctypes</span><span class="p">.</span><span class="n">CDLL</span><span class="p">(</span><span class="sa">f</span><span class="s">"./hello</span><span class="si">{</span><span class="n">version</span><span class="si">}</span><span class="s">.dll"</span><span class="p">)</span> <span class="n">hello</span><span class="p">.</span><span class="n">version</span><span class="p">.</span><span class="n">restype</span> <span class="o">=</span> <span class="n">ctypes</span><span class="p">.</span><span class="n">c_int</span> <span class="n">hello</span><span class="p">.</span><span class="n">version</span><span class="p">.</span><span class="n">argtypes</span> <span class="o">=</span> <span class="p">()</span> <span class="n">hello</span><span class="p">.</span><span class="n">dist</span><span class="p">.</span><span class="n">restype</span> <span class="o">=</span> <span class="n">ctypes</span><span class="p">.</span><span class="n">c_float</span> <span class="n">hello</span><span class="p">.</span><span class="n">dist</span><span class="p">.</span><span class="n">argtypes</span> <span class="o">=</span> <span class="p">(</span><span class="n">ctypes</span><span class="p">.</span><span class="n">c_float</span><span class="p">,</span> <span class="n">ctypes</span><span class="p">.</span><span class="n">c_float</span><span class="p">)</span> <span class="n">hello</span><span class="p">.</span><span class="n">rand64</span><span class="p">.</span><span class="n">restype</span> <span class="o">=</span> <span class="n">ctypes</span><span class="p">.</span><span class="n">c_ulonglong</span> <span class="n">hello</span><span class="p">.</span><span class="n">rand64</span><span class="p">.</span><span class="n">argtypes</span> <span class="o">=</span> <span class="p">()</span> <span class="k">return</span> <span class="n">hello</span> <span class="k">for</span> <span class="n">hello</span> <span class="ow">in</span> <span class="n">load</span><span class="p">(</span><span class="mi">1</span><span class="p">),</span> <span class="n">load</span><span class="p">(</span><span class="mi">2</span><span class="p">),</span> <span class="n">load</span><span class="p">(</span><span class="mi">3</span><span class="p">):</span> <span class="k">print</span><span class="p">(</span><span class="s">"version"</span><span class="p">,</span> <span class="n">hello</span><span class="p">.</span><span class="n">version</span><span class="p">())</span> <span class="k">print</span><span class="p">(</span><span class="s">"rand "</span><span class="p">,</span> <span class="sa">f</span><span class="s">"</span><span class="si">{</span><span class="n">hello</span><span class="p">.</span><span class="n">rand64</span><span class="p">():</span><span class="mi">016</span><span class="n">x</span><span class="si">}</span><span class="s">"</span><span class="p">)</span> <span class="k">print</span><span class="p">(</span><span class="s">"dist "</span><span class="p">,</span> <span class="n">hello</span><span class="p">.</span><span class="n">dist</span><span class="p">(</span><span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">))</span> </code></pre></div></div> <p>After loading the DLL with <code class="language-plaintext highlighter-rouge">CDLL</code> the program defines each function prototype so that Python knows how to call it. Unfortunately it’s not possible to build Python with w64devkit, so you’ll also need to install the standard CPython distribution in order to run it. Here’s the output:</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ python finale.py version 1 rand b011ea9bdbde4bdf dist 5.0 version 2 rand f7c86ff06ae3d1a2 dist 5.0 version 3 rand 2a35a05b0482c898 dist 5.0 </code></pre></div></div> <p>That output is the result of four different languages interfacing in one process: C, Go, x86-64 assembly, and Python. Pretty neat if you ask me!</p>
    https://upload.movim.eu/files/386deb01b4d6c7831131d61eb8ea4eaf309fe95e/UkNNvdXl1pJ07zeueOWprEh4i0xM1Z1b3yZdQNLe/895a8e8ced6c34f3c4e7.png.jpg


    Bliss ROMs

    Working together to bring you an Open Source OS, based on Android, that incorporates many customizations, options and added security features.

    And it is available for…

    About Bliss

    We are a volunteer based non-profit organization that works to maintain various open source & closed source development projects. We provide the inspiration, training and mentorship to help the dreams of those that join, become reality.

    One of our main focuses is to provide a quality ROM/OS that can run on all your devices, preserving customizations and options by syncing across all platforms. We try to include all the features you can imagine, for just about any situation.

    https://blissroms.com/

    #android #x86 #OS #Bliss #desktop