Author Topic: The illusion of portability at assembly level  (Read 6954 times)

Offline fredericopissarra

  • Full Member
  • **
  • Posts: 373
  • Country: br
The illusion of portability at assembly level
« on: February 02, 2023, 02:58:10 PM »
Here's the same code, using libc, for 3 modes of operation: Real, i386 and x86-64. Just a simple hello.
Code: [Select]
; hello16.asm
;
;  Using TurboC 2.01 or Borland C++ 3.1:
;
;    nasm -fobj hello16.asm -o hello16.obj
;    tlink -L\tc2\lib \tc2\lib\c0s.obj+hello16.obj,hello16.exe,nul,cs.lib
;
;  Change the absolute path if using Borland C++. Or, if using Microsoft C 6,
;  the command line is entirely different (and, yet, you must link c0.obj and the
;  small model libc library.
;
  bits  16

  ; There is no '.text', '.data' or '.rodata' in MSDOS.
  ; and the CLASS is necessary for some linkers (tlink, for example).

  section _DATA class=DATA

msg:  db  `Hello\n`,0

  section _TEXT class=CODE

  extern _printf

  global _main

_main:
  push  msg
  call  _printf
  add   sp,2      ; cleanup (2 bytes)

  xor   ax,ax
  ret
Notice, after the call we have to cleanup 2 bytes from the stack. Now, i386:
Code: [Select]
  bits  32

  section .rodata

msg:  db  `Hello\n',0

  section .text

  ; As in 16 bits mode libc must be preceeded by _.
  extern _printf

  global  _main

_main:
  push  msg
  call  _printf
  add   esp,4     ; Clean up, 4 bytes!

  xor   eax,eax
  ret
Not only we have to clean 4 bytes, but the segment names changed. The `.` prefix can be used in NASM as a shortcut, but 16 bits code excpects to be named as `_TEXT`, `_DATA` or `_BSS` (there is no `.rodata` equivalent). The use of E?? registers here is of no consequence, since we could use them in 16 bits code as well.

Now... x86-64:
Code: [Select]
  bits  64
  default rel

  section .rodata

msg:  db  `Hello\n`,0

  section .text

  ; NO _ prefix for identifiers.
  extern printf

  global main

main:
  %ifdef SYSVABI
    sub   rsp,8
    xor   eax,eax ; To inform # of fp args is mandatory in SysV ABI.
    lea rdi,[msg]   ; Needed to use RIP-relative addressing.
                    ; mov rdi,msg works, but requires a relocation.

    call  printf wrt ..plt  ; If in SysV ABI and linking dynamically.
  %else
    sub   rsp,40
    lea rcx,[msg]   ; Needed to use RIP-relative addressing.
                    ; mov rdi,msg works, but requires a relocation.

    call  printf
  %endif

  ; NO stack cleanup...

  xor  eax,eax
  ret
Suffice to say there is no RCX or RDI register in i386 or real modes, and if you are using SysV ABI (Linux, FreeBSD, MacOS), you need to zero AL before calling printf. SysV ABI use RDI as the first argument, MS ABI uses RCX... and we need to accomodate the shadow space in MS ABI (and align for DQWORD in both ABIs)...

The 3 sources are totally different at source level AND at binary level, hence they are NOT portable.
« Last Edit: February 02, 2023, 03:04:35 PM by fredericopissarra »