jart / cosmopolitan

build-once run-anywhere c library
ISC License
18.03k stars 618 forks source link

cosmopolitan quickjs #1005

Open serapath opened 9 months ago

serapath commented 9 months ago

Thank you @jart for this amazing project and thanks for encouraging me to open an issue. I believe many people would be amazed to see cosmopolitan 3.0+ be able to compile https://bellard.org/quickjs to APE to run on bare metal.

Something fails with qemu-system-x86_64 -fda o//third_party/quickjs/qjs.com

mikeptweet commented 9 months ago

This already exists ... download https://github.com/jart/cosmopolitan/releases/download/3.1.3/cosmocc-3.1.3.zip and you'll find it in the bin directory.

jart commented 9 months ago

I reached out to @tkchia earlier on Discord who's our bare metal expert. Another one of our experts is @ghaerr. Would either of you like to help our friend use Bellard's fabulous JavaScript REPL on bare metal? Thanks!

serapath commented 9 months ago

@mikeptweet i downloaded the zip file and while the README explains how the toolchain can be used to compile executables that run cross platform, nothing seems to be specifically about quickjs.

I'm a javascript developer and C is quite challenging to get into. I found a few articles titled "C for JavaScript Devs", but cosmopolitan is yet another beast. I have done C many many years ago during my studies, but haven't touched it since. I would like to get into cosmopolitan by trying to play with quickjs, but some guidance would be appreciated.

What tools do i need? How do I start writing, debugging and generally set up a feedback loop to progress getting to know cosmopolitan?

I read through https://justine.lol/cosmopolitan/ and the documentation is not comparable to MDN and the tutorials aren't helpful for somebody with my background.

Is there some pragmatic way to learn to set up a cosmopolitan project in combination with e.g. virtualbox or (i do have an unused spare HP laptop) to experiment with getting a "hello world" program and at some point a quickjs based "hello javascript" program work on "bare metal" via e.g. virtualbox or via e.g. my actual HP laptop using a bootable USB stick and/or ipxe or something?

I would love to go through recommended learning materials to learn more about C and how to setup and work on C projects with cosmopolitan as well, but I just have no idea where or how to start towards the goal of using cosmopolitan with quickjs this way.

jart commented 9 months ago

I agree that Cosmopolitan ought to have MDN-quality documentation. You could help blaze that trail for us. Documentation is oftentimes written by people documenting their getting started learning experience. Our wiki is open for public editing. Please help us do better!

mikeptweet commented 9 months ago

Hey @serapath ... the bin directory in the zip file I mentioned contains a universal binary - qjs that is quickjs.

leeoniya commented 9 months ago

btw, there is a much more actively maintained quickjs fork with new es2023 features, bug fixes, and perf improvements here: https://github.com/quickjs-ng/quickjs

they cherry pick any bugfixes/changes that land upstream, too.

ghaerr commented 9 months ago

Hello @serapath,

The business of getting QJS to run on bare metal is a bit complicated, but certainly could be done. The way bare metal is supported in Cosmo is literally booting the qjs.com file in real mode, which then internally sets up the CPU and system to run in (x86) 64-bit long mode, then starts running the compiled program. The statically-linked Cosmo library then special cases each of the what-otherwise-would-be system or C library calls, and performs them on bare hardware.

The work involved depends on which system calls and C library routines QJS might use, as many system calls aren't yet implemented on bare metal. I can only imagine the REPL uses some variation of read and write to the "console", along with possibly quite a few more. At the moment, we support a VESA graphics-mode output mechanism, which allows for screen display and ANSI terminal emulation. Keyboard input is complicated, as it requires the installation of a keyboard interrupt handler and scan code to unicode conversion tables based on keyboard type. While keyboard interrupt handlers and scan code converters have been written many times for x86 real mode, running in long mode can't use the BIOS, as that only works in real mode.

IIRC @tkchia may have a long mode interrupt handler working, but has gotten bogged down in the complex UEFI specification, which requires full implementation to guarantee working on any (that is "most newer") 64-bit PCs.

Sorry for the complicated explanation; but getting a javascript interpreter running could require much more, depending on its set of external C library and system call dependencies. We should probably investigate QJS a bit to see what it requires. QJS is actually pretty large for a JS interpreter, as its advertised as being quite complete and fast. I know it has lots of code very fast handling of heap vs stack upvalue conversions for proper closures, etc. Perhaps a much smaller interpreter, like MuJS, might be interesting, with much fewer depencies (and JS external functions).

In summary, getting this done depends on the sorts of things you need to run from the REPL... and thus which interpreter might be best used to keep complexities minimized.

Thank you!

serapath commented 9 months ago

Thank you for the detailed explanation. My head is spinning now. Much of what you shared is new to me and i was unaware. When you mentioned uefi/bios i somehow was reminded of the coreboot or libreboot, but probably not really relevant or any simpler.

I guess it would be interesting to have quickjs work on bare metal and at least one additional feature, which would be fetch or alternatively something more low level and raw. maybe udp/dgram access, or if bare metal means thats tough, then something even lower level?

i do feel a project like this would open an entire new world or new universe to so many developers, just like nodejs/npm created an enormously large new ecosystem out of seemingly nowhere.

@jart i would love to work towards an MDN, but i think you might underestimate how little i comprehend at the moment. I dont feel like I found a door that would even allow me to enter and do any first steps at all yet. It is difficult to explain, but right now it feel completely inaccessible. There is nothing around cosmos that relates to anything i am familiar with.

To start anything, i would need to be at the shores of cosmopolitan, but currently i feel i'm on another continent and there is an ocean in between where i am and the shores of cosmopolitan land if that makes any sense

jart commented 9 months ago

@serapath If you're looking for a gentle introduction to Cosmopolitan, our bare metal support isn't it. We haven't elevated it yet to a level of quality that it's ready to be consumed by people who aren't able to help us work on improving it.

@ghaerr Here's the system calls for non-interactive mode quickjs

master jart@nightmare:/opt/cosmo$ o//third_party/quickjs/qjs.com -e 'console.log("hi")' --strace
SYS      0      0             14'354 bell system five system call support 329 magnums loaded on gnu/systemd
SYS  18433  18433             29'359 getenv("COSMOPOLITAN_DISABLE_ZIPOS") → NULL
SYS  18433  18433             33'339 getenv("COSMOPOLITAN_INIT_ZIPOS") → NULL
SYS  18433  18433             52'786 getcwd([u"/home/jart/cosmo"], 1'019) → 17
SYS  18433  18433             58'238 openat(AT_FDCWD, "/home/jart/cosmo/o//third_party/quickjs/qjs.com", O_RDONLY) → 3
SYS  18433  18433             75'891 fstat(3, [{.st_size=1'292'999, .st_blocks=1'294'336/512, .st_mode=0100755, .st_uid=1000, .st_gid=1000, .st_dev=0x31, .st_ino=0x910d}]) → 0
SYS  18433  18433             79'705 mmap(0, 1'292'999, PROT_READ, MAP_SHARED, 3, 0) → 0x100080100000 (1'376'256 bytes total)
SYS  18433  18433            106'892 munmap(0x100080100000, 1'179'648) → 0 (196'608 bytes total)
SYS  18433  18433            112'080 madvise(0x10008022b000, 65'536, 4) → 0
SYS  18433  18433            114'042 mmap(0, 136, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) → 0x100080040000 (262'144 bytes total)
SYS  18433  18433            120'494 close(3) → 0
SYS  18433  18433            121'745 __zipos_get("/home/jart/cosmo/o//third_party/quickjs/qjs.com") → 0
SYS  18433  18433            125'334 mmap(0, 196'608, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) → 0x100080060000 (458'752 bytes total)
SYS  18433  18433          1'367'281 inflate([u"SYMT☺   !☼        ♥       ♥       @     "...], 196'608, u"∞▌y|‼┼π└²)≈▌Ç (*♣E♫♣J╣Eí▄-gÑ@╣$Mô┤♪MôÉú•"..., 51'509) → 0
SYS  18433  18433          1'370'760 GetSymbolTableFromZip() → 0x100080060000
SYS  18433  18433          1'372'640 getenv("TERM") → "xterm-256color"
SYS  18433  18433          1'379'233 getenv("HOME") → "/home/jart"
SYS  18433  18433          1'380'612 getenv("TMPDIR") → NULL
SYS  18433  18433          1'404'278 openat(AT_FDCWD, "/zip/.args", O_RDONLY) → -1 ENOENT
SYS  18433  18433          1'413'033 mmap(0, 65'536, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) → 0x100080050000 (524'288 bytes total)
SYS  18433  18433          1'628'338 mmap(0, 65'536, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) → 0x100080090000 (589'824 bytes total)
hi
SYS  18433  18433          1'709'485 write(1, u"hi◙", 3) → 3
SYS  18433  18433          1'808'852 exit(0)
SYS  18433  18433          1'810'176 __cxa_finalize(&fflush_unlocked, 0)
SYS  18433  18433          1'812'033 __cxa_finalize(&__gdtoa_Bclear, 0)
SYS  18433  18433          1'813'109 __cxa_finalize(&TeardownGc, 0)
SYS  18433  18433          1'814'415 _Exit(0)

Here's what it takes to bring up the prompt in interactive mode:

master jart@nightmare:/opt/cosmo$ o//third_party/quickjs/qjs.com --strace
SYS      0      0             14'250 bell system five system call support 329 magnums loaded on gnu/systemd
SYS  18493  18493             28'312 getenv("COSMOPOLITAN_DISABLE_ZIPOS") → NULL
SYS  18493  18493             32'062 getenv("COSMOPOLITAN_INIT_ZIPOS") → NULL
SYS  18493  18493             35'515 getcwd([u"/home/jart/cosmo"], 1'019) → 17
SYS  18493  18493             40'772 openat(AT_FDCWD, "/home/jart/cosmo/o//third_party/quickjs/qjs.com", O_RDONLY) → 3
SYS  18493  18493             44'982 fstat(3, [{.st_size=1'292'999, .st_blocks=1'294'336/512, .st_mode=0100755, .st_uid=1000, .st_gid=1000, .st_dev=0x31, .st_ino=0x910d}]) → 0
SYS  18493  18493             48'283 mmap(0, 1'292'999, PROT_READ, MAP_SHARED, 3, 0) → 0x100080100000 (1'376'256 bytes total)
SYS  18493  18493             59'708 munmap(0x100080100000, 1'179'648) → 0 (196'608 bytes total)
SYS  18493  18493             64'248 madvise(0x10008022b000, 65'536, 4) → 0
SYS  18493  18493             84'923 mmap(0, 136, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) → 0x100080040000 (262'144 bytes total)
SYS  18493  18493             90'917 close(3) → 0
SYS  18493  18493             92'175 __zipos_get("/home/jart/cosmo/o//third_party/quickjs/qjs.com") → 0
SYS  18493  18493            109'103 mmap(0, 196'608, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) → 0x100080060000 (458'752 bytes total)
SYS  18493  18493          1'312'096 inflate([u"SYMT☺   !☼        ♥       ♥       @     "...], 196'608, u"∞▌y|‼┼π└²)≈▌Ç (*♣E♫♣J╣Eí▄-gÑ@╣$Mô┤♪MôÉú•"..., 51'509) → 0
SYS  18493  18493          1'315'267 GetSymbolTableFromZip() → 0x100080060000
SYS  18493  18493          1'316'462 getenv("TERM") → "xterm-256color"
SYS  18493  18493          1'322'876 getenv("HOME") → "/home/jart"
SYS  18493  18493          1'323'867 getenv("TMPDIR") → NULL
SYS  18493  18493          1'330'820 openat(AT_FDCWD, "/zip/.args", O_RDONLY) → -1 ENOENT
SYS  18493  18493          1'338'617 mmap(0, 65'536, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) → 0x100080050000 (524'288 bytes total)
SYS  18493  18493          1'571'852 mmap(0, 65'536, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) → 0x100080090000 (589'824 bytes total)
SYS  18493  18493          1'745'596 mmap(0, 65'536, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) → 0x1000800a0000 (655'360 bytes total)
SYS  18493  18493          1'931'961 isatty(0) → true
SYS  18493  18493          1'935'043 tcgetwinsize(0, [{.ws_row=56, .ws_col=213}]) → 0
SYS  18493  18493          1'939'525 tcgetattr(0, [{.c_iflag=ICRNL|IXON|IUTF8, .c_oflag=OPOST|ONLCR, .c_cflag=CS8|CREAD|0xf, .c_lflag=ISIG|ICANON|ECHO|ECHOE|ECHOK|ECHOCTL|ECHOKE|IEXTEN, .c_cc[VMIN]=1, .c_cc[VTIME]=0, .c_cc[VINTR]=CTRL('C'), .c_cc[VQUIT]=CTRL('\\')}]) → 0
SYS  18493  18493          1'943'987 tcsetattr(0, TCSANOW, {.c_iflag=IUTF8, .c_oflag=OPOST|ONLCR, .c_cflag=CS8|CREAD|0xf, .c_lflag=ISIG|ECHOE|ECHOK|ECHOCTL|ECHOKE, .c_cc[VMIN]=1, .c_cc[VTIME]=0, .c_cc[VINTR]=CTRL('C'), .c_cc[VQUIT]=CTRL('\\')}) → 0
SYS  18493  18493          1'948'786 sigaction(SIGINT, {.sa_handler=&os_signal_handler, .sa_flags=SA_RESTART}, [{.sa_handler=SIG_DFL}]) → 0
QuickJS - Type "\h" for help
SYS  18493  18493          1'967'991 write(1, u"QuickJS - Type “\\h“ for help◙", 29) → 29
qjs >

So all you'd have to do is go down that burndown list of functions, e.g. tcgetwinsize(), madvise(), etc. and make sure they have an if (IsMetal()) { return enosys(); } so the Linux SYSCALL instruction doesn't cause it to crash. Then it'll likely start working.

tkchia commented 9 months ago

Hello @jart, hello @ghaerr,

Thank you. I have been a bit busy with life lately — and expect to be so for at least the next few weeks. Hopefully I can get around to work on the bare metal implementation some time soon. In the meantime perhaps you could have a go at it.

Hello @ghaerr,

I have a semi-working IRQ 0 (legacy timer) interrupt handler working on a development branch (https://github.com/tkchia/cosmopolitan/commits/tkchia/20230827/) — it probably needs some effort to be rebased or merged into the mainline code though.

What the code tries to do is to put the interrupt controllers "back" into legacy BIOS mode — even if booted via UEFI! — and also reprogram the legacy interval timer (PIT 0) to fire at 18.2 Hz. There is also some fantastically complicated logic to (semi-correctly) parse ACPI tables, cribbed from SeaBIOS. This is mainly there to sanity-check whether the PC actually has a legacy-compatible PIT 0.

Probably the more "correct" thing to do is to ditch all the legacy stuff, and program the interrupt controllers as modern APICs and look for newer hardware timer devices (e.g. Intel HPET) rather than legacy PITs. But this likely takes a bit more time to do right.

Thank you!

bellard commented 8 months ago

Note: cosmopolitan is now natively supported by https://bellard.org/quickjs