4

I'm trying to port DDetours lib to Delphi 5/7. The lines that wont compile have this pattern:

procedure Decode_J(PInst: PInstruction; Size: Byte);
var
  Value: Int64;
  VA: PByte;
begin
  ...
  VA := PInst^.VirtualAddr + (PInst^.NextInst - PInst^.Addr) // <- compiler error

with compiler error:

[Error] InstDecode.pas(882): Operator not applicable to this operand type

PInst^.VirtualAddr, PInst^.NextInst, PInst^.Addr are all declared as PByte (PByte = ^Byte)

What can I do to resolve this?


EDIT:

PInstruction is defined as:

  TInstruction = record
    Archi: Byte; { CPUX32 or CPUX64 ! }
    AddrMode: Byte; { Address Mode }
    Addr: PByte;
    VirtualAddr: PByte;
    NextInst: PByte; { Pointer to the Next Instruction }
    OpCode: Byte; { OpCode Value }
    OpType: Byte;
    OpKind: Byte;
    OpTable: Byte; { tbOneByte,tbTwoByte,... }
    OperandFlags: Byte;
    Prefixes: Word; { Sets of Prf_xxx }
    ModRm: TModRM;
    Sib: TSib;
    Disp: TDisplacement;
    Imm: TImmediat; { Primary Immediat }
    ImmEx: TImmediat; { Secondary Immediat if used ! }
    Branch: TBranch; { JMP & CALL }
    SegReg: Byte; { Segment Register }
    Rex: TRex;
    Vex: TVex;
    LID: TInternalData; { Internal Data }
    Errors: Byte;
    InstSize: Byte;
    Options: Byte;
    UserTag: UInt64;
  end;

  PInstruction = ^TInstruction;
zig
  • 4,524
  • 1
  • 24
  • 68

2 Answers2

6

In newer Delphi versions, PByte can be used in pointer arithmetic. Before that, the only type that could do that was PChar (PAnsiChar or PWideChar). In Delphi 5, PChar is a PAnsiChar.

Update

Now I know the structure of the record and know what you want to achieve, I think you should probably do the following.

You could change all PBytes in TInstruction to PAnsiChar (or PChar, if you only care about D5), and also all PBytes in every routine. Then you get:

PInstruction = ^TInstruction;
TInstruction = record
  Archi: Byte; { CPUX32 or CPUX64 ! }
  AddrMode: Byte; { Address Mode }
  Addr: PAnsiChar;
  VirtualAddr: PAnsiChar;
  NextInst: PAnsiChar; { Pointer to the Next Instruction }
  ...
end;

...

procedure Decode_J(PInst: PInstruction; Size: Byte);
var
  Value: Int64;
  VA: PAnsiChar;
begin
  ...
  VA := PInst^.VirtualAddr + (PInst^.NextInst - PInst^.Addr);
  ...

But note that if you want to read or write a byte using these PAnsiChars, you will have to cast them to PByte.

Community
  • 1
  • 1
Rudy Velthuis
  • 28,387
  • 5
  • 46
  • 94
  • Better to be explicit and use `PAnsiChar` surely, then at least the code will mean the same thing on all Delphi versions – David Heffernan Jul 14 '16 at 11:03
  • @David: I agree, but I was not sure if `PAnsiChar` is actually defined in Delphi 5, and if it is, if it can do the same as `PChar`. I haven't used Delphi 5 since, IIRC, 2001. – Rudy Velthuis Jul 14 '16 at 11:07
  • It has to be defined there because they have `PWideChar`, `WideString` etc. COM was supported from very early. – David Heffernan Jul 14 '16 at 11:09
  • @David: OK, I will edit my answer. Thanks for the info. – Rudy Velthuis Jul 14 '16 at 11:10
  • I get an error: `[Error] InstDecode.pas(884): Incompatible types: 'Char' and 'Byte'` changing the line to `PChar(VA) := PChar(PInst^.VirtualAddr) + (PChar(PInst^.NextInst) - PChar(PInst^.Addr))` works. note the `PChar(VA) :=...` – zig Jul 14 '16 at 11:12
  • as I mentioned in my question, all parameters in the original code are `PByte`. I also added the declaration of `PInstruction` in the EDIT. – zig Jul 14 '16 at 11:17
  • Note that in my answer, I had already changed the type of VA to P(Ansi)Char. So there is no need to cast it to PChar anymore. As I said: replace ***all*** PBytes to P(Ansi)Chars and cast to PByte when you must read or write a byte. – Rudy Velthuis Jul 14 '16 at 11:23
  • @zig: See my update. Now I know the record and what you want to do with it. Simply use PAnsiChar instead of PByte, and only cast to PByte is you must access a byte. – Rudy Velthuis Jul 14 '16 at 11:40
  • actually in D5 pointer maths was mandatory for every typed pointer, in later Delphi it became optional, but in D5 Pointer math is always on. `var i: integer; p: PInteger absolute I; begin i := 0; Inc(p, 10); writeln (i); end.` – Arioch 'The Jul 14 '16 at 13:05
  • @Arioch'The: Huh? With "pointer math" I mean adding an offset to a pointer, or subtracting two pointers to get a distance, and all of that scaled by the size of the type to which they point. `Absolute` is not pointer math, and it is, IMO, an abomination that ought to be removed from the language and the face of this earth. – Rudy Velthuis Jul 14 '16 at 13:14
  • "adding an offset to a pointer" is exactly what `Inc` procedure does. Granted, in newer Delphi there might be added more Pointer Math features, but some of them already were there in D5 – Arioch 'The Jul 14 '16 at 15:41
  • in soviet pascal for DEC PDP-11 the `absolute` feature was made into a variable name declaration: `var xxx: pointer; yyy origin xxx: integer;` - and it was surely more reasonable than making it part of variable type as in Turbo Pascal. However `absolute` still has one advantage: it is (while inherently unsafe) a DRY-compliant typecast, almost the same as declaring (and assigning) an extra local variable - but without wasting memory and CPU. Comparing to copy-pasting dozens of `type(var)` typecasts all through the function body, the "do it only once" nature of `absolute` looks better – Arioch 'The Jul 14 '16 at 15:45
  • 1
    @Arioch'The: `Inc(pointer, count)` has always worked in all Delphi versions. What does not work in D5 (except for `PAnsiChar`) is code that uses `pointer + count` directly instead. That was added in D2009 with the introduction of the `{$POINTERMATH}` directive. – Remy Lebeau Jul 14 '16 at 17:12
  • @RemyLebeau: not always in all Delphi versions. But I think it worked in D5 and later versions. – Rudy Velthuis Jul 14 '16 at 17:27
  • FWIW: I defined a type especially for pointer arithmetic: `{$IFDEF UNICODE} PArithByte = PByte; {$ELSE} PArithByte = PChar; {$ENDIF}`. One could improve the `IFDEF` condition, I guess. And maybe explicitly use `PAnsiChar`. It only has to work with D2007 and XE6 for me, so I didn't bother. – Uli Gerhardt Jul 15 '16 at 07:36
0

Under Delphi 5, try:

procedure Decode_J(PInst: PInstruction; Size: Byte);
var
  Value: Int64;
  VA: PByte;
begin
  ...
  VA := PByte(integer(PInst^.VirtualAddr) + (integer(PInst^.NextInst) - integer(PInst^.Addr))); 

Using integer() typecasting may fix the pointer arithmetic issue. But it will work under 32-bit executables only.

Arnaud Bouchez
  • 42,305
  • 3
  • 71
  • 159
  • Thanks, but then I have the same error on `PInst^.Branch.Target := VA + Value` where `Value` is `Int64`. should I change it to `PInst^.Branch.Target := PByte(Integer(VA) + Value);`? – zig Jul 14 '16 at 10:35
  • 1
    AFAIR another trick is `integer(VA) := integer(PInst^.VirtualAddr) + .....`. But take care about "Pointer Math" - it does not matter for PByte/Pointer but it will have effect for PWord, PInteger, etc. – Arioch 'The Jul 14 '16 at 10:54