NASM - The Netwide Assembler

NASM Forum => Programming with NASM => Topic started by: mjrice on June 22, 2018, 01:44:05 AM

Title: A 64-bit Nasm assignment procedure; set(val,var)?
Post by: mjrice on June 22, 2018, 01:44:05 AM
Callable from 64-bit Linux, gfortran or gcc (pick one). Is there a thread for function/procedure code contributions?

To keep it simple, val is a literal real or integer, var a variable.

global set_

set_:
Title: Re: A 64-bit Nasm assignment procedure; set(val,var)?
Post by: Frank Kotler on June 24, 2018, 02:00:09 AM
Dunno. Maybe something like...
Code: [Select]
global set_ ; trailing underscore for fortran?
set_:
; align stack?
mov rax, [rdi] ; dereference val?
mov [rsi], rax ; put it in var?
; restore stack?

Did you get your other problem solved?

Best,
Frank

Title: Re: A 64-bit Nasm assignment procedure; set(val,var)?
Post by: mjrice on September 30, 2018, 03:42:16 PM
Dunno. Maybe something like...
Code: [Select]
global set_ ; trailing underscore for fortran?
set_:
; align stack?
mov rax, [rdi] ; dereference val?
mov [rsi], rax ; put it in var?
; restore stack?

Did you get your other problem solved?

Best,
Frank

Hi, Frank.

If you mean from years ago, when you helped me successfully implement assembly code for Joseph Weisenbaum's SLIP? Yes, I'm that "Michael."

I SLIP-ed up. When I posted back in June I thought I'd receive an email notification that somebody had posted a reply. Since I never did, I just waited. Sorry about that.

Back in June I was thinking of recoding the SLIP NASM functions, formerly written for 32 bit, for a 32 bit gfortran, to run in 64 bit. I'll now continue with that but would first like to verify that the code I archived will still run in a 32 bit environment, which I no longer have. Is there a way to make 32 bit NASM code acceptable to a 64 bit environment? Or maybe a 64 bit environment to emulate 32 bit?

Michael
Title: Re: A 64-bit Nasm assignment procedure; set(val,var)?
Post by: Frank Kotler on October 01, 2018, 06:43:34 AM
Hi Michael,

By "other problem" I meant the "loc" function. I'm afraid I don't remember "SLIP".
 Good to see you still around, though!

To get emails about replies, you've got to check the "notify" box. Click on "unnotify" to turn it off if the volume becomes overwhelming.

32-bit code should run in a 64-bit system, provided that any libraries you link to are present. You might need to install "gcc multilibs" or some such. 32-bit NASM code itself shouldn't be a problem. You'll need to tell ld "-m elf_i386". "-m32" for gcc, to get 32-bit code. Dunno about gfortran.

Best,
Frank

Title: Re: A 64-bit Nasm assignment procedure; set(val,var)?
Post by: mjrice on October 01, 2018, 07:18:43 PM
Hello, Frank.

The SLIP work was seven or more years ago. I know that for sure because I dated the dvd (2011) on which I archived it. Long time. Good to see you're still around too. You were big help.

Back in June it seemed I'd have to use fortran's loc function instead of writing my own madov (SLIP machine address) function in NASM.  I'll be looking into that again.

Thanks for explaining the "Notify" function. Checked.

Just before I returned to the forum today I found this on the web:

dnf install gfortran-multilib
gfortran -m32 foo.f

And away we go...

Michael





Title: Re: A 64-bit Nasm assignment procedure; set(val,var)?
Post by: mjrice on October 10, 2018, 02:00:24 AM
I added two commands to set and restore the stack, then commented them out.

Variable k gets changed from 0 to 13 in both versions, but also get the same seg fault with both. How does one
return to Fortran from a subroutine call?

Michael

*****************************

global set_ ; trailing underscore for fortran?
set_:
; align stack?
push rbp       ; <== added
mov rax, [rdi] ; dereference val?
mov [rsi], rax ; put it in var?
; restore stack?
pop rbp        ; <== added
ret

      external :: set
      integer k
      k=0
      call set(13,k)
      print *, k
      end

*************************************

[mrice@localhost ~]$ nasm -f elf64 set.asm -o set.o && gfortran settest.f90 set.o && ./a.out
          13

Program received signal SIGSEGV: Segmentation fault - invalid memory reference.

Backtrace for this error:
Segmentation fault (core dumped)
[mrice@localhost ~]$
Title: Re: A 64-bit Nasm assignment procedure; set(val,var)?
Post by: Frank Kotler on October 10, 2018, 03:48:58 AM
I ASSume that we return to fortran with "ret". How else?

The fact that your code prints "13" indicates that the "set_" worked. It also indicates that we returned to fortran  before the segfault occurred. I have no idea what's happening here.

Best,
Frank

Title: Re: A 64-bit Nasm assignment procedure; set(val,var)?
Post by: mjrice on October 10, 2018, 11:02:16 AM
Makes sense.

I fixed it by adding some gfortran flags I used the last time, but wasn't sure I needed them for true 64-bit.

[mrice@localhost ~]$ nasm -f elf64 set.asm -o set.o && gfortran -fdefault-integer-8 -fdefault-real-8 -fno-range-check settest.f90 set.o && ./a.out
                   13
[mrice@localhost ~]$

Michael



Title: Re: A 64-bit Nasm assignment procedure; set(val,var)?
Post by: mjrice on October 10, 2018, 11:19:41 AM
I have at least 13 of these conversions of 32-bit NASM subprograms to 64-bit for use with Fortran to do that may prove useful for anyone doing something similar. I now have two threads, this one and another, "Running i386 on x86-64" or something like that. Should I just continue on this thread, the one I started back in June, or another one all together in a different category?

Michael   
Title: Re: A 64-bit Nasm assignment procedure; set(val,var)?
Post by: mjrice on October 10, 2018, 04:53:25 PM
Another setter subroutine, strind(var,var-addr). Tested ok.

Anything troubling about the asm code? I held my breath when I ran it.

Michael

global strind_
strind_:
mov rax, [rdi] ; dereference val?
push rbp
mov rbp, [rsi] ; dereference addr of var?
mov [rbp], rax ; put val where rbp points?
pop rbp
ret

      external :: strind
      integer k
      k=0
      call strind(13,loc(k))
      print *, k
      end

[mrice@localhost ~]$ nasm -f elf64 strind.asm -o strind.o && gfortran -fdefault-integer-8 -fdefault-real-8 -fno-range-check strindtest.f90 strind.o && ./a.out
                   13
[mrice@localhost ~]$

Title: Re: A 64-bit Nasm assignment procedure; set(val,var)?
Post by: dreamCoder on October 11, 2018, 02:25:04 AM
I added two commands to set and restore the stack, then commented them out.

Variable k gets changed from 0 to 13 in both versions, but also get the same seg fault with both. How does one
return to Fortran from a subroutine call?

Michael

*****************************

global set_ ; trailing underscore for fortran?
set_:
; align stack?
push rbp       ; <== added
mov rax, [rdi] ; dereference val?
mov [rsi], rax ; put it in var?
; restore stack?
pop rbp        ; <== added
ret


      call set(13,k)
*************************************

Segmentation fault - invalid memory reference.

I know nothing about FORTRAN but at this line, you're using dereferencing instead of taking direct values from the register?

should be
    mov rax,rdi   ;13 is a direct immediate
instead of
    mov rax,[rdi]? ;13 is an address

Or I maybe wrong altogether.
Title: Re: A 64-bit Nasm assignment procedure; set(val,var)?
Post by: Frank Kotler on October 11, 2018, 03:24:07 AM
Hi dreamCoder.

Thanks for jumping in here! If rdi holds the value 13, you are certainly correct. My understanding was that fortran always passes parameters "by reference". If I misunderstand that, it would explain a lot. However, I don't think fortran would be printing 13 at all (?).

In "strind_", Michael dereferences the value twice, and this seems to work. We don't use rbp as the usual "stack frame pointer", but this shouldn't be a problem.

I don't seem to have gfortran on this machine, so I can't try this stuff. I may get up the ambition to install it, but haven't yet. I'm old and tired and burnt out and a little depressed. My memory is all shot and my ambition is worse. Sorry.

I suppose multiple threads - with meaningful names - would make it easier for people to find this stuff in the future. I dunno...

Best,
Frank

Title: Re: A 64-bit Nasm assignment procedure; set(val,var)?
Post by: mjrice on October 11, 2018, 04:16:11 AM
Yes, Fortran passes by reference.

Frank, what would be a conventional way to do the necessary dereferencing?

Michael
Title: Re: A 64-bit Nasm assignment procedure; set(val,var)?
Post by: Frank Kotler on October 11, 2018, 05:03:46 AM
Code: [Select]
mov rdi, [rdi]
... would do it. It's okay to use the same register... Or a different register...

Best,
Frank

Title: Re: A 64-bit Nasm assignment procedure; set(val,var)?
Post by: dreamCoder on October 11, 2018, 11:49:52 AM
Hi dreamCoder.

Thanks for jumping in here! If rdi holds the value 13, you are certainly correct. My understanding was that fortran always passes parameters "by reference". If I misunderstand that, it would explain a lot. However, I don't think fortran would be printing 13 at all (?).

In "strind_", Michael dereferences the value twice, and this seems to work. We don't use rbp as the usual "stack frame pointer", but this shouldn't be a problem.

I don't seem to have gfortran on this machine, so I can't try this stuff. I may get up the ambition to install it, but haven't yet. I'm old and tired and burnt out and a little depressed. My memory is all shot and my ambition is worse. Sorry.

I suppose multiple threads - with meaningful names - would make it easier for people to find this stuff in the future. I dunno...

Best,
Frank
Ok then. I am completely clueless on FORTRAN. But I wonder where in memory the value is maintained? Where does RDI point to? Because sending an immediate 13 as argument doesn't reveal much about its storage (static, heap, stack etc). Anyway this is a good revelation for me about a new calling convention.
Title: Re: A 64-bit Nasm assignment procedure; set(val,var)?
Post by: mjrice on October 11, 2018, 12:28:41 PM
Passing subroutine parameters by reference allows one to change the parameter within the subroutine. A good example is a subroutine that swaps a and b.

Fortran:

      subroutine swap(a,b)
      temp=b
      b=a
      a=temp
      return
      end

Note: Fortran automatically types variables that begin with I-J-K-L-M-N as integer,
all others as real (float). Which means you that needn't explicitly declare them.

C:
void swap(int *a, int *b)
{
   int t;
 
   t  = *b;
   *b = *a;
   *a = t;
}
     
Fortran passes addresses implicitly. C must do it explicitly.

Michael
Title: Re: A 64-bit Nasm assignment procedure; set(val,var)?
Post by: dreamCoder on October 11, 2018, 01:07:54 PM
Thanks for the explanations Michael.

From what I see, I suspect that gfortran uses the heap to store the arguments. That means that you can't use RBP/RSP addressing due to them not being on the stack. But if they do, you could try moving up and down the stack to check its content, like;

mov rax,[rsp-16] / [rsp+16]

or something like that, regardless of alignment.

And also try comparing the value of RSP and RDI. If they're radically different, than I am pretty sure that RDI is pointing to a heap memory area. Similarly you can move up and down RDI to verify the content of any arguments in the vicinity.

This is fun and new to me. I wished I knew gfortran.
 
Title: Re: A 64-bit Nasm assignment procedure; set(val,var)?
Post by: mjrice on October 12, 2018, 03:31:14 AM
To Frank:

mov rdi, [rdi]

Thanks for that. It's been something I was wondering about.

To: dreamcoder

I'm unsure what problem you're addressing. I was only using RBP as a temporary register to execute the necessary double dereferencing. Frank's provision (see above) makes doing that much more straightforward.

Michael







 
Title: Re: A 64-bit Nasm assignment procedure; set(val,var)?
Post by: dreamCoder on October 12, 2018, 04:34:12 AM
I was actually trying to zoom in into the possibility that your earlier code was actually overwriting / missing the return address and hence throwing invalid memory reference upon return. x86's call / ret mechanism work the same across languages / calling conventions - return address must reside at TOS prior to RET. You first code is showing that clear symptom of missing the return address. But it's good to know that you already solved it.
Title: Re: A 64-bit Nasm assignment procedure; set(val,var)?
Post by: dreamCoder on October 12, 2018, 06:13:44 AM
After a detailed investigation, I found that the statement

call set(13,k)

will actually force the compiler to put a foreign immediate on the stack. This will make the return address to be off by one 'notch', hence the invalid reference upon return.

The remedy is not to pass an immediate to a call, since gfortran is pass-by-reference, but simply to supply a proper argument to such function where gfortran has more control over the internal stack. And btw, gfortran aligns the stack by default. So the solution is much simpler and more straightforward.

Code: [Select]
! prog.f     
! gfortran prog.f test.o -o prog
     
      external :: set
      integer k,j
      k=0
      j=13     !use this instead of direct immediate to feed an external function
      call set(j,k)
      print *,k
      end

The asm file
Code: [Select]
;test.asm
;nasm -f elf64 test.asm

global set_
section .text
set_:
mov rax, [rdi]
mov [rsi], rax
ret

That's how curious I am on new things that need reversing. hehehe ;D 

EDIT: On the second thought, using a direct immediate to feed an external function is going to clobber RDI.
Title: Re: A 64-bit Nasm assignment procedure; set(val,var)?
Post by: mjrice on October 12, 2018, 12:40:09 PM
Ignore. Pressed wrong button.
Title: Re: A 64-bit Nasm assignment procedure; set(val,var)?
Post by: mjrice on October 12, 2018, 02:12:37 PM
To dreamcoder:

The error could have many causes, most likely the disparity between 64-bit NASM compilation and not insisting that gfortran use 64-bit integers and reals, which was fixed by adding those flags to the gfortran compilation. Not insisting leaves those sizing decisions to gfortran's optimizer, which might leave garbage in the high-order 32-bits of a 64-bit word. Who knows...

Moving on. Earlier in the year, I believe I found value, and maybe behavioral, differences, between addresses acquired via Fortran's "loc" function and a machine address function written in assembly, "madov," which I used seven years ago. I'll be reviewing my notes about that and see what conclusion I reached, if any.

If you've downloaded gfortran you could write your own madov.asm function and test it with same gfortran main program. The "madov" function must leave the address in RAX, and must have those same ending underscores in its name. And gfortran main program must include.

integer, external :: madov

You could also keep Fortran's "loc" to see if they match.

Infer how to add it to the gfortran compilation by looking at how "set" was added. Don't forget the three Fortran flags.

Michael
Title: Re: A 64-bit Nasm assignment procedure; set(val,var)?
Post by: mjrice on October 13, 2018, 01:59:40 AM
I looked through some notes from last June and found that I was testing addresses without the three Fortran flags. With the flags included, the addresses acquired with "madov" and "loc" match.

global madov_

section .text  ;return the (64-bit integer) machine address of a variable

madov_:
  mov  rax, rdi
  ret

      integer, external :: madov
      c=17.5
      i=loc(c)
      j=madov(c)
      print *, i,j
      end

[mrice@localhost ~]$ nasm -f elf64 madov.asm -o madov.o && gfortran -fdefault-integer-8 -fdefault-real-8 -fno-range-check madovtest.f90 madov.o && ./a.out
      140730831529000      140730831529000
[mrice@localhost ~]$ nasm -f elf64 madov.asm -o madov.o && gfortran -fdefault-integer-8 -fdefault-real-8 -fno-range-check madovtest.f90 madov.o && ./a.out
      140720354825944      140720354825944

Next up, two asm functions to access the contents of an address; "cont" to access a real (float), "inhalt" to access an integer.

Michael



 
Title: Re: A 64-bit Nasm assignment procedure; set(val,var)?
Post by: mjrice on October 14, 2018, 12:06:37 AM
This isn't going to work. Seven years ago, back in 32-bit land, one could store an address in a 32-bit word. That no longer holds in 64-bit land, as the addresses printed in my last post reveal.

Decimal                   Hex
140720354825944   7FFC02C316D8

I need to poke around and see if there's a path forward.

Michael




Title: Re: A 64-bit Nasm assignment procedure; set(val,var)?
Post by: mjrice on November 11, 2018, 05:23:41 AM
I'm back, and back in 32-bit land after downloading and installing the latest 32-bit versions of Fedora Linux (29), Gfortran, and Nasm.

The test function executed fine after doing a new make on my archived directory.

I then ran the same code in my last two posts in 32-bit to produce the following:

          -1081206440           3213760856

The two values are the output the Gfortran "loc" function and the nasm "madov" function respectively. They're just the signed and unsigned decimal values of the same binary integer address.