Sign in Join
Wiki copytemporary

Win64/AMD64 API: what changed

Revision 1 to 2. Removed lines have a minus sign; added lines have a plus sign.

OldNewLine
11*(Windows only)*
22
33# Old Information
44
55As far as I know, there is no offical document yet decribing the win64/amd64 api so the follow information is collected from several sources.
66
77Begin in this link <http://msdn.microsoft.com/library/default.asp?url=/library/en-us/win64/win64/getting_ready_for_64_bit_windows.asp>
88
99## Data type sizes
1010
1111Basic information at <http://msdn.microsoft.com/library/default.asp?url=/library/en-us/win64/win64/the_new_data_types.asp>
1212
1313# Notes on Win64 for AMD calling conventions
1414
1515## About documentation
1616
1717Documentation about Windows x86-64 calling convention does exist. It can be found online on msdn: <http://msdn.microsoft.com/library/default.asp?url=/library/en-us/Kernel_d/hh/Kernel_d/64bitAMD_6ec00b51-bf75-41bf-8635-caa8653c8bd9.xml.asp>
1818
1919This documentation says that it's preliminary and subject to change, and is dated February 2005.
2020
2121However, there is a SWConventions.doc file in Microsoft Platform SDK, dated May 2005. It's a more recent version of what is published in URL above (in fact there are some important differences). In the file the only warning about "preliminary documentation" is found in "hot patchability" chapter. Since win64 has shipped in the USA I think that this documentation can be assumed as definitive. This file is located in the same directory of binaries for amd64 (cl, ml64, link and so on). I think that it cannot be redistributed (license says that one can copy it for its personal reference, but doesn't say anything about redistribution of the file). That file is the reference upon documentation in this wiki is based.
2222
2323## Preliminary notes:
2424
2525rvas are still 32 bit: maximum file size for an exe file is 2 gb, so there is no need to use larger address space.
2626
2727## Some definitions:
2828
2929leaf function: a function that doesn't call any function, neither allocates stack space by itself. This function doesn't have a frame pointer.
3030
3131frame function: a function that has a frame pointer.
3232
3333## Calling conventions in Win64 for AMD
3434
3535There is only one calling convention. Actually Microsoft include files are shared among Windows 32 bit and 64 bit. Functions modifiers like stdcall, cdecl and fastcall are ignored on 64bit windows since they use an unique calling convention.
3636
3737### Parameter passing
3838
3939The first 4 parameters are passed on registers. Integer parameters (from 1 to 8 bytes long) are passed in RCX, RDX, R8 and R9 registers, where the first parameter (the leftmost) is passed in RCX and the fourth in R9. Floating point parameters are passed in XMM0L, XMM1L, XMM2L and XMM3L. Parameters beyond the fourth are passed on stack. Note: If there are both integer and floating point parameters, use of n-th register is mutually exclusive; if the second parameter is a floating point number and the third is integer, the second one goes in XMM1L and the third in R8. Example:
4040
41-`function(int1,real1,int2,real2,int3,real3)`
41+``` pascal
42+function(int1,real1,int2,real2,int3,real3)
43+```
4244
4345means:
4446
45-`push real3  // passed on stack`\
46-`push int3   // passed on stack`\
47-`XMM3L=real2 `\
48-`R8=int2`\
49-`XMM1L=real1`\
50-`RCX=int1`
47+```text
48+push real3 // passed on stack
49+push int3 // passed on stack
50+XMM3L=real2
51+R8=int2
52+XMM1L=real1
53+RCX=int1
54+```
5155
5256Actually cl doesn't use real pushes to put arguments on stack, but we'll talk about it later.
5357
5458Parameters greater than 8 bytes are passed by reference (so it's caller duty to allocate space for a copy of the parameter and then pass a pointer to it).
5559
5660Some notes for vararg/unprototyped functions: if there are floating point arguments, they must be passed both in integer register and in floating point register, without conversion, so that the function can retrieve the parameter if it expected a integer parameter (in other words: if an unprototyped function expects an integer parameter as second parameter it would look in RDX. Since it's not prototyped we don't know it, and if we pass a floating point argument as second parameter we end up with the value in XMM1L and garbage in RDX: so we copy the value in both registers to avoid problems.) If the argument is beyond the fourth there is no special-handling, since callee will look at the same stack location if it expects an integer parameter or a floating point parameter.
5761
5862### Return values
5963
6064Integer values which have length between 1 and 8 bytes are returned in RAX. Floating point values, __m128, __m128i or __m128d values are returned in XMM0.
6165
6266If return value is too big to fit in one of these registers, it's caller duty to allocate stack space before calling the function, and to pass a pointer to this location as first parameter (so there are now only three registers to store other parameters).
6367
6468### Volatile and nonvolatile registers
6569
6670In addition to registers used to pass parameters, RAX, R10, R11, XMM4 and XMM5 aren't preserved by callee and should be saved by the caller if needed (R10 and R11 are used for syscall/sysret, don't know about XMM4 and XMM5). Other registers must be saved by callee if used, even if a special treatment is reserved to x87 and mmx registers.
6771
6872#### About x87 and mmx registers
6973
7074x87 and mmx registers aren't used: all floating points calculations are made in XMM* registers. Although x87 and mmx registers are guaranteed to be preserved across context switching, this is not true for functions calls. Microsoft says that if you use x87 registers you should consider them as volatile registers. There's nothing about mmx registers apart the context-switching thing, so I think that even mmx registers have to be considered volatile.
7175
7276#### x87 and MMX control registers
7377
7478x87 The x87 control word is considered nonvolatile and must be preserved by the callee. When program is started, FPCSR is set this way:
7579
76-`    FPCSR[0:6]         : Exception masks all 1's (all exceptions masked)`\
77-`    FPCSR[7]           : Reserved - 0`\
78-`    FPCSR[8:9]         : Precision Control - 10B (double precision)`\
79-`    FPCSR[10:11]       : Rounding  control - 0 (round to nearest)`\
80-`    FPCSR[12]          : Infinity control - 0 (not used)`
80+```text
81+ FPCSR[0:6] : Exception masks all 1's (all exceptions masked)
82+ FPCSR[7] : Reserved - 0
83+ FPCSR[8:9] : Precision Control - 10B (double precision)
84+ FPCSR[10:11] : Rounding control - 0 (round to nearest)
85+ FPCSR[12] : Infinity control - 0 (not used)
86+```
8187
8288If a function modifies this register it should then reset the register to the original value before returning. It should do the same thing when calling another function (however, if there is a function whose purpose is to modify this register this pass can be skipped. It can be skipped even if it's known that called function doesn't rely on that register). MMX MMX control register bits are considered volatile from 0 through 5, and nonvolatile from 6 and beyond. Same considerations made for FPCSR are valid for nonvolatile part of MXCSR. This is MCXSR setting when program starts:
8389
84-`    MXCSR[6]        : Denormals are zeros - 0`\
85-`    MXCSR[7:12]     : Exception masks all 1's (all exceptions masked)`\
86-`    MXCSR[13:14]    : Rounding  control - 0 (round to nearest)`\
87-`    MXCSR[15]       : Flush to zero for masked underflow - 0 (off)`
90+```text
91+ MXCSR[6] : Denormals are zeros - 0
92+ MXCSR[7:12] : Exception masks all 1's (all exceptions masked)
93+ MXCSR[13:14] : Rounding control - 0 (round to nearest)
94+ MXCSR[15] : Flush to zero for masked underflow - 0 (off)
95+```
8896
8997### Stack configuration
9098
9199#### Shadow space
92100
93101There is the concept of "shadow space" when calling a function. The caller must always allocate a fixed space of 32 bytes (4*8 bytes) on stack before calling a function: this is called shadow space, and it's used by callee to store arguments that are passed on registers if the callee needs these arguments to be in memory (after storing four register arguments to shadow area, all arguments appear as contiguous array on stack, making it easy to implement functions like printf()). The register parameters are also typically stored into shadow space when generating debugging code, providing the debugger a consistent location where to look for parameter values. Microsoft cl has an option to force storing register parameters to shadow space, which is enabled for debug builds. This stack space must always be allocated, even if function has less than 4 parameters. This space must be adjacent to callee return address: stack pointer must point to the end of this space before calling the function. So if more than 4 parameters are passed, parameters beyond 4 are at higher addresses, then there is shadow space on top of the stack.
94102
95103#### Stack alignment
96104
97105Stack pointer (RSP) must be aligned on multiples of 16 bytes when calling another function. Note: when called functions begins execution, stack isn't aligned in 16-bytes boundary: in fact it is aligned before call instruction, then call instruction places return address on stack (which is 8 bytes long) making stack unaligned. Therefore, in leaf functions the stack stays unaligned (according to the definition of leaf functions, any change to the stack pointer makes the function non-leaf).
98106
99107#### Frame pointer
100108
101109In most cases, Win64 API uses the fixed stack, i.e. value of the stack pointer is changed only in prolog and epilog, not in the function body. This removes the need of having a separate frame pointer. An exclusion to this rule is dynamic stack allocation using alloca() function. Also, a frame pointer can be set pointing into the middle of local variables area, so local variables are addressed at both positive and negative offsets from the FP. Since offsets values below 128 are encoded using shorter instructions, this allows generating smaller code. The frame pointer is not restricted to be RBP, it can be any non-volatile register.
102110
103111#### Parts of function: prolog, body and epilog
104112
105113To make work of exception handler easy, functions are considered "splitted" in three parts: prolog, body of function and epilog. Prolog is described using unwind data, and the epilog has a standard configuration so that exception handler can recognize them properly.
106114
107115##### Function prolog
108116
109117Prolog is the very first part of function. Generally, function prolog does this work:
110118
111119- Copies parameters from registers to shadow space
112120- Pushes registers to be preserved on stack
113121- Allocates room on stack for local variables
114122- Sets a frame pointer (so frame pointer is set AFTER local variables!) if needed
115123- Allocates space needed to store volatile registers that must be preserved in function calls
116124- Allocates shadow space for called functions.
117125
118126This is what microsoft cl does, although this should not be seen as a restriction, since you can chose other methods. If in the body of function dynamic allocation of memory on stack is needed, no problem: memory is allocated but block stays between frame pointer and rsp-(size of space needed for volatile registers+size of shadow space for called functions), so that shadow space for called functions it's always on top of the stack. This layout might help:
119127
120-` ---------------     --`\
121-`|      R9       |      |`\
122-` ---------------       |`\
123-`|      R8       |      |`\
124-` ---------------       |Shadow space for this function, allocated by caller`\
125-`|      RDX      |      |`\
126-` ---------------       |`\
127-`|      RCX      |      |`\
128-` ---------------     --`\
129-`| Return address|`\
130-` ---------------`\
131-`|               |`\
132-`|               |        Local variables of this function`\
133-`|               |`\
134-`|               |`\
135-` ---------------`\
136-`| Frame Pointer |        Optional`\
137-` ---------------`\
138-`|               |`\
139-`|               |        Memory used for dynamic allocation`\
140-`|               |`\
141-`|               |`\
142-` ---------------`\
143-`|               |`\
144-`|               |        Memory used to save registers that may be modified by callee`\
145-`|               |`\
146-`|               |`\
147-` ---------------     --`\
148-`|      R9       |      |`\
149-` ---------------       |`\
150-`|      R8       |      |`\
151-` ---------------       |Shadow space for callee, allocated by this function`\
152-`|      RDX      |      |`\
153-` ---------------       |`\
154-`|      RCX      |      |`\
155-` ---------------     --`
128+``` pascal
129+ --------------- --
130+| R9 | |
131+ --------------- |
132+| R8 | |
133+ --------------- |Shadow space for this function, allocated by caller
134+| RDX | |
135+ --------------- |
136+| RCX | |
137+ --------------- --
138+| Return address|
139+ ---------------
140+| |
141+| | Local variables of this function
142+| |
143+| |
144+ ---------------
145+| Frame Pointer | Optional
146+ ---------------
147+| |
148+| | Memory used for dynamic allocation
149+| |
150+| |
151+ ---------------
152+| |
153+| | Memory used to save registers that may be modified by callee
154+| |
155+| |
156+ --------------- --
157+| R9 | |
158+ --------------- |
159+| R8 | |
160+ --------------- |Shadow space for callee, allocated by this function
161+| RDX | |
162+ --------------- |
163+| RCX | |
164+ --------------- --
165+```
156166
157167Code example:
158168
159-`int func2(int i1, int i2, int i3)`\
160-`{`\
161-`  int lets_waste_stack_space = 53;`\
162-`  return (i1 + i2 + i3);`\
163-`}`\
169+```text
170+int func2(int i1, int i2, int i3)
171+{
172+ int lets_waste_stack_space = 53;
173+ return (i1 + i2 + i3);
174+}
164175
165-`int func1(int i1, int i2)`\
166-`{`\
167-`  int another_local_variable = 51;`\
168-`  void * buf = alloca(100);`\
169-`  return (func2(i1, i2, 3));`\
170-`}`\
176+int func1(int i1, int i2)
177+{
178+ int another_local_variable = 51;
179+ void * buf = alloca(100);
180+ return (func2(i1, i2, 3));
181+}
171182
172-`int main()`\
173-`{`\
174-`  int a_local_variable = 50;`\
175-`  func1(1,2);`\
176-`  return(0);`\
177-`}`
183+int main()
184+{
185+ int a_local_variable = 50;
186+ func1(1,2);
187+ return(0);
188+}
189+```
178190
179191This is memory layout in func1:
180192
181-`xxxxxxxx`\
182-`xxxxxxxx`\
183-`i2 = rdx`\
184-`i1 = rcx`\
185-`retaddr`\
186-`old rbp                       | local variables`\
187-`buf                           |`\
188-`another_local_variable <= rbp |`\
189-`xxxxxxxx         | shadow for    | room for buf`\
190-`xxxxxxxx         | func2         | dinamically`\
191-`xxxxxxxx         | before        | allocated`\
192-`xxxxxxxx <= rsp1 | alloca        |`\
193-`xxxxxxxx                         |`\
194-`xxxxxxxx                         |`\
195-`xxxxxxxx                         |`\
196-`xxxxxxxx                         |`\
197-`xxxxxxxx                         |`\
198-`xxxxxxxx                         |`\
199-`xxxxxxxx                         |`\
200-`xxxxxxxx                         |`\
201-`xxxxxxxx                         |`\
202-`xxxxxxxx <= buf__________________|`\
203-`xxxxxxxx                                       | shadow for`\
204-`xxxxxxxx                                       | func2`\
205-`xxxxxxxx                                       |`\
206-`xxxxxxxx <= rsp2______________________________ |`
193+```text
194+xxxxxxxx
195+xxxxxxxx
196+i2 = rdx
197+i1 = rcx
198+retaddr
199+old rbp | local variables
200+buf |
201+another_local_variable <= rbp |
202+xxxxxxxx | shadow for | room for buf
203+xxxxxxxx | func2 | dinamically
204+xxxxxxxx | before | allocated
205+xxxxxxxx <= rsp1 | alloca |
206+xxxxxxxx |
207+xxxxxxxx |
208+xxxxxxxx |
209+xxxxxxxx |
210+xxxxxxxx |
211+xxxxxxxx |
212+xxxxxxxx |
213+xxxxxxxx |
214+xxxxxxxx |
215+xxxxxxxx <= buf__________________|
216+xxxxxxxx | shadow for
217+xxxxxxxx | func2
218+xxxxxxxx |
219+xxxxxxxx <= rsp2______________________________ |
220+```
207221
208222Main allocates shadow space of func1: 32 bytes even if only 2 parameters are used. It puts 1 in rcx and 2 in rdx, then call instruction places return address on stack. This is what func1 does in its prolog:
209223
210224- Copies parameters from registers to shadow space:
211225 - Copies rdx in rsp+16 and rcx in rsp+8
212226- Pushes registers to be preserved on stack:
213227 - Pushes ebp.
214228- Allocates room on stack for local variables:
215229 - Decrements rsp by 16 to reserve room for buf and another_local_variable
216230- Sets a frame pointer:
217231 - rbp now points to rsp (that is, to another_local_variable address)
218232- Allocates space needed to store volatile registers that must be preserved in function calls:
219233 - no space needed
220234- Allocates shadow space for called functions:
221235 - Decrements rsp by 32. rsp position is rsp1 in previous scheme.
222236
223237End of prolog.
224238
225239In function body, alloca is called: rsp is decremented by 112 (alloca allocates memory aligned on 16-bytes boundary). rsp position is rsp2 in schema now. Buf doesn't point to same location of rsp since shadow space for callee must stay on top of the stack: instead it points 32 bytes before rsp, so that buf stays between rbp+8 and rsp-32. When func2 is called, it finds its shadow space where it should be (adjacent to return address).
226240
227241Note that this is modus operandi of microsoft cl, but it's perfectly legal to use other strategies (this is even remarked by microsoft). It is perfectly legal to push registers to be preserved and to allocate shadow space before a function call, and then release memory after the call.
228242
229243##### Function epilog
230244
231245Epilog is the last part of function. Function epilog does this work:
232246
233247- Release stack space
234248- Pops registers that were saved
235249- returns
236250
237251It should not do other things: function result should be set before epilog. Microsoft suggests that for functions without frame pointer stack is released adding a constant value to rsp. For function with frame pointer this can be accomplished in two ways: adding a costant to rsp or loading rsp with rbp address. This restrictions are needed so that exception handler can easily detect function epilog.
238252
239253This is ML64 assembly listing for func1 in previous example; Comments and exception-related stuff has been removed. You can see that cl uses a more optimized approach: instead of reserve space for local variables, set frame pointer and reserve space for shadow it reserves space in an unique operation and then sets frame pointer to the right value.
240254
241-`PUBLIC func1`\
242-`_TEXT  SEGMENT`\
243-`func1  PROC NEAR`\
244-`       mov DWORD PTR [rsp+16], edx           ; save second parameter`\
245-`       mov DWORD PTR [rsp+8], ecx            ; save first parameter`\
246-`       push    rbp                               ; save old rbp value`\
247-`       sub rsp, 48                           ; room for local vars + shadow space for called function`\
248-`       lea rbp, QWORD PTR [rsp+32]           ; sets frame pointer to the end of local variables block.`\
249-`                                                 ; end of prolog`\
250-`       `\
251-`       mov DWORD PTR [rbp], 51               ; another_local_variable = 51;`\
252-`       `\
253-`       mov eax, 112                          ; make room for buf`\
254-`       sub rsp, rax`\
255-`       lea rax, QWORD PTR [rsp+32]           ;`\
256-`       mov ecx, DWORD PTR [rax]              ; buf now points to rsp+32. Shadow space lies between`\
257-`       mov QWORD PTR 8[rbp], rax             ; rsp+32 and rsp.`\
258-`       `\
259-`       mov r8d, 3                            ; third parameter of func2: 3`\
260-`       mov edx, DWORD PTR 40[rbp]            ; second parameter: i2`\
261-`       mov ecx, DWORD PTR 32[rbp]            ; first parameter: i1`\
262-`       call    func2                             ; call to func2`\
263-`       `\
264-`                                                 ; here starts epilog`\
265-`       lea rsp, QWORD PTR [rbp+16]           ; frame function, use lea to release stack`\
266-`       pop rbp                               ; pop ebp register that was saved`\
267-`       ret 0                                 ; return`\
268-`func1  ENDP`\
269-`_TEXT  ENDS`
255+``` pascal
256+PUBLIC func1
257+_TEXT SEGMENT
258+func1 PROC NEAR
259+ mov DWORD PTR [rsp+16], edx ; save second parameter
260+ mov DWORD PTR [rsp+8], ecx ; save first parameter
261+ push rbp ; save old rbp value
262+ sub rsp, 48 ; room for local vars + shadow space for called function
263+ lea rbp, QWORD PTR [rsp+32] ; sets frame pointer to the end of local variables block.
264+ ; end of prolog
265+
266+ mov DWORD PTR [rbp], 51 ; another_local_variable = 51;
267+
268+ mov eax, 112 ; make room for buf
269+ sub rsp, rax
270+ lea rax, QWORD PTR [rsp+32] ;
271+ mov ecx, DWORD PTR [rax] ; buf now points to rsp+32. Shadow space lies between
272+ mov QWORD PTR 8[rbp], rax ; rsp+32 and rsp.
273+
274+ mov r8d, 3 ; third parameter of func2: 3
275+ mov edx, DWORD PTR 40[rbp] ; second parameter: i2
276+ mov ecx, DWORD PTR 32[rbp] ; first parameter: i1
277+ call func2 ; call to func2
278+
279+ ; here starts epilog
280+ lea rsp, QWORD PTR [rbp+16] ; frame function, use lea to release stack
281+ pop rbp ; pop ebp register that was saved
282+ ret 0 ; return
283+func1 ENDP
284+_TEXT ENDS
285+```
270286
271287## Data alignment
272288
273289For performance reasons, data should be aligned to its natural alignment. Arrays should be aligned on their elements' natural alignment. Fields inside structures should be aligned on their natural alignment, and structure itself should be aligned according to alignment of its wider field (so if a record is made of a byte and a qword, record is aligned on 8-byte boundary, byte starts at record+0 and qword at record+8).
274290
275291Functions must be aligned on 4 byte multiples, but 16-byte alignment is encouraged for performance reasons.
276292
277293## Constants
278294
279295Enums and integer constants are treated as 32-bit integers. Bitfields can't be larger than 64 bits.
280296
281297## Exception
282298
283299Win64 doesn't support the old i386 way of doing exception handling using fs:(0) as a linked list of exception handlers. Instead, it uses table based exception handling. However, FPC uses it's own code to unwind exceptions so it needs a way to intercept exceptions before the table based exception handling jumps in. This can be done using vectored exception handling which is new in Windows XP: <http://windowssdk.msdn.microsoft.com/library/default.asp?url=/library/en-us/Debug/base/using_a_vectored_exception_handler.asp>
284300
285301Don't get fooled by the comment at the top of the article, it only means that the code below uses fields in the context record which aren't available in win64 (eip vs. rip) so adapting this, the code works.
286302
287303## Debugger support
288304
289305## Differences between PE and PE+ exe format
290306
291307PE32+ Magic number (in optional header) is 0x20b while PE32 is 0x10b
292308
293309There is no BaseOfData field in Standard Fields of Optional Header
294310
295311Windows Nt Specific Fields (Optional Header): ImageBase, SizeOfStackReserve, SizeOfStackCommit, SizeOfHeapReserve, SizeOfHeapCommit are 8 bytes long instead of 4
296312
297313Import Lookup Table entries are 64 bits long instead of 32. Bit 63 is Ordinal/Name flag, bits 62 through 0 are Ordinal Number or Hint/Name Table RVA when Ordinal/Name flag is 1 or 0 respectively
298314
299315Import Address Table has same structure as Import Lookup Table
300316
301317TLS Directory Raw Data Start VA, Raw Data End VA, Address of Index, Address of Callbacks are 8 bytes long instead of 4.
302318
303319## Things that must be done on FreePascal
304320
305321First of all: I'm not a fpc developer nor I know fpc internals (I only have some ideas of what files do what thing) so I might be wrong :P It has been said that it's useless to make a win64 port before gcc has been ported, since fpc relies on gas, ld and gdb. But maybe something can start already, so that fpc is already "one step forward" when gcc arrives on win64.
306322
307323### Calling convention
308324
309325First of all, a new calling convention is required. Here there are two choices:
310326
311327- Make a brand new calling convention (win64amd?)
312328- Make cdecl, stdcall and safecall as "aliases" to this new calling convention on x86_64-win64
313329
314330Second choice is microsoft choice: since microsoft c compiler considers these conventions all the same thing, logical consequence is that every other c compiler will use this convention in the future. Old calling conventions have no meaning in win64.
315331
316332First choice can be used if there is the possibility that some c compiler uses these conventions even on win64. This means that freepascal headers for windows apis should be rearranged: on i386 there should be a
317333
318-`{$calling stdcall}`
334+```text
335+{$calling stdcall}
336+```
319337
320338while on win64 for amd there should be a
321339
322-`{$calling win64amd}`
340+```text
341+{$calling win64amd}
342+```
323343
324344and parts where calling convention is directly specified as function modifiers should be carefully modified/moved to inc files.
325345
326346If second option is chosed it could be useful to temporary set this name to "win64amd" so that it can be tested and debugged on linux-x86_64, where we have gdb (making functions with win64amd modifier and calling them in pascal source files, and test and debug the calling convention). When everything is ok it will be an alias to cdecl, stdcall and safecall on win64 for amd and name will be removed.
327347
328348A function that is declared to use this convention should do these things:
329349
330350- if RCX, RDX, R8, R9, XMM0, XMM1, XMM2, XMM3 are used, their value should be saved in shadow space
331351- other nonvolatile registers, if used, should be pushed on stack. These registers are R12 through R15, RDI, RSI, RBX, XMM6 through XMM15
332352- MXCSR and FPCSR, if used, should be pushed on stack.
333353
334354A function that call a function that uses this convention should do these things:
335355
336356- Save volatile registers, if used. These are RCX, RDX, R8, R9, XMM0, XMM1, XMM2, XMM3, RAX, R10, R11, XMM4, XMM5.
337357- Save x87 and MMX registers, if used.
338358- If MXCSR and FPCSR have been modified, their original value should be restored.
339359- Push parameters beyond the fourth on stack and align stack on 16-byte boundary if needed. Stack must be padded before pushing parameters according to current stack alignment and number of parameter beyond fourth, so that when last parameter that must be on stack is pushed, stack is 16 byte aligned.
340360- Put parameters 1..4 on registers.
341361- Allocate 32 bytes on stack for shadow space of callee.
342362
343363### Output format
344364
345365Okay, there is no gas for win64. Since rtl and windows api should be adjusted, a temporary way to start working on it could be to use masm output and compile with ml64. There is a masm writer, and it looks like it has been upgraded (if not upgraded, something has started to move) to handle ml64 syntax too. While this is a good thing, since it's good to have masm output option even if gas would be available, it can be seen as a useless effort. So, binary writer could be improved to handle x86_64 as well. x86_64 is i386 with larger pointers, a couple of instruction were added and some removed: it should be easy to adapt i386 binary writer so that it can write x86_64 code too. Another good thing is that this can be tested and debugged under linux for x86_64, and this platform would benefit from binary writer too. There is the PE+ coff format to implement, but it's not very different to PE coff: maybe differences will be written in this wiki too.
346366
347367### Adapt rtl
348368
349369Having binary writer and calling convention, link can be performed with microsoft link until binutils are ported to win64: we could start adapting windows rtl.
350370
351371Of course, debugging support will still be missed, and I think that adding support for another debugger like windbg should be a big and useless effort, but while gcc isn't available for win64 freepascal could be almost-ready and win64 could be considered an experimental platform, not ready yet to be included to the official ones.
352372
353373---
354374*From the Free Pascal wiki, page "Win64/AMD64 API", as it stood on 2015-01-06 after a change by Jwdietrich, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).*
355375Category: Assembler
356376Category: Windows

Back to history