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

Arne Vajhøj arne at vajhoej.dk
Mon Aug 28 19:12:40 EDT 2023


On 8/28/2023 8:32 AM, Johnny Billquist wrote:
> On 2023-08-28 13:15, John Dallman wrote:
>> In article <ucgkbf$1bupe$2 at dont-email.me>, arne at vajhoej.dk (Arne Vajhøj)
>> wrote:
>>> On 8/27/2023 8:45 AM, Dan Cross wrote:
>>>> Ah, this is precisely what x86 does, again underscoring how it
>>>> is different from the approach that DEC chose with PDP-11
>>>> compatibility on the VAX.
>>>
>>> That is not how x86-64 work.
>>>
>>> You cannot mix code.
>>>
>>> A 64 bit program cannot use 32 bit libraries.
>>>
>>> The build will detect it and reject it. But if that check was
>>> disabled then the result would be almost guaranteed to crash.
>>
>> Dan is distinguishing between what the x86-64 hardware can do, and what
>> the commonly-used operating systems support.
>>
>> None of Linux, macOS and Windows support calling libraries built for the
>> x86-32 versions of the respective operating systems from programs built
>> for the x86-64 versions of the operating systems. You're quite right
>> about that.
> 
> [...]
> 
> 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?
> 
> 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.
> 
> 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.
> 
> If "it works, but some things will break", then it don't work. If you 
> need to turn on some mode bit in order for it to work, then it is not 
> like DG did it. (See previous comments about mode bits...)

Some things will break.

It is not easy to get 32 bit code executed in 64 bit mode,
because on common platforms:
- the linker/loader will not produce a 64 bit executable with
   code compiled for 32 bit
- the OS will not start a 32 bit executable in 64 bit mode

But it is possible with a little creativity. Assembler
byte directives are not checked for mode.

f1.c:

#include <stdio.h>

__asm__(
     "f:\n"
     "_f:\n"
     ".byte 0xb8, 0x29, 0x00, 0x00, 0x00\n" // 32 bit mode : movl $41, %eax
                                            // 64 bit mode : movl $41, %eax
     ".byte 0x40\n" // 32 bit mode : inc %eax
                    // 64 bit mode : (REX prefix)
     ".byte 0xC3\n" // 32 bit mode : ret
                    // 64 bit mode : ret
);

int f();

int main()
{
     printf("The answer is %d\n", f());
     return 0;
}

On Windows:

C:\Work\mode>del f1.exe

C:\Work\mode>gcc -m32 f1.c -o f1.exe

C:\Work\mode>f1
The answer is 42

C:\Work\mode>del f1.exe

C:\Work\mode>gcc -m64 f1.c -o f1.exe

C:\Work\mode>f1
The answer is 41

On Linux:

rm f1
gcc -m32 f1.c -o f1
./f1
The answer is 42
rm f1
gcc -m64 f1.c -o f1
./f1
The answer is 41

f2.c:

#include <stdio.h>

__asm__(
     "f:\n"
     "_f:\n"
     ".byte 0x1E\n" // 32 bit mode : push %ds
                    // 64 bit mode : (undefined)
     ".byte 0x1F\n" // 32 bit mode : pop %ds
                    // 64 bit mode : (undefined)
     ".byte 0xC3\n" // 32 bit mode : ret
                    // 64 bit mode : ret
);

void f();

int main()
{
     printf("Start\n");
     f();
     printf("Finish\n");
     return 0;
}

On Windows:

C:\Work\mode>del f2.exe

C:\Work\mode>gcc -m32 f2.c -o f2.exe

C:\Work\mode>f2
Start
Finish

C:\Work\mode>del f2.exe

C:\Work\mode>gcc -m64 f2.c -o f2.exe

C:\Work\mode>f2
Start

On Linux:

rm f2
gcc -m32 f2.c -o f2
./f2
Start
Finish
rm f2
gcc -m64 f2.c -o f2
./f2
Start
Illegal instruction (core dumped)

In some ways I think the first example is the worst.

But the bottom line is that while most 32 bit instructions
will work fine in 64 bit mode, then some will produce wrong results
and some will crash. It is not safe to execute 32 bit
code in 64 bit mode.

And it is a characteristics of how x86-64 is defined.

The toolchain and platform does not matter for that. How to get
assembler code into a HLL depends on the toolchain and what happens
when an invalid instruction is encountered depends on the
platform.

Which is why the toolchains don't allow mixing.

f.c:

#include <stdio.h>

void f()
{
     printf("f\n");
}

m.c:

#include <stdio.h>

void f();

int main()
{
     printf("main\n");
     f();
     return 0;
}

Windows:

C:\Work\mode>del m.exe

C:\Work\mode>gcc -c -m32 f.c -o f.obj

C:\Work\mode>gcc -m32 m.c f.obj -o m.exe

C:\Work\mode>m
main
f

C:\Work\mode>del m.exe

C:\Work\mode>gcc -c -m64 f.c -o f.obj

C:\Work\mode>gcc -m64 m.c f.obj -o m.exe

C:\Work\mode>m
main
f

C:\Work\mode>del m.exe

C:\Work\mode>gcc -c -m32 f.c -o f.obj

C:\Work\mode>gcc -m64 m.c f.obj -o m.exe
C:/GCC/13.1/bin/../lib/gcc/x86_64-w64-mingw32/13.1.0/../../../../x86_64-w64-mingw32/bin/ld.exe: 
i386 architecture of input file `f.obj' is incompatible with i386:x86-64 
output
C:/GCC/13.1/bin/../lib/gcc/x86_64-w64-mingw32/13.1.0/../../../../x86_64-w64-mingw32/bin/ld.exe: 
C:\Users\arne\AppData\Local\Temp\ccgfmh7E.o:m.c:(.text+0x71): undefined 
reference to `f'
C:/GCC/13.1/bin/../lib/gcc/x86_64-w64-mingw32/13.1.0/../../../../x86_64-w64-mingw32/bin/ld.exe: 
f.obj:f.c:(.text+0x18): undefined reference to `__imp____acrt_iob_func'
C:/GCC/13.1/bin/../lib/gcc/x86_64-w64-mingw32/13.1.0/../../../../x86_64-w64-mingw32/bin/ld.exe: 
f.obj:f.c:(.text+0x2d): undefined reference to `___mingw_vfprintf'
collect2.exe: error: ld returned 1 exit status

Linux:

rm m
gcc -c -m32 f.c -o f.o
gcc -m32 m.c f.o -o m
./m
main
f
rm m
gcc -c -m64 f.c -o f.o
gcc -m64 m.c f.o -o m
./m
main
f
rm m
gcc -c -m32 f.c -o f.o
gcc -m64 m.c f.o -o m
/usr/bin/ld: i386 architecture of input file `f.o' is incompatible with 
i386:x86-64 output
collect2: error: ld returned 1 exit status

Actually there are more reasons for that than just the unsafe
nature of executing 32 bit instructions in 64 bit mode.

Common toolchains use a different calling convention for 32 bit
and 64 bit. For 32 bit they typical prefix function name with an
underscore and pass arguments by stack (lots of variations though).
For 64 bit they do not prefix and pass first arguments by register.

But my hypothesis is that if a call from 64 to 32 had been safe,
then there would have been created a workaround for the calling
convention.

void __call32bit f()

or whatever.

Arne







More information about the Info-vax mailing list