Dyalog / library-core

Core application development library for Dyalog APL
MIT License
2 stars 3 forks source link

When creating new processes in a classic interpreter, force "DYALOG_NETCORE=0" into the arguments #13

Closed mbaas2 closed 1 year ago

mbaas2 commented 2 years ago

When initiating a new process on a machine that has a global setting of DYALOG_NETCORE=1 in a classic interpreter that a cmdline-setting DYALOG_NETCORE=0, make sure that the new session is also created with DYALOG_NETCORE=0.

Probably the initial part of that sentence is not relevant and the issue should be: when launching new processes in a classic interpreter, be sure to pass an argument DYALOG_NETCORE=0.

bpbecker commented 1 year ago

I don't think we can do this because we can't always be sure that an interpreter is a classic interpreter. In either classic or Unicode, the executable name is dyalog or dyalogrt.

lstefano71 commented 1 year ago

I once asked support if there was a way to distinguish the classic interpreter from the unicode one by looking at the executable. I was told the executable contains the name of the full path to the pdb file, and that includes the kind of interpreter. In practice the executable contains the string \{kind}\winapi\dev\opt\dyalog{type}.pdb where "kind" can be either "classic" or "unicode" and "type" can be either "" or "rt". If you think this is slightly foolish, I agree, but, hey, it works! This is for the Windows version. It's possible that something similar also applies to the Linux version.

If you want to target more precisely the exact spot in the file where this string is present, then that is possible by parsing the binary structures contained in the file. For Windows you want to learn to parse the PE structures as described here and traverse, one structure at a time, the file until you get to the IMAGE_DEBUG_TYPE_CODEVIEW structure.

This function, which is based on bits of our development environment that are tedious to port but could be ported to a vanilla interpreter, does exactly this:

     ∇ pdbname←ReadPDBName filename;tie;R_;dos;nt;rdr;opt;DebugDir;debug;Sections;i;startopt;startsect;j;off;cvd
[1]    pdbname←⎕NULL
[2]    :If filename≡''
[3]        filename←'\\aplstore01\discol\dyalog18.2\Dyalog APL-64 18.2 Unicode\dyalogrt.exe'
[4]    :EndIf
[5]    tie←filename #.PLUS.ntie 0
[6]    rdr←⎕NEW #._OS.BinaryStream tie
[7]   
[8]    R_←#.PLUS.nuntie #.uDefer tie
[9]    dos←HeaderDOS ReadStruct rdr 0
[10]   →(dos.e_magic≢23117)⍴0 ⍝ not a PE
[11]   nt←HeaderNT ReadStruct rdr dos.e_lfanew
[12]   →(nt.Signature≢17744)⍴0 ⍝ IMAGE_NT_SIGNATURE: not an NT exe
[13]   →(nt.NumberOfSections=0)⍴0
[14]   startopt←rdr.Seek'current' 0
[15]   opt←HeaderOpt ReadStruct rdr startopt
[16]  ⍝ →(opt.Magic≢523)⍴0 ⍝     PE64
[17]   →(opt.NumberOfRvaAndSizes<7)⍴0
[18]   Sections←⍬
[19]   startsect←rdr.Seek'current' 0
[20]   :For i :In ⍳nt.NumberOfSections
[21]       Sections,←HeaderSect ReadStruct rdr(rdr.Seek'current' 0)
[22]   :EndFor
[23]   DebugDir←opt.Directories[7;]
[24]   →(DebugDir∨.=0)⍴0 ⍝ directory entry
[25]   Sections←Sections[⍋Sections.VirtualAddress]
[26]   off←Sections RVAtoRaw⊃DebugDir
[27]   debug←HeaderDebug ReadStruct rdr off
[28]   →(debug.Type≠2)⍴0 ⍝ CodeView
[29]   cvd←(HeaderCVDebug debug.SizeOfData)ReadStruct rdr debug.PointerToRawData
[30]   pdbname←⎕UCS cvd.(Path↑⍨¯1+Path⍳0)
[31]   R_.DO
[32]  ⍝∘ (c)APLIT D PE  stf 08/11/2022 11:19          ¯1851787281 C01
     ∇

The structures are described like so:

     ∇ spec←HeaderDOS
[1]   
[2]    spec←⍬
[3]    spec,←⊂'e_magic' 'u16'
[4]    spec,←⊂'e_cblp' 'u16'
[5]    spec,←⊂'e_cp' 'u16'
[6]    spec,←⊂'e_crlc' 'u16'
[7]    spec,←⊂'e_cparhdr' 'u16'
[8]    spec,←⊂'e_minalloc' 'u16'
[9]    spec,←⊂'e_maxalloc' 'u16'
[10]   spec,←⊂'e_ss' 'u16'
[11]   spec,←⊂'e_sp' 'u16'
[12]   spec,←⊂'e_csum' 'u16'
[13]   spec,←⊂'e_ip' 'u16'
[14]   spec,←⊂'e_cs' 'u16'
[15]   spec,←⊂'e_lfarlc' 'u16'
[16]   spec,←⊂'e_ovno' 'u16'
[17]   spec,←⊂'e_res' 'u16' '4'
[18]   spec,←⊂'e_oemid' 'u16'
[19]   spec,←⊂'e_oeminfo' 'u16'
[20]   spec,←⊂'e_res2' 'u16' '10'
[21]   spec,←⊂'e_lfanew' 'i32'
[22]  
[23]  
[24]  ⍝typedef struct _IMAGE_DOS_HEADER {  // DOS .EXE header
[25]  ⍝    USHORT e_magic;         // Magic number: 23117 for "MZ"
[26]  ⍝    USHORT e_cblp;          // Bytes on last page of file
[27]  ⍝    USHORT e_cp;            // Pages in file
[28]  ⍝    USHORT e_crlc;          // Relocations
[29]  ⍝    USHORT e_cparhdr;       // Size of header in paragraphs
[30]  ⍝    USHORT e_minalloc;      // Minimum extra paragraphs needed
[31]  ⍝    USHORT e_maxalloc;      // Maximum extra paragraphs needed
[32]  ⍝    USHORT e_ss;            // Initial (relative) SS value
[33]  ⍝    USHORT e_sp;            // Initial SP value
[34]  ⍝    USHORT e_csum;          // Checksum
[35]  ⍝    USHORT e_ip;            // Initial IP value
[36]  ⍝    USHORT e_cs;            // Initial (relative) CS value
[37]  ⍝    USHORT e_lfarlc;        // File address of relocation table
[38]  ⍝    USHORT e_ovno;          // Overlay number
[39]  ⍝    USHORT e_res[4];        // Reserved words
[40]  ⍝    USHORT e_oemid;         // OEM identifier (for e_oeminfo)
[41]  ⍝    USHORT e_oeminfo;       // OEM information; e_oemid specific
[42]  ⍝    USHORT e_res2[10];      // Reserved words
[43]  ⍝    LONG   e_lfanew;        // File address of new exe header
[44]  ⍝  } IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;
[45]  ⍝∘ (c)APLIT D PE  stf 07/11/2022 11:36           1008385558 C01
     ∇

The hardest bit by far was to understand how the mapping from RVAs to Raw offsets works:

     ∇ off←Sections RVAtoRaw rva;j
[1]   ⍝ Convert an RVA to a Raw pointer in the file
[2]   ⍝ Sections must be sorted by VirtualAddress
[3]    j←Sections.VirtualAddress ⍸ rva
[4]    off←Sections[j].PointerToRawData+rva-Sections[j].VirtualAddress
[5]   ⍝∘ (c)APLIT D PE  stf 08/11/2022 11:19          ¯1387480123 C01
     ∇

Have fun... and... should you happen to talk to somebody at Dyalog who can provide a more straightforward way to figure out which interpreter is which (starting from v19 for instance), we'd all be delighted to throw away this madness :-)

bpbecker commented 1 year ago

Thanks Stefano... I had a chat with Andy about how can you tell what Dyalog version would start from an executable and he also indicated it's not a pretty process (which you've now confirmed 😄)

The issue is further complicated when you're starting a process via ssh and you don't necessarily have access to the executable to inspect it.

With the advent of 2250⌶, I'm marking this issue as "wontfix" because it should really be the started process' responsibility to check for the availability of .NET and act appropriately.