Re: Question about AMD64 ABI

From: Paul Procacci <pprocacci_at_gmail.com>
Date: Sun, 13 Nov 2022 09:50:10 UTC
On Sun, Nov 13, 2022 at 4:07 AM Daniel Cervus <DanieltheDeer@outlook.com>
wrote:

> Hi Paul,
>
>   You mean I need to ensure higher bits are zeros (including signed int,
> short, char...) when passing the parameters? Okay, I get it.
>


That's not what I said.
I said:  "32-bit operands generate a 32-bit result, zero-extended to a
64-bit result in the destination general-purpose register."
I also said: "16 and 8 bit operands don't have this "built-in" so you would
indeed need to ensure the higher bits are cleared."

So what does this mean?  Let's take the following C function prototype as
well as its implementation .. something I just pulled out of thin air and
is useless, but is only for demonstration purposes:

        ;                                                         rdi
        ; long long iAmGoingToDoStuff(long long value)
        iAmGoingToDoStuff:
                inc     rdi
                mov     rax, rdi
                ret

Now let's take two separate asm snippets...one that WILL work as expected
and another that WON'T work as expected:
The working example is the easiest.  Even though the prototype declares it
accepts a long long, we only have to
explicitly set edi as it's a 32 bit operand that automatically gets zero
extended to 64 bits.

        workingExample:
                xor     edi, edi                ; Pass 0, all bits in rdi
are 0 due to abi rules
                call    iAmGoingToDoStuff
                ret

With the nonworking example I've tried to provide a scenario with the value
stored in edi was previously set by an outside force.  That's the first
`mov'.
The second `mov' in this example only sets the lower 4 bits of edi, leaving
the rest of the register alone.  If the intent was to set the register to
0, then
this is obviously a problem and you would have to ensure to clear the
proper bits.

        nonWorkingExample:
                mov     edi, 0xCF0A             ; Set edi to a random value
                mov     dl, 0
                call    iAmGoingToDoStuff
                ret

My advice regarding manipulating 16 bits or smaller registers is ... don't
unless you absolutely have to.  If you know what EFLAGS stalls are and know
some 16 computations you're doing won't stall on it, then it's not a
problem.
It's best however to just avoid them altogether.  (Exceptions apply but are
far as few between).


  By the way, is there any good resources that I can learn 64-bit FB (or
> *nix) assembly programming from?
>
>
This answer might seem clique but I swear it's not the intention.  The best
reference for instructions, associated operands, rules regarding zero
extending of 32-bit registers, etc., is right in the manual:

https://cdrdv2.intel.com/v1/dl/getContent/671110

The above naturally doesn't "teach" assembly, but it will be your "goto"
for just about everything.  I've been programming in assembly for well over
2 decades, damn nearing 3 decades now, and it's STILL the Holy Bible of how
a CPU ticks ( no pun intended).

With that said, each assembler has their own syntax when it comes down to
translating its version of assembly into machine code.
I certainly advocate for nasm as it's my goto but others might find the
syntax of fasm, for example, more to their liking.
It's up to you to find an assembler that's to your liking and read it's
manual through and through.


> Regards,
> Daniel
>
>
>
~Paul




> 在 2022年11月13日,下午4:52,Paul Procacci <pprocacci@gmail.com> 写道:
>
> 
>
>
> On Sat, Nov 12, 2022 at 10:31 PM Daniel Cervus <DanieltheDeer@outlook.com>
> wrote:
>
>> Hi everyone,
>>
>> I’m trying to do assembly programming on FB in 64-bit mode. I have a
>> question, 64-bit mode requires parameters to be passed on 64-bit registers.
>> But when a parameter is 32-bit or smaller, do I need to sign-extend (or
>> zero-extend) them to 64-bit? The System V ABI specifications only says "The
>> size of each argument gets rounded up to eightbytes." It’s somewhat
>> ambiguous. How to round up 'float', when they are passed on stack?
>>
>> Thanks,
>> Daniel
>
>
> (Didn't Reply all)
>
> Hi Daniel,
>
> There are a handful of operations that operate on 32bit registers that
> automatically clear the high bits for you.
>
> 32-bit operands generate a 32-bit result, zero-extended to a 64-bit result
> in the destination general-purpose register.
> 16 and 8 bit operands don't have this "built-in" so you would indeed need
> to ensure the higher bits are cleared.
>
> mov dword edi, 1  is effectively setting rdi to the value of
> 0x0000000000000001
>
> As for sign extending the values, the answer is `no'.....under most
> circumstances.  If you are sticking to widths of 32 and 64 bits then you
> are fine.
> The moment you mess with 16bits or smaller, then yes, you need to ensure
> no garbage lives in your higher bits because the cpu doesn't clear this for
> you.
>
> "The size of each argument gets rounded up to eight bytes."
>
> The size of ALL arguments passed to the callee via general purpose
> registers is 8 bytes "regardless of what a function def says".  It's HOW
> the callee operates upon the register arguments that matters.
>
> As for passing arguments on the stack ...  you shouldn't have to.  Not
> only are there the GPR's rdi, rsi, rdx, rcx, r8, r9, r10 at your disposal
> for int/scalar types there are also xmm0-xmm7 for your floats.
>
> Thanks,
> ~Paul
>
> --
> __________________
>
> :(){ :|:& };:
>
>

-- 
__________________

:(){ :|:& };: