[Info-vax] VAX vs. MV/8000 [was Re: Hard links on VMS ODS5 disks]

Dan Cross cross at spitfire.i.gajendra.net
Mon Aug 28 15:26:20 EDT 2023


In article <8350f858-079b-4361-be47-97f96a4dde32n at googlegroups.com>,
John Reagan  <xyzzy1959 at gmail.com> wrote:
>On Monday, August 28, 2023 at 12:26:04 PM UTC-4, John Dallman wrote:
>> In article <uci441$lgj$2... at news.misty.com>, b... at softjar.se (Johnny
>> Billquist) wrote: 
>> 
>> > I've tried to stay out, and don't want to get too deep in. 
>> > But in a sense, my question/issue would be: Can you take a binary 
>> > for x86 and run it without any "mode bit", or anything else, on an 
>> > x86-64 and it works?
>> Depends on how much setup you're willing to do before kicking off the 
>> binary. As Dan says, if you set up the segments right, many of the 
>> instructions will execute, but you'll hit problems with others, and 
>> certainly when you start trying to call OS functions that are at all 
>> complicated.
>> > Individual instructions is not all. Will the stack offsets remain 
>> > the same when I push stuff, will address references be fine when I 
>> > just use 32 bits, and do arithmetic and address offsets and so on.
>> Stack offsets and arithmetic operations will likely be OK; there may be 
>> some corner cases. The problem comes with memory references being passed 
>> between 32-bit and 64-bit code. Those are offsets within segments, and 
>> there is no intrinsic or canonical mapping between 32-bit segments and 
>> 64-bit segments on x86. 
>> 
>> To run executables intended for a 32-bit OS on a 64-bit OS, the usual 
>> route on x86 is "thunks", small chunks of code that receive OS calls made 
>> by the 32-bit code map the data into 64-bit addressing, and then call the 
>> OS' native 64-bit API. That's how 64-bit Windows and Linux run 32-bit 
>> executable. 64-bit macOS did that too, until Apple removed its ability to 
>> run 32-bit executables. The thunks are collected into special versions of 
>> the various run-time libraries that 32-bit code calls. Sometimes, for 
>> performance reasons, chunks of the "real" 32-bit run0time libraries are 
>> included.
>> > If that is the case, then yes, it has been done the same way DG did 
>> > it. Which truly meant you could take your old binaries and run them 
>> > on the newer hardware without any added trickery.
>> No, it isn't the full DG job. But it isn't a VAX-style separate mode 
>> either. It's something in between. 
>> 
>> An example of a modern separate mode comes with ARM. The ARM32 and ARM64 
>> instruction sets are /very/ different. Most ARM32 instructions are 
>> conditionally executed using predicate flags in the instructions, taking 
>> up 5 bits in almost all instructions. ARM64 threw that out, and some old 
>> ARM hands reckon ARM64 is more like MIPS than ARM32. 
>> 
>> John
>I've tried to stay out of this so far, but I do want to point out that 64-bit mode
>doesn't use CS, DS, ES,  SS segments in that way.  You can put whatever values you want into
>them, but they read as 0.

It's unclear what you mean here.  Do you mean the segment
selectors; that is, the visible portion of the segment
registers?  If so, the "read as 0" part is incorrect.  They read
as whatever value you write to them (most OS's zero them out
once they get into long mode in early boot).  This is easily
shown empirically:

: samudra; cat segregs.c
#include <stdio.h>

int
main()
{
	unsigned short cs, ds, es, ss, fs, gs;

	cs = 0;
	ds = 0;
	es = 0;
	fs = 0;
	gs = 0;
	ss = 0;

	asm("mov %%cs, %0" : "=r"(cs) ::);
	asm("mov %%ds, %0" : "=r"(ds) ::);
	asm("mov %%es, %0" : "=r"(es) ::);
	asm("mov %%ss, %0" : "=r"(ss) ::);
	asm("mov %%fs, %0" : "=r"(fs) ::);
	asm("mov %%gs, %0" : "=r"(gs) ::);

	printf("cs = %#hx\n", cs);
	printf("ds = %#hx\n", ds);
	printf("es = %#hx\n", es);
	printf("ss = %#hx\n", ss);
	printf("fs = %#hx\n", fs);
	printf("gs = %#hx\n", gs);

	return 0;
}
: samudra; make segregs
cc -O2 -pipe    -o segregs segregs.c
: samudra; ./segregs
cs = 0x2b
ds = 0x23
es = 0x23
ss = 0x23
fs = 0x23
gs = 0x23
: samudra; file segregs
segregs: ELF 64-bit LSB shared object, x86-64, version 1
: samudra; uname -a
OpenBSD samudra.gajendra.net 7.3 GENERIC.MP#17 amd64
: samudra;

(That's on an i9; same program run on Ryzen, EPYC, and Xeon
under a variety of different operating systems gives similar
results.)

Indeed, if one could not read the selector values in 64-bit mode
then it would not be possible to preserve them on entry to a
64-bit kernel from a 32-bit user-space program.

As an aside, since much of this discussion has revolved around
mixing 32- and 64-bit code in the same "process", Unix is an
interesting example here.  Things like kernel page-table
isolation not withstanding, the mental model is that every Unix
process has the kernel mapped into it; 64-bit kernels running
32-bit user programs are, therefore, an example of two different
types of code running in the same "process."

>We do use GS for some things in the OS.

GS and FS are special and commonly used for CPU-local and
thread-local storage, respectively.  With the fast system call
instruction support, you pretty much need GS (cf the `SWAPGS`
instruction).

However, none of this means that one cannot use segmentation to
switch into 32-bit compatibility mode; this can be accomplished
in a few different ways, such as by means of a _long jump_ into
a 32-bit code segment, as I mentioned elsewhere in this thread.
Intel calls this a "Far jump" in the documentation of the JMP
instruction: the target is a segment selector, which is an index
into one of the two segment tables (either global or local).

It is correct that just MOV'ing a value into a segmentation
register has little effect in 64-bit mode, but that's not how
you switch into 32-bit mode.

>From the architecture manual, Volume 1,
>section 3.4.2.1, "Segment Registers in 64-Bit Mode"
>
>In 64-bit mode: CS, DS, ES, SS are treated as if each segment base is 0, regardless of the value of the associated
>segment descriptor base. This creates a flat address space for code, data, and stack. FS and GS are exceptions.

It is important to note that, in this section, the SDM is
talking about the entire segment register, not just the visible
segment selectors we see in %cs et al.  The values in the
segment selector portion are indicies into a table of segment
descriptors that are themselves pointed to by the GDTR and LDTR.
In 64-bit the base is always 0 (regardless of what's in the
descriptor) and the limit is ignored.  In 32-bit mode, base and
limit are respected.

>Both segment registers may be used as additional base registers in linear address calculations (in the addressing
>of local data and certain operating system data structures).
>
>Even though segmentation is generally disabled, segment register loads may cause the processor to perform
>segment access assists. During these activities, enabled processors will still perform most of the legacy checks on
>loaded values (even if the checks are not applicable in 64-bit mode). Such checks are needed because a segment
>register loaded in 64-bit mode may be used by an application running in compatibility mode.
>
>Limit checks for CS, DS, ES, SS, FS, and GS are disabled in 64-bit mode.

This is good background information for those wishing to
understand the architecture at a level deeper but not super
relevant to the topic at hand.  If you want to switch into
32-bit mode from 64-bit mode, you need a far jump/call gate.

But again, all of this is qualitatively different than executing
a 32-bit instruction in 64-bit mode, which is normal.

	- Dan C.




More information about the Info-vax mailing list