Of course PE64 is also valid, as are ELF32/ELF64, but with SO instead of DLL. While "export" is normally treated as an alias of "global", when creating a shared lib the compiler collects all top-level export routines in the main file and uses that list to populate the export section.
A requirement when producing shared libraries is that neither the main file nor any include file should generate any top-level code whatsoever - since it would never be executed. One case in particular is "constant x = somefunc()". At the time of writing such things are not flagged as errors, but they should be.
Phix takes the stern line that any dll/so it creates should be universally valid: it is no good returning a sequence to a C program that cannot comprehend it (and the same with phix-style strings, though IupRawStringPtr or something similar might be a possibility).
In a similar vein it might sound dandy having a dll routine that accepts (nested) sequences, but really there is little point if only phix knows how to invoke it - ultimately, the more you think about it, phix should not care if the dll it happens to be calling was written in Phix, C, or any other language, or for that matter harbour any hidden lists of language-specific caveats, or indeed force anyone else using the result of your efforts into using phix.
Instead, therefore, all parameters and return values of all export routines must be atom.
(Note that you can pass strings directly to any dll, but they will always arrive in raw atom pointer form, aka char*.)
Likewise you cannot allocate memory in a shared lib and free it in a completely separate module, or for that matter, free memory in a dll/so that was originally allocated elsewhere. When necessary the programmer must engineer some appropriate callback(s) to achieve the required result. I am of course talking about hll allocate() and free() - on Windows, should it prove absolutely necessary for some fixed API, you may be able to utilise kernel32/
In practice, when the compiler populates the export section, it does so via "static callbacks" which act as a kind of "trampoline", converting full 32/64-bit values to 31/63-bit integers or floats, etc, as required by the phix runtime. This further cements the requirement that all exports be fully callback-compatible.
For Windows, special treatment is given to an export function DllMain(atom hinstDLL, integer fdwReason, atom lpvReserved), which should be kept as simple as possible, and according to the official Microsoft documentation must not invoke any of: dll_open/LoadLibrary, FreeLibrary, GetStringType, CoInitialize, CreateProcess, create_thread/CreateThread, exit_thread/ExitThread, CreateNamedPipe, or any registry functions - in short, defer everything you can.
If such a function is detected, it is moved to the top of the list and becomes the main entry point for LoadLibrary;
if no such function is detected, the equivalent of a simple return 1 is automatically inserted,
in either case the standard :>init routine processing is always performed first.
You should also note that a DllMain that does anything other than return 1 is unlikely to work properly on Linux, since that code will/may never be executed.
[DEV (Linux)] Linux shared libraries are not supported at the time of writing. It creates a .so, but with incorrect relocations and an empty DT_REL. Most things found online strongly suggest pic (position independant code) is the only way to go, but that would be very difficult, especially for the data section, however libiup.so analysis suggests that relocations are fine. Since Linux has no equivalent mechanism to kernel32/LoadLibrary (ie DLL_PROCESS_ATTACH) it should not matter whether a DllMain exists or not (unless you call it manually). The initial code is likely to become something like:
::top <call >:init -- ret (not necessary, :>initStack clears the following field) ::check cmp dword[ds+12]|qword[ds+16],0 jne :top retwith :check being invoked from every "static callback" for every top-level export routine. (see pilx86.e/opInit) I should also note that pbinary.e/CreateELF() currently does nothing (useful) with the exports parameter. No timescale is offered, but hopefully it will be a matter of several relatively small tweaks, as opposed to a drastic system-wide rewrite.
At the time of writing no attempt has been made to allow export variables - in any case non-integers would have to be phix-style refs that only phix understands, and hence they would be of very limited use anyway, and besides it is not particularly difficult to compose suitable getter/setter routines instead.
Lastly, I trust it goes without saying that there is no way to "interpret" a shared lib, and hence there can be no way to trace/debug it at the source code level. [DEV] Also, no testing has been done yet that errors in dll files generate sensible ex.err files.