I have this function (RDRand - written by David Heffernan) that seam to work ok in 32 bit, but failed in 64 bit :
function TryRdRand(out Value: Cardinal): Boolean;
{$IF defined(CPU64BITS)}
asm .noframe
{$else}
asm
{$ifend}
db $0f
db $c7
db $f1
jc #success
xor eax,eax
ret
#success:
mov [eax],ecx
mov eax,1
end;
doc of the function is here: https://software.intel.com/en-us/articles/intel-digital-random-number-generator-drng-software-implementation-guide
Especially it's written :
Essentially, developers invoke this instruction with a single operand:
the destination register where the random value will be stored. Note
that this register must be a general purpose register, and the size of
the register (16, 32, or 64 bits) will determine the size of the
random value returned.
After invoking the RDRAND instruction, the caller must examine the
carry flag (CF) to determine whether a random value was available at
the time the RDRAND instruction was executed. As Table 3 shows, a
value of 1 indicates that a random value was available and placed in
the destination register provided in the invocation. A value of 0
indicates that a random value was not available. In current
architectures the destination register will also be zeroed as a side
effect of this condition.
My knowledge of ASM is quite low, what did I miss ?
Also I do not quite understand this instruction :
...
xor eax,eax
ret
...
What it's does exactly ?
If you want a function that performs exactly the same then I think that looks like this:
function TryRdRand(out Value: Cardinal): Boolean;
asm
{$if defined(WIN64)}
.noframe
// rdrand eax
db $0f
db $c7
db $f0
jnc #fail
mov [rcx],eax
{$elseif defined(WIN32)}
// rdrand ecx
db $0f
db $c7
db $f1
jnc #fail
mov [eax],ecx
{$else}
{$Message Fatal 'TryRdRand not implemented for this platform'}
{$endif}
mov eax,1
ret
#fail:
xor eax,eax
end;
The suggestion made by Peter Cordes of implementing a retry loop in the asm looks sensible to me. I will not attempt to implement that here, since I think it is somewhat outside the scope of your question.
Also, Peter points out that in x64 you can read a 64 bit random value with the REX.W=1 prefix. That would look like this:
function TryRdRand(out Value: NativeUInt): Boolean;
asm
{$if defined(WIN64)}
.noframe
// rdrand rax
db $48 // REX.W = 1
db $0f
db $c7
db $f0
jnc #fail
mov [rcx],rax
{$elseif defined(WIN32)}
// rdrand ecx
db $0f
db $c7
db $f1
jnc #fail
mov [eax],ecx
{$else}
{$Message Fatal 'TryRdRand not implemented for this platform'}
{$endif}
mov eax,1
ret
#fail:
xor eax,eax
end;
Related
I want to access the local variables of a Delphi procedure from its nested assembly procedure. Although the compiler does allow the references of the local variables, it compiles wrong offsets which only work if the EBP/RBP values are hacked. In the x86 environment I found a fairly elegant hack, but in x64 I couldn't find yet any decent solution.
In the x86 environment the workaround below seems to work fine:
procedure Main;
var ABC: integer;
procedure Sub;
asm
mov ebp, [esp]
mov eax, ABC
end;
...
In the above code, the compiler treats the variable ABC as it would be in the body of Main, so hacking the value of EBP in the fist assembly line solves the problem. However, the same trick won't work in the x64 environment:
procedure Main;
var ABC: int64;
procedure Sub;
asm
mov rbp, [rsp]
mov rax, ABC
end;
...
In the above code, the compiler adds an offset when it references the variable ABC which isn't correct neither with the original (Main) value of the RBP, nor with its new (Sub) value. Moreover, changing the RBP in a 64-bit code isn't recommended, so I found the workaround below:
procedure Main;
var ABC: int64;
procedure Sub;
asm
add rcx, $30
mov rax, [rcx + OFFSET ABC]
end;
...
As the compiler passes the initial value of RBP in RCX, and the reference to the variable ABC can be hacked to be RCX rather than RBP based, the above code does work. However, the correction of $30 depends on the number of variables of Main, so this workaround is kind of a last resort stuff, and I'd very much like to find something more elegant.
Does anyone have a suggestion on how to do this in a more elegant way?
Note that:
Of course: in my real code there are a large number of local variables to be accessed from the ASM code, so solutions like passing the variables as parameters are ruled out.
I'm adding x64 compatibility to x86 code, and there are dozens of codes like this, so I'd need a workaround which transforms that code with small formal changes only (accessing the local variables in a fundamentally different way would become an inexhaustible source of bugs).
UPDATE:
Found a safe but relatively complicated solution: I added a local variable called Sync to find out the offset between the RBP values of Main and Sub, then I do the correction on the RBP:
procedure Main;
var Sync: int64; ABC: int64;
procedure Sub(var SubSync: int64);
asm
push rbp
lea rax, Sync
sub rdx, rax
add rbp, rdx
mov rax, ABC
pop rbp
end;
begin
ABC := 66;
Sub(Sync);
end;
So far nobody came with a solution, so I consider the code below to be the best known solution:
procedure Main;
var Sync: int64; ABC: int64;
procedure Sub(var SubSync: int64);
asm
push rbp
lea rax, Sync
sub rdx, rax
add rbp, rdx
mov rax, ABC
pop rbp
end;
begin
ABC := 66;
Sub(Sync);
end;
BTW, as this very much looks like a Delphi bug, I posted this to the Embarcadero as a bug report.
When I write a function in Delphi 10.2.3 Pro that has a record as return value and I just leave it empty I don't get a W1035 "W1035 Return value of function '%s' might be undefined" warning. Why don't I get a warning?
Thanks in advance!
Unfortunately the warning is also suppressed for unmanaged record. I guess the reason is that it is difficult for the compiler to keep track with the changes of all the fields. Also, if you modify one of the fields of a record, do you consider the whole record as defined or not?
Here is the code to test:
{$O-}
type
TRecordType = record
a, b: Word;
end;
function Test: TRecordType;
begin
end;
procedure TForm1.FormCreate(Sender: TObject);
var
v: TRecordType;
begin
v := Test;
ShowMessage(Format('%d, %d', [v.a, v.b]));
end;
It does give me random numbers (Not changing if run repeatedly, because the stack is almost the same).
The assembly code for calling:
0046CCA9 E8CAFFFFFF call Test
0046CCAE 8945F8 mov [ebp-$08],eax
The assembly code of Test:
0046CC78 55 push ebp
0046CC79 8BEC mov ebp,esp
0046CC7B 51 push ecx
0046CC7C 8B45FC mov eax,[ebp-$04] ; eax is the result, not initiated
0046CC7F 59 pop ecx
0046CC80 5D pop ebp
0046CC81 C3 ret
When the size of the record is not 1, 2, 4, the result is passed by reference as a hidden parameter. For example, if a and b are integer, the assembly code for calling become
0046CCAA 8D45F4 lea eax,[ebp-$0c] ; address of V, V is not initiated
0046CCAD E8C6FFFFFF call Test
The assembly code of Test:
0046CC78 55 push ebp
0046CC79 8BEC mov ebp,esp
0046CC7B 51 push ecx
0046CC7C 8945FC mov [ebp-$04],eax
0046CC7F 59 pop ecx
0046CC80 5D pop ebp
0046CC81 C3 ret
So in both case, the result is undefined without warning.
From my point of view this is clearly a bug in the warning generator of the compiler.
Keep in mention for records the compilers has to check much more than it does for a simple type like integer or real. For example what would you expect here:
function Test: TRecordType;
begin
result.a := 1;
end;
Because the record is partly (for b) undefined the compiler should also mention a W1035 here. On the other side what would you expect for the records with variant cases:
TRecordType = record
ID: string;
case boolean of
true:
(a: integer);
false:
(b: string);
end;
As you can see here it is not possible to determinate if a record is completely initialized or not. At least the Delphi compiler could warn if static fields of a record are unassigned as we see in case the TRecordType is a class.
I recommend to enter this bug in the embarcadero quality reporting side at
https://quality.embarcadero.com
and reference to this thread on stack overflow.
Quoted from https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html,
— Built-in Function: int __builtin_clz (unsigned int x)
Returns the number of leading 0-bits in x, starting at the most significant bit position. If x is 0, the result is undefined.
What is the Delphi equivalent to the C __builtin_clz() ? If there isn't, how to implement it efficiently in Delphi?
Actually, I want to use it to calculate the base-2 logarithm of an integer.
If you only care about 32 bit code then it goes like this:
function __builtin_clz(x: Cardinal): Cardinal;
asm
BSR EAX,EAX
NEG EAX
ADD EAX,32
end;
Or if you want to support 64 bit code as well then it would be:
function __builtin_clz(x: Cardinal): Cardinal;
{$IF Defined(CPUX64)}
asm
BSR ECX,ECX
NEG ECX
ADD ECX,31
MOV EAX,ECX
{$ENDIF}
{$IF Defined(CPUX86)}
asm
BSR EAX,EAX
NEG EAX
ADD EAX,31
{$ENDIF}
end;
It's likely that an asm guru could trim this down a little, but BSR (bit scan reverse) is the key instruction.
For the mobile compilers, I don't know how to do this efficiently.
According to the "Using Assembler in Delphi", eax will contain Self. However, the content of eax is 0 as shown. I wonder what is wrong ?
procedure TForm1.FormCreate(Sender: TObject);
var
X, Y: Pointer;
begin
asm
mov X, eax
mov Y, edx
end;
ShowMessage(IntToStr(NativeInt(X)) + ' ; ' + IntToStr(NativeInt(Y)));
end;
The code generated when I compile this, under debug settings, is like so:
begin
005A9414 55 push ebp
005A9415 8BEC mov ebp,esp
005A9417 83C4E4 add esp,-$1c
005A941A 33C9 xor ecx,ecx
005A941C 894DEC mov [ebp-$14],ecx
005A941F 894DE8 mov [ebp-$18],ecx
005A9422 894DE4 mov [ebp-$1c],ecx
005A9425 8955F0 mov [ebp-$10],edx
005A9428 8945F4 mov [ebp-$0c],eax
005A942B 33C0 xor eax,eax
005A942D 55 push ebp
005A942E 6890945A00 push $005a9490
005A9433 64FF30 push dword ptr fs:[eax]
005A9436 648920 mov fs:[eax],esp
mov X, eax
005A9439 8945FC mov [ebp-$04],eax
mov Y, edx
005A943C 8955F8 mov [ebp-$08],edx
When the code starts executing, eax is indeed the self pointer. But the compiler has chosen to save it away to ebp-$0c and then zeroise eax. That's really up to the compiler.
The code under release settings is quite similar. The compiler still chooses to zeroise eax. Of course, you cannot rely on the compiler doing that.
begin
005A82A4 55 push ebp
005A82A5 8BEC mov ebp,esp
005A82A7 33C9 xor ecx,ecx
005A82A9 51 push ecx
005A82AA 51 push ecx
005A82AB 51 push ecx
005A82AC 51 push ecx
005A82AD 51 push ecx
005A82AE 33C0 xor eax,eax
005A82B0 55 push ebp
005A82B1 6813835A00 push $005a8313
005A82B6 64FF30 push dword ptr fs:[eax]
005A82B9 648920 mov fs:[eax],esp
mov X, eax
005A82BC 8945FC mov [ebp-$04],eax
mov Y, edx
005A82BF 8955F8 mov [ebp-$08],edx
Remember that parameter passing defines the state of registers and stack when the function starts executing. What happens next, how the function decodes the parameters is down to the compiler. It is under no obligation to leave untouched the registers and stack that were used for parameter passing.
If you inject asm into the middle of a function, you cannot expect the volatile registers like eax to have particular values. They will hold whatever the compiler happened to put in them most recently.
If you want to examine the registers at the very beginning of the execution of the function, you need to use a pure asm function to be sure to avoid having the compiler modify the registers that were used for parameter passing:
var
X, Y: Pointer;
asm
mov X, eax
mov Y, edx
// .... do something with X and Y
end;
The compiler will make its choices very much dependent on the code in the rest of the function. For your code, the complexity of assembling the string to pass to ShowMessage causes quite a large preamble. Consider this code instead:
type
TForm1 = class(TForm)
procedure FormCreate(Sender: TObject);
private
i: Integer;
function Sum(j: Integer): Integer;
end;
....
procedure TForm1.FormCreate(Sender: TObject);
begin
i := 624;
Caption := IntToStr(Sum(42));
end;
function TForm1.Sum(j: Integer): Integer;
var
X: Pointer;
begin
asm
mov X, eax
end;
Result := TForm1(X).i + j;
end;
In this case the code is simple enough for the compiler to leave eax alone. The optimised release build code for Sum is:
begin
005A8298 55 push ebp
005A8299 8BEC mov ebp,esp
005A829B 51 push ecx
mov X, eax
005A829C 8945FC mov [ebp-$04],eax
Result := TForm4(X).i + j;
005A829F 8B45FC mov eax,[ebp-$04]
005A82A2 8B80A0030000 mov eax,[eax+$000003a0]
005A82A8 03C2 add eax,edx
end;
005A82AA 59 pop ecx
005A82AB 5D pop ebp
005A82AC C3 ret
And when you run the code, the form's caption is changed to the expected value.
To be perfectly honest, inline assembly, placed as an asm block inside a Pascal function, is not very useful. The thing about writing assembly is that you need to fully understand the state of the registers and the stack. that is well defined at the beginning and end of a function, defined by the ABI.
But in the middle of a function, that state depends entirely on the decisions made by the compiler. Injecting asm blocks in there requires you to know the decisions the compiler made. It also means that the compiler cannot understand the decisions that you made. This is usually impractical. Indeed for the x64 compiler Embarcadero banned such inline asm blocks. I personally have never used an inline asm block in my code. If ever I write asm I always write pure asm functions.
Just use the Push/Pop to get the pointer of SELF, and then use properties freely, like this:
asm
push Self
pop edx //Now, [edx] is the pointer to Self
mov ecx, [edx].FItems //ecx = FItems
mov eax, [edx].FCount //eax = FCount
dec eax //test zero count!
js #Exit //if count was 0 then exit as -1
#Loop: //and so on...
......
I am running into some weird behaviour with Delphi's inline assembly, as demonstrated in this very short and simple program:
program test;
{$APPTYPE CONSOLE}
uses
SysUtils;
type
TAsdf = class
public
int: Integer;
end;
TBlah = class
public
asdf: TAsdf;
constructor Create(a: TAsdf);
procedure Test;
end;
constructor TBlah.Create(a: TAsdf);
begin
asdf := a;
end;
procedure TBlah.Test;
begin
asm
mov eax, [asdf]
end;
end;
var
asdf: TAsdf;
blah: TBlah;
begin
asdf := TAsdf.Create;
blah := TBlah.Create(asdf);
blah.Test;
readln;
end.
It's just for the sake of example (moving [asdf] into eax doesn't do much, but it works for the example). If you look at the assembly for this program, you'll see that
mov eax, [asdf]
has been turned into
mov eax, ds:[4]
(as represented by OllyDbg) which obviously crashes. However, if you do this:
var
temp: TAsdf;
begin
temp := asdf;
asm
int 3;
mov eax, [temp];
end;
It changes to
mov eax, [ebp-4]
which works. Why is this? I'm usually working with C++ and I'm used to using instance vars like that, it may be that I'm using instance variables wrong.
EDIT: Yep, that was it. Changing mov eax, [asdf] to mov eax, [Self.asdf] fixes the problem. Sorry about that.
In the first case, mov eax,[asdf], the assembler will look up asdf and discover it is a field of offset 4 in the instance. Because you used an indirect addressing mode without a base address, it will only encode the offset (it looks like 0 + asdf to the assembler). Had you written it like this: mov eax, [eax].asdf, it would have been encoded as mov eax, [eax+4]. (here eax contains Self as passed in from the caller).
In the second case, the assembler will look up Temp and see that it is a local variable indexed by EBP. Because it knows the base address register to use, it can decide to encode it as [EBP-4].
A method receives the Self pointer in the EAX register. You have to use that value as the base value for accessing the object. So your code would be something like:
mov ebx, TBlah[eax].asdf
See http://www.delphi3000.com/articles/article_3770.asp for an example.