简体   繁体   中英

How to convert 64 bit function to 32 bit to 16 bit to 8 bit?

I am new to assembly i don't know how to convert 64 bit function to 32 bit to 16 bit to 8 bit

Purpose of below functions is to print number and return number of digits in it.

64 bit:

          global print_uint64
    section .text
        print_uint64:

          mov rax,rdi
          mov rdi,10
          mov rsi,rsp

       while:
          xor  rdx  ,rdx
          div  rdi
          add  rdx  ,48
          dec  rsi
          mov  [rsi],dl
          cmp  rax  ,0
          jne  while 

          mov rax,1
          mov rdi,1
          lea rdx,[rsp]
          sub rdx,rsi
          syscall

          lea rax,[rsp]
          sub rax,rsi
          ret

this works fine 

32 bit:

      global print_uint32
section .text
    print_uint32:

      mov eax,edi
      mov edi,10
      mov rsi,rsp

   while:
      xor  edx  ,edx
      div  edi
      add  edx  ,48
      dec  rsi
      mov  [rsi],dl
      cmp  eax  ,0
      jne  while 

      mov eax,1
      mov edi,1
      lea edx,[rsp]
      sub edx,esi
      syscall

      lea eax,[rsp]
      sub eax,esi
      ret

this works fine

16 bit:

      global print_uint16
section .text
    print_uint16:

      mov ax,di
      mov di,10
      mov rsi,rsp

   while:
      xor  dx  ,dx
      div  di
      add  dx  ,48
      dec  rsi
      mov  [rsi],dl
      cmp  ax  ,0
      jne  while 

      mov ax,1
      mov di,1
      lea dx,[rsp]
      sub dx,si
      syscall

      lea ax,[rsp]
      sub ax,si
      ret

but this did not work

I studied some questions on stack overflow regarding to this question, what i understand is i can't change rsp to esp because esp set upper 32 bits to zero so when we use [] on that access the memory not allocated to that program so it throws segment fault.

My question is:

1)What is the basic rules to convert 64 bit to 32 bit to 16 bit to 8 bit.

The basic rules is that pointers are still 64-bit no matter what width the data is. Just like in C, sizeof(int*) and sizeof(char*) are the same (on normal systems).

This is why all your versions have to use dec rsi and mov [rsi],dl : RSP holds a 64-bit pointer. Truncating it to 32-bit will not produce a valid pointer.

Also, syscall numbers and the fd are still the same size; mov ax,1 and mov di,1 leave garbage in the high bytes of the register. Use strace./my_program to decode what you actually passed to syscall .


The narrow versions can just zero-extend their input to 32-bit and jump to the 32-bit version.

But other than that, the basic rules is to use 32-bit operand-size whenever possible; it's the natural size for x86-64 ( The advantages of using 32bit registers/instructions in x86-64 ). eg always zero RDX/EDX/DX with xor edx,edx .

Writing a 32-bit register zero-extends to 64-bit, unlike 8/16 which just merge into the old value.
Why do x86-64 instructions on 32-bit registers zero the upper part of the full 64-bit register? Using 16-bit mov reg,imm16 and leaving high garbage is presumably why your system call didn't work.
Why doesn't GCC use partial registers?

Notably, lea dx,[rsp] / sub dx,si potentially leaves garbage in the upper bits of RDX, the arg to the write syscall.

This is a pointer subtract, calculating the number of elements in a char buffer. It makes no sense to choose an operand-size for this based on the size of the input number. It's actually fine to do a narrow subtract as long as you make sure the result is zero-extended into RDX, because in this case you know the number of digits will be at most 19 (for the 64-bit version) because that's how long 2^64-1 is in base 10.

So mov edx, esp / sub edx, esi is what you should have been doing in all the versions. Since the full RSP and RSI are nearby, their difference is small. Truncating the inputs before subtraction instead of truncating the result after doesn't change the result; carry propagates from low to high bits. See Which 2's complement integer operations can be used without zeroing high bits in the inputs, if only the low part of the result is wanted?

Using LEA to copy a register is not efficient; lea dx, [rsp] is architecturally identical to mov dx, sp but slower.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM