0

As far as I noticed a 32bit program uses the FLAT memory model and the 64bit also. Using the 32bit program one has only 4GB to address and using 64bit (rcx for example) makes it possible to saturate the 40 to 48 address bits modern CPU provide and address even more.

So beside this and some additional control registers that a 32bit processor does not has, I ask myself if it is possible to run 32bit code in linux flawlessly.

I mean must every C code I execute be 64bit for instance?

I can understand that since C builds upon a stack frame and base pointer pushing a 32bit base pointer on stack my introduce problems where the stack pointer is 64bit and one might access the pop and push op codes in 32 bit fashion.

So what are the difference and is it possible to actually run 32bit code when running a 64bit Linux kernel?

[Update]

To state the scenario clear I am running a 64bit program and load a ELF64 file into memory map everything and call the method directly. The idea is to generate asm code dynamically.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
Martin Kersten
  • 5,127
  • 8
  • 46
  • 77
  • 2
    Yes, 64 bit kernel offers 32 bit compatibility mode. You can run 32 bit programs unmodified. – Jester Apr 09 '15 at 12:41
  • You listed the most important differences : max RAM use, register sizes... And yes, 64 bit systems can run 32 bit programs. – Aracthor Apr 09 '15 at 12:45
  • @Jester the idea is to run it within a 64bit program like reading a ELF64 file create a Code Segment and Data Segment (or just copy it to an existing segment) and run the code. – Martin Kersten Apr 09 '15 at 12:52
  • You miss an important point. 64-bit processors can't execute 32-bit assembly (well, maybe they can, but only coincidentally, it shouldn't be relied on). 64-bit processors actually have a legacy 32-bit mode, that "turn" them into a 32-bit processor. OSes use this legacy mode to execute 32-bit code. – ElderBug Apr 09 '15 at 13:07
  • Thats correct for the control registers. Beside this the original op codes are still doing the same as far as I understand it. – Martin Kersten Apr 09 '15 at 17:03
  • Just to add a twist, you might also take a look at [x32](http://en.wikipedia.org/wiki/X32_ABI) which: _allows programs to take advantage of the benefits of x86-64 (larger number of CPU registers, better floating-point performance, faster position-independent code shared libraries, function parameters passed via registers, faster syscall instruction) while using 32-bit pointers and thus avoiding the overhead of 64-bit pointers._ May not fit your needs, but is something interesting to be aware of. – David Wohlferd Apr 09 '15 at 20:51
  • Thanks David, this is something I will write down for further investigation. Once I can run 64bit I will use this.. The overhead of 64 bit pointers as far as I understood is only related to symbols when one is loading a extended r-register since one uses absolute positions to load values and have to deal with 8 bytes instead of 4. – Martin Kersten Apr 09 '15 at 21:45
  • When I use [rbx + 16] there will be no difference right? I mean the +16 is not expressed as 8 byte value? I am quite not sure... . – Martin Kersten Apr 09 '15 at 21:47
  • In the [intel manual](http://www.intel.com/content/dam/www/public/us/en/documents/manuals/64-ia-32-architectures-software-developer-manual-325462.pdf): _3.7.5.1 Specifying an Offset in 64-Bit Mode - Displacement — An 8-bit, 16-bit, or 32-bit value._ Also: the 'overhead' of 64 bit registers comes in handy when accessing more than 4 gig of address space. Something that is easily accomplished in c using malloc(). – David Wohlferd Apr 10 '15 at 04:43

2 Answers2

4

The main difference between them is the different calling conventions. On 32bit there are several types: __stdcall, __fastcall, ...

On 64bit (x64) there's only one (on Windows® platforms, about others I don't know) And it has some requirements, which are very different to 32bit. More on https://future2048.blogspot.com Note that ARM and IA64 (Itanium) also are different Encodings as x64 (Intel64/AMD64)

And you have 8 more general registers r8..r15, with sub registers r8d..r15d, r8w..r15w, r8b..r15b For the SIMD-based code also 8 additional registers xmm8..xmm15 are present.

The exception handling is data-based on 64bit; on 32bit it was code-based. So on 64bit for unwinding exceptions no longer instructions are used to build the exception frame. The exceptiom handling is completely data-based so that no additional instructions are required to try/catch.

The memory limit of 2GB on 32bit apps (or with /LARGEADDRESSAWARE 3GB on an app on 32bit Win OS, or 4GB on 64bit Win OS) is now much larger More on https://msdn.microsoft.com/en-us/library/windows/desktop/aa366778(v=vs.85).aspx

And of course, the general purpose registers have 64bit width instead of 32bit. So any integer calculation can process values bigger than the 32bit limit of 0..4294967296. (signed -2147483648..+2147483647)

Also reading and storing memory with a simple MOV instruction can read and write a QWORD (64bit) at once; on 32bit that only could write a DWORD (32bit).

Some instructions have been removed: PUSHA + POPA disappeared. And one Encoding form of INC/DEC is now used as REX-Byte prefix Encoding.

Lucas H. Xu
  • 909
  • 1
  • 11
  • 23
  • The exception-handling differences are very Windows-specific, at least the SEH stuff. In the i386 and x86-64 System V ABIs used by Linux, there's not a lot of difference AFAIK. (or are you talking about how `push ebp / mov ebp,esp` stack-frame stuff isn't required and it uses metadata now? Linux does that in 32-bit as well). Another important difference: SSE2 is baseline in x86-64, so you don't have to check CPUID before using SSE2 vector instructions to zero or copy some memory, and both the Windows and System V ABIs use xmm registers for passing/returning FP args. – Peter Cordes Aug 26 '16 at 22:15
  • Windows and SysV both switched from stack-args calling conventions to register-args calling conventions, [but with differences](http://stackoverflow.com/questions/4429398/why-does-windows64-use-a-different-calling-convention-from-all-other-oses-on-x86). See [the x86 tag wiki](http://stackoverflow.com/tags/x86/info) for ABI links, and maybe also [the not-very polished calling convention topic on SO docs](http://stackoverflow.com/documentation/x86/3261/calling-conventions#t=201608262219053050677) – Peter Cordes Aug 26 '16 at 22:20
  • @Peter Cordes, thanks for your answers. I'm talking about the unwind data (you call it meta data), that's only present on 64bit Windows and not on 32bit. on 32bit it was completely different. I implemented 32+64bit exception handlers in in my applications. That was no real fun on 32bit! –  Aug 28 '16 at 00:19
  • 1
    That *is* what I was talking about, sorry I didn't manage to say it clearly (600 char comment limit xD). Modern x86 Linux uses unwind data for 32 and 64-bit x86 (from the ELF `.eh_frame` / `.eh_frame_hdr` sections), and the default is `-fomit-frame-pointer` for both 32 and 64-bit code. The unwind info is generated from `.cfi_` directives that describe which register is saved where, and where the stack pointer is modified, just like the unwind info described in that link. AFAIK, 32 and 64-bit x86 Linux unwind the stack the same way as Win64 does (not following the linked list of saved EBP). – Peter Cordes Aug 28 '16 at 00:24
  • 1
    I haven't looked at the asm of `catch` blocks, though, for Windows or Linux. I just know you need to get this right for non-leaf functions if you want stack unwinding for exceptions to get past your stack frame. (And same for debug backtraces.) – Peter Cordes Aug 28 '16 at 00:27
  • they unwind the stack, yes, that's right, but 64bit is data-based using a.pdata section in PE EXE on Windows. and 32bit was code-based, cause each exception handler frame had to be set up using FS:[0] and TEB... 600chars really a bit short! –  Aug 28 '16 at 00:29
  • I haven't looked at Windows at all. I'm not arguing against your statements about how 64 and 32-bit Windows work. I'm *just* describing how Linux works, where i386 and amd64 Linux work the same way, similar to Win64. – Peter Cordes Aug 28 '16 at 00:33
  • yes, ok that's great, cause I know nearly nothing about Linux –  Aug 28 '16 at 00:33
  • Good job. Nice follow up. – Beeeaaar Jul 09 '18 at 15:23
1

Some 32 bit code will work in a 64 bit environment without modification. However, in general, functions won't work because the calling conventions are probably different (depends on the architecture). Depending on the program, you could write some glue to convert arguments to the calling convention you want. So you can't just link a 32-bit library into your 64-bit application.

However, if your entire application is 32-bit, you will probably be able to run it just fine. The word size of the kernel doesn't really matter. Popular operating systems all support running 32-bit code with a 64-bit kernel: Linux, OS X, and Windows support this.

In short: you can run 32-bit applications on a 64-bit system but you can't mix 32-bit and 64-bit code in the same application (barring deep wizardry).

Dietrich Epp
  • 205,541
  • 37
  • 345
  • 415
  • Thanks. That's the answer I am looking for. Can you shed some light on the deep wizardry? I thought about using 32bit and just alter the selector registers to point to the logical start of those code snippets. But thinking about jump points and everything I am in doubt if it would work but yet again the ret op codes, push and pop and alike would carry all those information with it. – Martin Kersten Apr 09 '15 at 12:57
  • @MartinKersten You can allocate some memory from the low 4GiB address space and set up 32 bit LDT selectors, yes. As long as the code doesn't try to interoperate with libraries or the kernel it should work. – Jester Apr 09 '15 at 13:14